CQRS on itself is a very simple pattern. It only describes that the component of an application that processes commands should be separated from the component that processes queries. Although this separation is very simple on itself, it provides a number of very powerful features when combined with other patterns.
The diagrams below show an example of an extended layout of a CQRS-based event driven architecture. The UI component, displayed on the left, interacts with the rest of the application in two ways: it sends commands to the application (shown in the top section), and it queries the application for information (shown in the bottom section).
Command handling
The user interface sends commands to make changes to the system. Commands are simple object that contain all the data needed to perform the underlying action. An example of the command could be MoveUserToNewAddress. The command should hold the new address for the user and the user id that indicates which user has moved.
Commands also tent to express intent by there name. For example, although the command MoveUserToNewAddress andCorrectAddressForUser both contain the same data – the address and the user id – the intent is definitely different.
All commands are send to a Command Service. This service receives the commands and routes them to the corresponding command handlers. Command handlers should not contain any business logic. The only thing they do is making changes to aggregate roots from the domain and makes changes to them.
The domain
The command handler retrieves domain objects (Aggregates) from a repository and executes methods on them to change their state. All business logic is captured within these objects and is not used for querying. This allows us to optimize this model for behavior.
The domain events
All state changes in the domain are represented by domain events. They are simple object that contain all data related to the change. We gave two examples of command names. The events that are related to the state change of these commands will be UserMovedToNewAddress and AddressCorrectedForUser. Notice that the names are in the past tense.
The Repository
The event store
All events that have occurred end up in the event store. It contains all the event that represents the state changes in the system. These can be used to build up the current state by replaying them all. This store can also be used to fill up new or repair existing read model.
The event bus
When an aggregate root is saved via the repository all the uncommitted events that represent the state changes that has been made are persisted into the event store. Beside that, the repository also publish these events via the event bus. This bus publishes it to event listener that has registered itself as one being interested in the events. An important choice to make is whether the bus publishes the events to the subscribers in a synchronous or asynchronous way.
Synchronous means that the event handling that is done by the subscribers blocks the current execution and that this waits for all subscribers to complete before returning to the user. This model is simple and is a sensible default.
The event handlers
There are different event handlers subscribed to the events bus. The most common one are denormalizers. These event handlers take events and makes changes to the read model based on them. For example, a denormalizer could update the users address in the user table based on the data in the UserMovedToNewAddress event. But it could also update the total number of users in city X based on that the same event.
Event handlers are not only interesting to keep the read model up to date. But they could also be written to make changes to an external system or send warning email to the business when certain event occur. It could also be that an event handler issues a new command . Event handlers are great components to extent the system with new functionality without making changes in it.
Read models
An important part of every application is data. Most of the screen in an user interface request it. But every screen just tents to have a different view on the data. For example, one wants all the products with there name, price and category, while another wants the products with there name and top 3 latest product review score and name of the reviewer.
Read models are models that can be optimized for data querying. And what is a optimal query? That is a query that just queries the data from one source. In other words: select * from x where y. No joining, just give me the data. We can accomplish this by creating one table per view. So that every view can just request data by a simple query.