A few weeks ago, we shared our evolution from tightly coupled code to microservices through DDD. Following this, we received some questions from various fronts: From the comments on the video on YouTube, to the Slack of BcnEng (by the way, we take this opportunity to invite you to join it!), through several tweets. Thus, in this video we will attempt to answer those questions that, in many cases, are the same questions we had when starting with DDD and CQRS. If you're not very familiar with CQRS and DDD, we recommend starting with the introduction to Hexagonal Architecture that we prepared a while ago. This Software Architecture is the foundation upon which DDD is based, and it will help you begin to delve into these topics:
Thanks to everyone who has reached out with your questions. We hope to add value by sharing our perspective on:
Commands
Minute 0:55. It is important to first highlight that we are talking about commands understood within the CQRS world, and not the Command design pattern. Is it possible to have public attributes to avoid the need for getters? Ideally, they should ensure immutability. To do this, we should avoid exposing the writing of their attributes (make them private, and have only getters, not setters).
Synchronous command buses
Minute 3:08. We start from the premise that ideally our system should be as asynchronous as possible. We deduce this from initiatives like the Reactive Manifesto, and basically, it is the characteristic that makes our systems more scalable. However, there will be occasions when managing eventual consistency in the user interface is extremely complicated. Let's remember that from the moment we process our commands asynchronously, we are not returning a definitive response since we do not know the outcome that command will have (whether the user registration will go well or not, for example). Therefore, there will be times when it will not be beneficial for our commands to be processed asynchronously. Basically, because the difficulty of making our systems truly reactive and compatible with eventual consistency does not compensate for the benefits we gain. If our systems do not require that level of performance in terms of scalability, we understand that the cost of adopting asynchrony is not justifiable. Therefore, even though ideally everything should be asynchronous, it is very likely that we will have synchronous command buses in our system. These buses will block waiting for the response from the CommandHandler, for example, to return a HTTP 201 from the REST API confirming that the user has been registered successfully, or a 409 because the username already exists. Conclusion: We should promote that commands are asynchronous as much as we can. Otherwise, we have another synchronous implementation of the CommandBus to handle the situation 🙂
UUID Identifiers
Minute 5. Here we talk about UUID type identifiers. This type of identifier is useful to avoid delegating the responsibility of ID generation to our infrastructure. Since UUIDs are generated randomly, with an insignificant probability of collision, we can make our entities have the identifier as one of the required parameters for construction. This way, it is not necessary to go through the database repository to know what identifier the entity will have, so we can even expect it from outside. Here’s the example of implementing an entity, and how thanks to this:
- The example test is greatly simplified
- The client does not need to depend on the server's response to redirect to the edit page after creating an entity. Since the client is the one who generated the identifier, at the moment of receiving the HTTP 201 from the API, it can redirect (provided that the command is synchronous as we mentioned before 🙂)
Decoupling from the framework
Minute 7:30. At this point, we discuss the benefits of decoupling from the directory structure imposed by the frameworks, and how we can achieve this. You have the code example from the CQRS and DDD repository to take a look at 😬. It's important to detail the separation of applications from the code with business logic #fineDetail👌.
Where to publish domain events
Minute 11:55. First, we discuss why we prefer the semantics of "record" over what the "raise" method would offer for registering domain events. This detail is important despite how subtle it might seem. Here is exactly where the difference lies with the solution proposed by Carlos Buenosvinos in his post about it. He suggests that it is the entity itself that performs the publication (through a singleton service). What we propose in the example repository is that it is indeed the entity that records the event, but the publication is handled by the Application Service representing the use case. Therefore, this emphasizes the importance of the semantics of the method "record". This method only saves the event in the entity so that it can eventually be published. This is a detail, but it is relevant since, as we explained in the introduction to Hexagonal Architecture, what characterizes Application Services is to represent an atomic use case. This means that the responsibility of publishing the event should lie with the Application Service, as it determines in this case that the Video has been created and, therefore, the event can be published. If we transfer that responsibility to the entity, we lose the ability to avoid publishing the event due to circumstances arising after instantiation. For example, if that entity cannot ultimately be persisted. Besides the conceptual problems, we believe that it complicates testing too much because it forces us to use stubs when coupling the entity to the singleton that publishes events.
Conclusion
We want to clarify that all this comes from our interpretation regarding CQRS and DDD. As we mention throughout this post, in many cases it is about deciding whether or not it is worth assuming certain drawbacks. Therefore, having explained our stance on this, we would love to hear different alternatives through the comments on the video, in the Slack of BcnEng, or on Twitter 🙂. Lastly, as we said in the talk, we want to thank all those people who have helped us learn all these concepts. Thank you!!!