Architecture Styles Essential

CQRS Pattern

Command Query Responsibility Segregation: separate write models (commands) from read models (queries) to independently optimize each for its workload.

⏱ 9 min read

What it is

CQRS (Command Query Responsibility Segregation) is an architectural pattern that divides a system's operations into two distinct models: Commands that change state (write model), and Queries that return data (read model). These models can use completely different data representations, storage technologies, and optimizations. The write model enforces domain invariants and maintains strong consistency; the read model is denormalized and optimized for specific query shapes. Greg Young formalized CQRS as a natural extension of Bertrand Meyer's Command Query Separation principle (a method should either be a command that performs an action or a query that returns data, but not both). CQRS can be applied at different scales — from simple method separation within a single service to fully separated write and read databases with asynchronous projection updates.

Why it exists

In traditional CRUD systems, the same model serves both reads and writes. This creates tension: the write model needs normalization and invariant enforcement; the read model needs denormalization and query performance. A customer dashboard view may join 10 tables — but the write model doesn't need those joins. Conversely, optimizing the write model for read queries degrades write performance and complicates domain logic. CQRS resolves this by allowing each side to evolve independently. Read models (projections) can be rebuilt from scratch by replaying history; they can be optimized for specific UI shapes with no coupling to the write model. When combined with Event Sourcing, CQRS also enables temporal query capability — "what did this entity look like at 2pm yesterday?"

When to use

  • Read and write workloads have significantly different performance profiles (heavy-read, low-write systems, or write-intensive with complex aggregations).
  • The write model is a rich domain model with complex invariants that would be degraded by read optimization concerns.
  • Multiple distinct read models (dashboards, reports, mobile views) need different projections of the same underlying data.
  • Combined with Event Sourcing — CQRS + ES is the most natural and powerful pairing.
  • Scalability requirements: read replicas and caches for the read side, independent of write path scaling.

When not to use

  • Simple domains where a single CRUD model is sufficient — CQRS adds significant complexity for no benefit.
  • When immediate consistency after writes is required in the UI — async projection updates mean queries may return stale data briefly.
  • Small teams without the bandwidth to maintain two separate data models and projection update logic.

Typical architecture

Commands go through the domain model which maintains write-side state. After processing, the write model publishes domain events that update read-side projections, which are optimized views consumed by queries.

  ┌──────────┐    Command     ┌─────────────────┐
  │  Client  │──────────────▶│  Command Handler │
  └──────────┘               │  (Domain Logic)  │
       │                     └────────┬─────────┘
       │                              │ Persist + Event
       │                     ┌────────▼─────────┐
       │                     │   Write Store     │
       │                     │  (Normalized DB)  │
       │                     └────────┬─────────┘
       │                              │ Domain Events
       │                     ┌────────▼─────────┐
       │                     │  Event Projector   │
       │                     │  (async/sync)      │
       │                     └────────┬─────────┘
       │                              │ Updates
       │ Query              ┌─────────▼──────────┐
       └───────────────────▶│   Read Store        │
                            │ (Denormalized Views)│
                            └────────────────────┘

Pros and cons

Pros

  • Write and read models evolve independently — change a query projection without touching domain logic.
  • Read models can be database-technology-specific for query optimization (ElasticSearch, Redis, read replica).
  • Projections can be rebuilt at any time — derive new views from existing write history.
  • Simplifies the domain model by removing read-side complexity from domain objects.
  • Read side scales independently from write side.

Cons

  • Eventual consistency between write and read models — UIs may show stale data after a command.
  • Significantly more code: command handlers, events, projectors, read models, query handlers.
  • Projection logic bugs can corrupt read models; requires rebuild strategy.
  • Debugging "why is my query returning wrong data?" requires tracing through projection logic.

Implementation notes

Start with logical CQRS (separate command and query objects in the same service against the same database) before jumping to physical CQRS with separate databases — most systems get 80% of the benefit from logical separation alone. When moving to physical CQRS, decide on synchronous projections (updated in the same transaction as the write) vs. asynchronous projections (updated via events after the write). Synchronous projections sacrifice decoupling for consistency; async projections are more scalable but require clients to handle eventual consistency. Always include a projection rebuild mechanism — a command to replay all events and regenerate a projection from scratch. Use optimistic concurrency on the write side (version numbers on aggregates) to prevent lost updates.

Common failure modes

  • Applying CQRS to everything: Using full CQRS for simple lookup tables or configuration data adds overhead with no benefit.
  • No projection rebuild mechanism: A projection bug has been deploying for a week but there's no way to fix the corrupted read model without manual SQL.
  • Stale read UX failures: User submits a command, immediately queries, and sees the old data — the UI needs to handle eventual consistency (optimistic updates or polling).
  • Command handler with query logic: A command handler calls the read model to make a decision — violates the segregation and creates circular dependencies.
  • Over-normalized write model: The write model becomes an ORM aggregate dump rather than a rich domain model, losing the key benefit of CQRS.

Decision checklist

  • Is there a genuine mismatch between write and read complexity that justifies separate models?
  • Can the UI handle eventual consistency (optimistic UI updates or polling after a command)?
  • Is there a projection rebuild strategy for when projection logic needs to be corrected?
  • Are command handlers free of query logic (no reading from read models to make write decisions)?
  • Is optimistic concurrency implemented on the write side to prevent lost updates?
  • Is a logical CQRS approach sufficient, or is physical separation (separate DBs) actually needed?

Example use cases

  • Collaborative document editing: Write model enforces document structure and version invariants; multiple read projections (search index, activity feed, recent changes) are rebuilt from the same write events.
  • E-commerce order management: Order write model enforces stock reservation invariants; read projections power order history (customer-facing), fulfillment dashboard (ops), and analytics (business intelligence) separately.
  • Financial trading platform: Trade execution write model is simple and strongly consistent; read models power portfolio views, P&L dashboards, and audit reports — each with different retention and format requirements.
  • Event Sourcing — The natural partner to CQRS; events are the write model; projections are the read models.
  • Event-Driven Architecture — Domain events bridge the write and read models in async CQRS.
  • Hexagonal Architecture — Provides the structural home for CQRS command and query ports within the application core.

Further reading

  • CQRS — Martin Fowler's concise overview of the pattern.
  • CQRS Pattern — Microsoft Azure Architecture Center.
  • CQRS Documents — Greg Young's original CQRS documentation.