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.
1. A Command is generated from the UI
Typically the UI displays data to the user. The user generates a command by requesting some form of change.
2. The Message Router or Bus receives the command
The message bus is responsible to routing the command to it’s handler.
3. The handler prepares the aggregate root
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.
4. The command is issued
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.
5. The command handler requests the changes
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).
6. The command handler requests persistence of the uncommitted events
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.
7. The events are published onto the Bus or Message Router
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.
8. De-normalisers build up the Read Model
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.
9. Data Transfer Objects are persisted to the Read Model
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.
All this results in a Highly Optimised Read Side Model
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.
A. User requests 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.
B. Simple Data Transfer Objects
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.