A common issue I see is understanding the flow of commands, events and queries within a typical CQRS ES based system. The following post is designed to clear up what happens at each step. Hopefully this will help you to reason about your code and what each part does.
Typically the UI displays data to the user. The user generates a command by requesting some form of change.
The message bus is responsible to routing the command to it’s handler.
The handler gets the aggregate root and applies all previous events to it. This brings the AR (aggregate root) up to it’s current state. This is typically very fast, even with thousands of events, however, if this becomes a performance bottleneck, a snapshotting process can be adopted to overcome this issue.
Once the AR is up to date the command is issued. The AR then ensures ensures it can run the command and works out what would need to change but DOESN’T at this point change anything. Instead, it builds up the event or events that need to be applied to actually change it’s state. It then applies them to itself. This part is crucial and is what allows ‘events’ to be re-run in the future. The command phase can be thought of as the behaviour and the apply phase is the state transition.
At this point, assuming no exceptions have been raised, the command handler requests the uncommitted changes. Note that no persistence has actually taken place yet. The domain classes have no dependencies on external services. This makes them much easier to write and ensures they are not polluted by persistence requirements (I’m look at you Entity Framework).
Here is when an event storage service comes into play. It’s responsibility is to persist the events and also to ensure that no concurrency conflicts occurs. You can read up on how to do this on a previous post of mine: How to handle concurrency issues in a CQRS Event Sourced system.
Unlike commands which only trigger 1 command handler, events can be routed to multiple de-normalisers. This enables you to build up very flexible optimised read models.
The concept of a de-normaliser can at first be a little tricky. The problem is that we are all trained to think in ‘entities’, ‘models’ or ‘tables’. Generally these are derived from normalised data and glued together into the form required for the front end. This process often involves complex joins, views and other database query techniques. A de-normaliser on the hand translates certain events into the perfect form required for the various screens in your system. No joins required at all, ever! This is makes reads, very fast and is the basis behind the claim that this style architecture is, almost, linearly scalable. Most people begin to get twitchy at this point when they realise that duplicate data may exist in the read model. The important thing to remember is that the ‘event stream’ is the only source of truth and there is no (or should be no) accidental duplication within it. This allows you to re-create the entire read model or just parts of it, at will.
The final phase of the de-normaliser is to persist the simple DTO’s (data transfer objects) to the database. These objects and essentially property buckets and usually contain the ID of the aggregate they are associated with and a version number to aid in concurrency checking. These DTO’s provide the information the user requires, in order to form new commands and start the cycle over again.
The read/query side is entirely independent of the commands and events, hence CQRS (Command Query Responsibility Segregation). The query side of the application is designed to issue queries against the read model for DTO’s. This process is made entirely trivial due to the de-normalisation of the read data.
All the data is optimised for reading. This makes querying very simple. If you require values for a ‘type ahead drop down list’, just get the items from an optimised list designed especially for the task. No extra data need be supplied apart from that required to drive the drop down. The helps keeps the weight of the data payload light which in turn helps the appication remain responsive to the user.
The read model just returns simple and slim DTO’s that are, as I said before easy to work with on the front end.
CQRS’s biggest hurdle is it’s perceived complexity. Don’t be fooled by all the steps above. Unlike a ‘simple CRUD’ approach which starts off simple but quickly gains in complexity over time. This approach remains relatively resistant to increased complexity in the scope of the application.
I'm a professional software engineer of near on 15 years. Lucky enough to work for a small but rapidly growing company in London called Redington. They have given me the technical freedom to learn some cutting edge technologies like CQRS and Event Sourcing. Now I'm sharing what I learn here.
Please log in again. The login page will open in a new tab. After logging in you can close it and return to this page.