CQRS
Command Query Responsibility Segregation separates write models optimized for decisions from read models optimized for queries, at the cost of consistency and plumbing.
Definition
CQRS separates the model used to change state from the model used to read it. Commands validate intent and update the write model; queries read one or more projections shaped for the screen or API. The two models may share a database or use separate stores connected by events. CQRS is often paired with event sourcing but does not require it.
Context and scope
CQRS is useful when read and write workloads have different shapes, scaling needs, or authorization rules, such as dashboards over complex transactional data or high-volume event processing.
Common misconceptions
- CQRS requires event sourcing — it can be implemented with a shared relational database and simple projections
- Every system needs separate databases — separating models is the core idea; separate stores are an optional scaling decision
- CQRS removes consistency problems — it moves them into projection lag and read-your-writes design
Anti-patterns
- Applying CQRS to a small CRUD service with identical read and write shapes
- Letting the write model serve dashboard queries because the projection is not ready
- Building projections without a rebuild strategy or a position checkpoint
Related concepts
Authoritative source
Different shapes for different jobs
A write model enforces invariants: an order cannot be paid twice, inventory cannot go negative. A read model is shaped for a screen: an order list with customer names and status badges. Forcing one model to do both often produces a compromise that is slow to query and awkward to validate.
CQRS separates them. A command handler loads the write model, validates the command, and commits a change. A projector consumes the change and updates a read table or search index. Queries read the projection.
POST /orders -> command handler -> write model -> event
|
v
projector -> orders_read
GET /orders -> query handler -> orders_read
Consistency is explicit
When the write store and read store are separate, a read immediately after a write may not see the change. This is projection lag, not a bug. Decide per screen: redirect to a detail page that reads the write model, show an optimistic local update, or display a “processing” state until the projection catches up. Monitor lag and alert when it exceeds the product’s tolerance.
Projections need a checkpoint. On restart, resume from the last processed position instead of replaying the entire history. If a projection is wrong, rebuild it into a new table and switch reads when it is caught up. Keep the old projection available for rollback.
Authorization and boundaries
Read models may expose data to users who should not see it. Apply authorization in the query handler or at the row level; do not assume the write model’s rules carry over. A projection that copies sensitive fields into a search index creates a new data-retention and access-control surface.
When to use it
Use CQRS when read and write workloads genuinely differ in shape or scale, or when multiple projections serve different consumers. The advantage is focused models and independent scaling; the drawback is eventual consistency, duplicated data, and projector operations. Compared to a single model, CQRS adds plumbing that is only justified when the separation solves a measured problem.