#The modest version
Command and query responsibility segregation says that the model you write through and the model you read through do not have to be the same model . That is all it says. Everything else that travels with it, including event sourcing, aggregates, sagas and a message bus, is optional.
The reason it attracts more architecture than it needs is that the write model and the read model really are different in most systems, and once you have admitted that, a lot of other separations start to look reasonable. Some of them are. Most of them are not free.
#Where it paid
Three places in Ultimate Backend justified the cost.
Reads that span aggregates. A dashboard showing an organisation, its members, their recent activity and its subscription state touches four write models. Assembling that from the write side means four queries and a join in application code. A read model built for the dashboard is one query.
Writes with real invariants. Adding a member to an organisation has to check the seat limit on the subscription. That is a decision, and decisions belong on the write side where the invariant lives. Keeping the write model small and focused made those checks obvious rather than scattered.
Audit. Once commands are explicit objects, recording what was attempted, by whom, and whether it succeeded is a property of the pipeline rather than something each handler remembers to do.
#Where it cost more than it returned
Eventual consistency between the write side and the read side is a genuine user-visible cost. A user who saves a form and immediately sees stale data does not care about your architecture. The mitigation is either to read from the write side for that specific case, or to hold the response until the projection catches up, and both are compromises you should choose deliberately.
The other cost is conceptual. A team that has not used the pattern will look for the object that "is" the entity, and there isn't one, and explaining that repeatedly has a price measured in weeks.
#On event sourcing specifically
Event sourcing is a separate decision . It says the events are the source of truth and current state is derived. That buys a complete history and the ability to build a new read model over past data, which is genuinely valuable.
It charges you schema evolution. Events written in 2019 must still be readable in 2024, which means versioning them, upcasting old ones on read, and deciding whether a projection rebuild is safe. None of that appears in a tutorial, and all of it appears in year two.
The way I would summarise the whole family now is the way Helland framed the underlying constraint and Kleppmann later laid out in detail . Once your data does not fit in one transaction, you are choosing which inconsistency to accept. CQRS is one way of choosing. It is not a way of avoiding the choice.
References
- [1]Greg Young, “CQRS Documents”, cqrs.files.wordpress.com, 2010
- [2]Martin Fowler, “Event Sourcing”, martinfowler.com, 2005https://martinfowler.com/eaaDev/EventSourcing.html ↗
- [3]Pat Helland, “Life beyond Distributed Transactions: An Apostate's Opinion”, Conference on Innovative Data Systems Research (CIDR), 2007
- [4]Martin Kleppmann, “Designing Data-Intensive Applications”, O'Reilly Media, 2017