What i like about command query segregation is that it allows you to clearly distinguish between operations that modify states (via persisting aggregates) and queries that only read. Reads require very little business logic or behavior so why do many system still handle reads the same way (architecturally speaking) as writes? Why do reads need to pass through a domain repository, a domain entity and (god forbid a domain “manager” class), then only to get mapped to a DTO and later a view model?
Realizing the big difference between reads and writes has been the biggest change in mindset for me the last year. When you realize this you can create different architectures and layering for reads and for writes.
For example queries can be handled like this:
The above query handler needs to return product details and the order count in which that product exists. No behavior, no business rules, no domain entities or aggregates needed, just data that needs to be returned to later be displayed in a GUI. So no repository needed! So how do you test the query handler? The same way you would test the repository method, using a database test (preferably in-memory).
If you read a whole aggregate in a query handler something is most likely wrong, aggregates should only be read if you need to perform a command (modify it’s state).
Separating between read models and write models makes a huge difference. Write models will be modeled using domain driven design, aggregate roots should correspond to transactional boundaries. Aggregates should not require a change just because we need to display more data in the GUI. I have seen domain entities filled with relationships and properties only there because some part of the GUI requires it.
If you use your domain aggregates when reading and displaying data it can lead to a huge amount of unnecessary data in the aggregate, data that is never used by the business rules but is still being read from the database on every write operation.
There is an interesting consequence in event sourcing, in that it allows you to create a domain model state that only contains state (data) that is necessary for the behavior, if you have data that is only required for reads then you can ignore that data in your aggregate (it will be stored in the event store, and in the read model).
I am only a novice when it comes to Event Sourcing and CQRS but I find both incredibly interesting. I like how they both create pretty big limitations that force you toward something good, and it makes you think more are about domain driven design, about boundaries and responsibilities and about intent. I would love to get the chance to try event sourcing and pure CQRS (separate read/write model and persistence) in a real project.
For more on CQRS: