CQRS Pattern
One model for changing data, another for reading it. Know where the idea comes from, the three levels you can apply it at, and the consistency cost that comes with the second store.
Overview
Command Query Responsibility Segregation (CQRS) splits a system's model in two. Commands change state and return nothing; queries return data and change nothing. Instead of one set of classes that does both, the write side gets a model built for rules and validation, and the read side gets a model built for the shapes the screens need.
The name is Greg Young's. His *CQRS Documents* (2010) describe it as growing out of Bertrand Meyer's Command-Query Separation, a rule for single methods: a method either changes state or returns a value, never both. Young's step was to apply the split to whole objects, so that one object holds the commands and another holds the queries. Martin Fowler's 2011 bliki entry credits Young and adds a warning that most systems do not need it.
Interviewers bring CQRS up in two ways. In a design question they want to see whether you can name the trade-off (a read model that lags the write model) before they ask. In a low-level question they want the shape: a command handler, a query handler, and what carries changes from one side to the other.
From CQS to CQRS
- Applies to — One method — One model or service
- Rule — A method changes state or returns data, not both — Commands go to a write model, queries to a read model
- Shape — account.deposit(10) returns nothing; account.balance() changes nothing — PlaceOrderHandler and OrderSummaryQuery are different classes, often with different data
- Cost — Almost none — Two models to keep in step
Fowler keeps one exception to CQS worth quoting: popping a stack both changes it and returns a value, and that idiom is too useful to give up.
The shape
The write side owns the rules: it loads an aggregate (an object that keeps its own invariants), applies the command, and saves. The read side has no rules at all; it returns rows shaped for a page, often denormalized so that a screen is one lookup instead of five joins.
Three levels of the same idea
Fowler notes that both models may share one database, in which case the database is the channel between them. The Azure Architecture Center describes the two ends of this ladder: separate models on one store, and separate stores kept in step by events.
- 1. Split the code — Command handlers, domain model — Query handlers with their own DTOs — Same tables — Immediate
- 2. Split the schema — Normalized tables — Denormalized views or tables in the same database — A trigger, a transaction, or a job — Immediate or slightly behind
- 3. Split the stores — Its own database — Its own database, maybe a different kind — Events published on write, consumed by a projector — Eventual
Level 1 costs little and already gives clearer code. Every level after it adds a moving part. A strong answer starts at level 1 and climbs only with a reason.
A runnable example
The command side keeps the rule (no more than five items in an order) and records what happened as events. The projector turns events into a read model that answers the screen's question in one dictionary lookup.
In this example the projector is called in the same process, so the read model is updated immediately. Put a message queue between publish and on_event and the same code becomes level 3, with a lag.
The consistency cost
- Return the result from the command — The write side answers with the new state, so the UI does not need to query — The screen only needs what it just changed
- Optimistic UI — The client shows the change at once and reconciles later — Low-stakes updates, such as a like or a comment
- Read your own writes — The command returns a version; the query waits until the read model reaches it — Lists the user expects to see updated now
- Say it out loud — The UI shows 'processing' until the projection catches up — Anything with money in it
When to use it, and when not
Fowler's advice is blunt: for most systems CQRS adds risky complexity, and where it fits, it fits one bounded context, not the whole system. The Azure guidance names the cases where it earns its keep.
- Reads and writes have very different shapes or loads — The domain is simple CRUD
- Many users change the same data and commands need to be fine-grained to avoid conflicts — One model already serves every screen
- The UI is task-based ('book a room'), not form-based ('set status') — The team has not run an event-driven system before
- Separate teams own the write rules and the read screens — Users must always see their own change immediately, and you cannot afford the plumbing above
What you will be able to do
- Say where CQRS comes from (Meyer's CQS, Young's split) and what it separates.
- Draw the two paths and name the part that carries changes across.
- Place a design on the three-level ladder and justify the level.
- Name the consistency cost before the interviewer asks, and give two ways to handle it.
- Decide, and say why, when a plain layered design is the better answer.
Interview questions
- What problem does CQRS solve that a layered CRUD design cannot?
- What are the trade-offs of CQRS?
- Does CQRS require two databases or Event Sourcing?
- A user places an order and refreshes at once. The order is missing. What do you do?
- Commands should be task-based. What does that mean, and why does it matter?
Sign up free to read the full topic
More Low-Level Design topics
Start free on Cariara · Coding problems · System design studio