I have been on a two day intensive DDD course with Greg Young. The course was great both from a social and educational perspective. To make sure I don’t forget all of what I’ve learned at the course I will try to write a couple of blog posts to remind myself, and perhaps give others some valuable information.
The majority of the course revolved around the Command Query Separation (CQS) architecture promoted by Greg in various presentations.
<EDIT>
We now call it command and query responsibility segregation CQRS :)
</EDIT>
http://www.infoq.com/presentations/greg-young-unshackle-qcon08
http://www.infoq.com/interviews/greg-young-ddd
http://www.vimeo.com/3171910
The CQS architecture follows the same principle as Bertrand Meyer’s Query Command Separation principle ( http://en.wikipedia.org/wiki/Command-query_separation ), but they are not the same.
Overview of the CQS architecture
One of the main ideas of the CQS architecture is keeping a clear separation between changing the state of the domain and querying . The key is that:
“A single model cannot be appropriate for reporting, searching and transactional data”
In box style the architecture it will look something like this:
The write side
The UI will query the read model to get the DTOs it wishes to display. In the UI the user will build up commands and send them to the domain. The commands will be in the imperative form like “ChangeDescriptionCommand”. The domain will process the commands and events. The events will be in the past tense like “DescriptionChangedEvent”. The language is important. It does not need to be a one-to-one relationship between the commands and the events as in the example I just gave.
The read side
On the “read” side the denormalizer subscribes to the events from the domain, and changes the model according to the events published by the domain. The read model is usually optimized for querying, and is often kept in 1. normal form. Depending on your non-functional requirements, the read model can be fully consistent or eventually consistent with the domain.
Add only model
The domain events are stored in the event store. The events are serialized and stored in the order the are published. The event store is important. Notice we have a add-only model. As opposed to the common CRUD paradigm we are only adding information. We are not updating or deleting anything in the event store. That means that we don’t loose any information. Consider a shopping cart. A user adds an item to the shopping cart, and then he removes it again. In a common CRUD scenario we would insert the item and then delete it. When inspecting the data we would see an empty shopping cart. The fact that it was added and then removed is lost. In a add-only model we would still have this information. For “the business” these things can be very valuable.
Of course in a CRUD model you could start recording this information and in a couple of months you could start giving “the business” reports about users adding and removing items. In an add-only model your could create a report of user behavior from the beginning of time. Do you see the difference ? Using an add only model you don’t loose information. You are capturing user behavior as opposed to just updating the data. It’s sweet.
Loading an aggregate root
Loading an aggregate root is a little bit different. Since you are not storing the current state of the domain, but the just the events that have happened you need to load up the aggregate root by supplying all the events that have occurred since the beginning of time and replaying these. This will obviously give an performance problem if you need to load 500 000 events to get your aggregate root to the current state. To solve this problem you can store a snapshot of the aggregate root at certain intervals. When loading the aggregate you load up the last snapshot of the aggregate and replay only the events that happened after the last snapshot.
Implementing the event storage and the snapshots is straight forward, but as in all systems you need to handle concurrency issues which makes it a little bit tricky.
To be continued..
I’m planning to make a series of blog posts digging deeper into various topics and looking at how you would solve some of the obvious problems you will run into using this architecture. I am hoping to cover the following topics, but no promises :)
- Building up commands
- Handling commands
- Building up aggregate roots using the repository and event storage
- Creating the event storage
- Concurrency issues
- Set based validation
- Testing the domain in a BDD style
- Testing the denormalizer
- Disconnected clients
- Contract first development
- Enriching events and moving domain logic into independent components.