Update — this article was moved from the personal website into Medium platform.
Date of the article: 19.06.2020
Microservices … we keep hearing this term lately. Is it just a hot topic, new trend in the IT industry, a must-learn for backend developers or perhaps a new standard? What is a microservices architecture? In short, it’s an architectural style where your application is based on a collection of interconnected services. Each service has a specific role in the system. Multiple microservices, each taking care of a certain functionality of an app, combined with clients (e.g. the consuming interface of web and mobile applications) and other (optional) intermediary layers make a microservices-based architecture.
The main topic of this article is to provide a quick overview about the event-driven architecture with microservices using event sourcing and CQRS, with DDD approach.
DDD (Domain Driven Design)
Domain Driven Design specifies a number of patterns and concepts that help designing software effectively, in-line with the business requirements. A business serves within a domain and that’s what we’re trying to model and design with this approach. In the context of DDD, we can derive some definitions of a Domain:
- Root (sub)domain — most important part of your domain — the $$$ maker — key generator for the business
- Supporting — associated to the business but not a differentiator.
- Generic — not really specific to the business
A model is an useful approximation to the problem at hand.”
For example, think about Java. An Employee.class is not a real employee. It models a real employee. We know that the model does not capture everything about real employees, and that’s not the point of it. It’s only meant to capture what we are interested in for the current context.
Different domains may have interest in different means to model the same thing. For example, the salary department and the human resources department may model employees in different forms.
Basically, a model is a system of abstractions that defines aspects of a domain and can be used as a problem solver linked within the domain. We can derive more concrete definitions of a Domain Model in the context of DDD:
- A concept that exists in a solution space
- A software programing model which is applied to a specific domain (problem area)
The Domain Model should define the vocabulary and should act as a communication tool for everyone involved (business and IT) deriving an Ubiquitous Language. This language needs to be rigorous, since software doesn’t really cope well with ambiguity.
When designing a software system, we have the following components for crafting tactical building blocks of a Domain Model:
- Aggregate root
- Value object
- Domain event
DDD is tightly associated with CQRS and Event Sourcing in software development. As it can get very complex, I recommend to go for https://domainlanguage.com/ddd/ to find out more. Eric Evans has a great book about this approach.
What is CQRS?
Command-Query Responsibility Segregation is an architectural development pattern that defines a strict split within an application between the processing operations part and the requesting data part.
“Processing operations” is referred to as the Command side and “Requesting data” part is what is defined as the Query side of an application. CQRS is based on the idea that there are significant benefits resulting from separating code for the write (command) and the read (query) parts of an application.
Basically, with this architectural pattern, we have two different ways to handle the application model.
The main task of the Command Model is handling the expressions of intent which an application might have. The so called expression of intent (known as command), in general, is a business related operation that maps to specific tasks that can be executed. Upon receiving the command, the model will establish whether that task can be performed at that point in time. The process of making decisions when handling the command is guided by business logic within the domain the application is nesting in.
To put it more simply, commands are responsible for changing the application state, like creating, deleting and updating entities (data) or potentially trigger a multitude of side effects. They are shaped reflecting about the Domain rules, restrictions and transaction boundaries. So, the only information which is necessary for the Command Model is no more and no less than the information that is required to make the decisions.
When handling commands, the application model is generally defined by Domain Driven Design constructs, e.g. root aggregates, entities, value objects, etc., and there are generally some kind of rules that restrict the allowed state changes, for example an order has to be paid before delivery.
When it comes to the Query Model (which is also referred to as View Model or Projection), it will not deal with any expressions of intent to change state or perform some actions. Its main scope is to deal with requests for information (queries). An application should be able to handle informations in different formats up to large sets of data and needs to support different ways of retrieving the application’s state.
More simply, queries are responsible for reading the application state, like displaying data to the user. They are created with the presentation layer (client UI) in mind.
When handling queries, the application model is generally defined by entities and relations and can be read much like SQL or NoSQL queries to display data and information. We can quite conveniently use any storage format for the Query Model without influencing the Command Model’s approach to storing its state to take business decisions. Also, it allows implementing several storage types customised towards the type of query which needs to handled.
Queries don’t change state, so they can be requested as much as required and will always return the same values (as long as the application state hasn’t been changed by the commands and events). They are idempotent.
With this pattern, many developers are asking :
How we are going to update UI, we don’t even know whether record is successfully saved or not”.
This is indeed a very good question and from my experience most of the people are only familiar with synchronous communication and always expect data back after a request.
Even though CQRS pattern divides the Command and Query side into dedicated parts, they are still components of the same application. As such, the need for synchronisation is there. The Command Model owns the command handling job and will notify that a decision was made. There are different ways to achieve this, e.g. a common data source between both models or even through stored procedures. However, in my opinion, using events as notifiers for the to solve the synchronization requirement is the best approach. (Event Driven Architecture)
When leveraging the Event Driven Architecture, the Command Model would thus publish an event upon (successfully) handling a command. The event will then be handled by the Query Model(s) to update it’s state accordingly. Following this pattern for synchronization also allows us to update the Command Model based on the events it publishes itself (we will later see in Event Sourcing).
Eventual Consistency & Distributed Transactions
In order to maintain accuracy, it’s vital to ensure that data will be replicated from the event store to the read store of a CQRS application. However, it’s impossible to guarantee precisely when this will occur due to factors like network latency.
Eventual consistency results in a considerably simpler system that is both superior in performance and more painless to operate. But, of course, there is a cost. The CAP theorem by Eric Brewer states that a system can only be consistent or available in the face of partitions but not both.
Owning an eventually consistent system poses challenges in offering a real-time user experience on the UI side, but there are a few solutions for this, depending on which technology stack you use.
Another challenge is a result of having distributed transactions. The entire concept of ACID transactions is no longer simple to uphold anymore, so we embrace the concept of BASE transactions. We would now handle distributed transaction consistency through various orchestration techniques, such as SAGA pattern.
In my opinion, using CQRS pattern, we have a highly beneficial focus when developing software. The segregation allows for dedicated deployments of the Command and Query components.
Non-functional requirements like differing scaling requirements can thus be achieved. What about the situation where the application has high demands for handling queries? We can simply scale up the number of query instances of our application, without affecting the command handling part at all.
Obviously, the same holds if large amounts of state-changes should be processable; we can just introduce more command instances.
Benefits alongside the NFR’s grasp a little further than just scaling. Particular accessibility approached and data persistence options can be chosen per side of the application. Of course, a different choice of database for the Command side in comparison to the Query side has now turned trivial as they no longer depend on one another. Requirements for a more efficient way of storing the application state, to optimise for handling queries, is now a design option for an application.
While Event Sourcing works very well in conjunction with CQRS, it is an unique pattern and it is used in the backend of many of the pieces of software that we use daily, including file systems and (of course), database engines. So, Event Sourcing is a pattern for data storage. The main idea behind it is to store all past changes to that state of an entity instead of storing the current state. The current state is reconstructed based on the full history of events, where each event serves as a change or fact in our application. Events give us a single source of truth about what happened in our application. Compared to a CRUD approach, where only the last state is available after the processing, we start to see some of the advantages of this pattern.
CQRS and Event Sourcing
Event Sourcing is definitely a natural fit with CQRS. Usually, the command model in a CQRS based architecture is not stored, other than by its sequence of events. The query model is updated continuously in order to contain a specific representation of the current state, based on these same events.
Rather than rebuilding the whole command model state, which would be quite a lengthy process, we separate the model in aggregates; parts of the model that require to be solidly consistent. This separation in aggregates makes models easier to reason about, more adaptable to change, and more importantly, it makes applications more scalable, which is a main focus in microservices.
Loading an aggregate state involves reliably re-applying all past events to an aggregate. As we can imagine, after appending a broad number of events, this can become a quite a long process. In order to solve this issue, we can take a snapshot after a certain time, or a number of events, or some other criteria. In this context, a snapshot serves as the current state of an aggregate. Next time we want to do a replay of events, we would start from a snapshot and only replay the events post snapshot creating. Snapshotting does not interfere with normal event processing, usually being an asynchronous process.
Evolvability of software systems is one of the key benefits of Event Sourcing. When we report a new component that needs to be added to the system, we can just code it up, replay historic events to it and have it running. In blue-green deployments, this is highly beneficial when zero downtime is enforced.
Indeed, this pattern provides a fine instrument for synchronising one or more query data models with a command data model, but it also comes with some other indirect benefits:
- Possibility to integrate with external systems using events (events are our API and external system must understand them; of course, we aren’t obliged to publish all events to the integration event hub, but only ones that are publicly important)
- Opportunity to create query models from historical data (as opposed to CRUD’s last update state)
- Audit log (trail of updates made to any piece of data in the system)
- Decoupled side-effects (a command service that emits events can be decoupled from any logic expected to materialize on certain updates)
- Assisted debugging and troubleshooting
Why use CQRS + Event Sourcing
In my point of view, the main problem using the CQRS architectural pattern is the decision of why we should use it in our projects and, of course, the learning curve of CQRS itself.
Growing out of the Domain Driven Design movement, CQRS and Event Sourcing can and will offer the right project some appreciable advantages. A movement designed to handle, reduce and control the growing complexity of software development. As a result, it’s ideally suited to more complex domains. The structure allows you to handle business logic, different models and potentially complicated business rules in a simpler way.
CQRS + ES is suited to problems that have a clear domain model. And given the value attained through event sourcing, it is best suited to business-critical applications. There is an argument to develop less critical software using these ideas but more as a training ground for an under-experienced team.
As with many other top-level architectural patterns, elements can be snatched out for independent utilization. For example, separating out the query responsibility can bring benefits to other styles of apps. We can model the business logic more efficiently if we isolate the domain from external dependencies.
Developing distributed systems with microservices can get challenging. That is what makes novel solutions like Event Sourcing appealing to consider. However, before starting your journey it is essential to understand the multiple impacts of the CQRS and ES patterns. Otherwise, it is extremely easy to generate a complete mess both on the functional and the technical level.
I personally think CQRS + ES is a great pattern for microservice development, and I am currently architecting a private project (Project Alpha for now), trying to leverage this pattern.
Technical wise, the project consists in a mobile application developed with React-Native technology (I find it suitable when we want Android — iOS cross-platform capabilities) which consumes a microservice based cloud backend.
I’m developing a cluster of cloud-compatible and scalable services using Spring Boot. Concepts like application Gateway, OAuth2 and Service Discovery can be implemented pretty straight-forward using Spring Cloud (I’m taking out Spring Cloud Config because I leverage Kubernetes config maps).
Even tough I coped with Apache Kafka (for some reasons) for a while, Axon Framework is my go to when implementing CQRS + ES pattern in Java. Also, we have the Axon Server (SE or EE) which provides a powerful event-store and command-events dispatching capabilities. For the Query part of the application, I’m using Postgres DB to store materialised views (projections) and MinIO to store images and files.
Using Kubernetes as a container orchestration engine, each application is being packaged into a Docker container image for it to run in a k8s cluster. This is among the most essential requirements for building microservices, as it directly ties into how Kubernetes operates as a platform.
Helm is the de-facto form of deploying production-grade applications into Kubernetes, so it’s a clear choice of using it in the project I’m designing. It allows me to describe the application in charts — so I can publish, version, share, and do other useful things with the application.
All of the technologies stated above, alongside with CQRS + ES patterns in microservices, have a pretty steep learning curve, but I think it’s worth it.
In conclusion, I think using this approach is highly beneficial and I’m sure that more and more enterprises will adopt it, especially when converting from complex monolithic software to a microservice cloud based architecture.
*The CQRS schemas presented were inspired by AxonIQ