From the beginning of March to the end of August 2022, I wrote my master’s thesis in the field of software architecture. The complete title of my thesis is as follows:
Event Sourcing, CQRS, and Domain-Driven Design, and Their Application to Event-Driven Microservices
The title on its own involves quite a number of concepts already. There is no chance to cover all of them in extensive detail in this blog post. Rather, I would like to share some of my findings and things I learned during writing my thesis. For this reason, the present post requires a basic understanding of the concepts discussed. Some sections refer to a proof of concept, which is an event-driven library application, that I developed as part of my thesis.
Does combining event sourcing, CQRS, and DDD make sense?
Foremost, I find it astonishing how well event sourcing, CQRS, and domain-driven design (DDD) complement each other. Although all concepts are fundamentally different and have totally different purposes, they work very well in conjunction. But why is that? Well, I came up with several reasons that I want to describe briefly in the following:
First, CQRS is a natural consequence of event sourcing. According to Greg Young (who coined the notion of event sourcing and CQRS), CQRS was always intended to be a stepping stone toward the idea of event sourcing. Although CQRS can be applied without event sourcing, it actually originates from the event sourcing concept.
Second, all concepts focus on behavior and business capabilities rather than on storing state or other technical aspects. DDD focuses on the domain, its natural boundaries, language, and behavior. Everything within that domain is made explicit, for example by defining bounded contexts and domain events. We can store those domain events in a proper event store and build the complete application state from our events. As event sourcing is not (always) appropriate for queries, we can apply the CQRS principle and implement read models better suited for the execution of queries.
Third, all concepts are pretty straightforward to apply, however, they come with a steep learning curve. It took me a while to actually understand these concepts and answer very detailed questions, but I think it is worth it. Event sourcing and CQRS are gaining popularity. DDD seems to be well established in many industries and software companies.
To provide a short answer to the question above: Yes, the combination of event sourcing, CQRS, and DDD makes sense when we want to make things explicit in a domain or some part of it.
How do these concepts apply to event-driven microservices?
Microservices are rooted in the ideas of domain-driven design, especially in the notion of a bounded context. In my research, I found that every microservice is a bounded context, but not every bounded context is a microservice. Aligning microservices with subdomains seems to be a safe heuristic. Subdomains can be identified as part of the strategic design principles of DDD. I could successfully apply this approach in my proof of concept, where I was able to do a one-to-one mapping of subdomain and (event-driven) microservice.
Not every microservice is an event-driven microservice. An event-driven microservice shares many characteristics of the traditional microservices architecture style, though. Event-driven microservices should be autonomous and asynchronously communicate with other services through events.
The inter-service communication aspect is exactly where event sourcing can be handy. Since events are already modeled as domain events within a service boundary, it is more straightforward to use those domain events to communicate with other services. However, usually, we don’t want to use internal domain events to communicate with other services, because domain events leak data that might not be understood by other services. Furthermore, it might hurt the services’ autonomy. Instead, we “transform” those events into some kind of thin events. By the way, EventStorming can help with identifying those special events, which in this context are referred to as Pivotal Events.
I had the privilege to talk with Mauro Servienti, a highly experienced solution architect, and he stated that the highest degree of autonomy can be achieved by following a share-nothing policy. Sharing data should be limited to very stable data, such as identifiers. In my proof of concept, all events that are used to communicate between service boundaries consist of identifiers only. As identifiers are not expected to change, we can consider them stable. A thin event that notifies other service boundaries about a student that was matriculated could look as follows:
student matriculated event:
{
"studentId": "01e2c0c8-d4f5-43af-a9b4-6d8c12933203"
}
Are these concepts in combination a silver bullet?
As with everything in software architecture, all concepts come with benefits and shortcomings. This also applies to the combination of event sourcing, CQRS, DDD, and event-driven microservices. Especially when working on my proof of concept, I realized that the concepts’ combination might not always be beneficial. Even though they come with a great amount of flexibility and great scalability options, applying event sourcing and CQRS to event-driven microservices increases the system’s complexity a lot. Remember that for every event-sourced microservice, you need to maintain one event store instance, at least one projection, and at least one read model. Each contributes to the overall complexity of the system and represents moving pieces within the architecture. Although these building blocks are usually not very complex, their impact must still be taken into consideration when designing a solution architecture, in my opinion. In the case of my proof of concept, at least one of my four service boundaries could be implemented without event sourcing and CQRS, as it mainly represented traditional CRUD behavior.
In conclusion, I found that the strategic design principles of DDD can be applied to the complete solution architecture, but we want to be a bit more careful when it comes to event sourcing and CQRS. Event sourcing might not be appropriate when there is not much behavior in the domain/subdomain. CQRS might not be necessary if there is just one view of the data within a domain, or if scaling the command and query side independently is not essential.
My takeaways
The following list shows some of the results and personal findings that I find worth mentioning when it comes to what I have learned from my master’s thesis:
- Focus on business capabilities, not entity services.
- Don’t think of microservices, but rather in service boundaries.
- There are problems that are not technical, even if they look alike.
- Concentrate on what’s really important from a business perspective.
- Dive deep into how the domain works, which helps to handle special cases and race conditions.
- …
This short post just covered some aspects of my thesis, in fact, it touched on many more interesting areas, which cannot be part of this very first blog post. Some of the concepts discussed in this post will probably be covered in future posts. I am looking forward to writing about more specific aspects and approaches in future blog posts, and highly appreciate it if you check them out as well.
Thanks!