vishal patel
UnderstoodAdvancedUpdated 2026-09-23

CQRS (Command Query Responsibility Segregation)

Use one model to change data (commands) and a different, optimised model to read it (queries) — often kept in sync by events.

read-modelsscalabilityeventsddd

The problem

One model has to serve both sides. Writes need validation and invariants, on a normalised shape. Reads need denormalised, pre-joined and filterable shapes, and there are often 100× more of them. Optimising one hurts the other.

How it works

diagram
  • Commands express intent, may be rejected, and return little or nothing.
  • Queries never change state.
  • Projections build one or more read models, each shaped for a screen or API.

Levels of CQRS

  1. Code-level: separate command and query classes over the same DB. Cheap and often enough.
  2. Separate read tables or views in the same DB, updated in the same transaction.
  3. Separate read stores (Elasticsearch, Redis, a denormalised Mongo collection) updated asynchronously. Eventual consistency starts here.
Use it when
  • Read and write loads are very different
  • Read screens need heavy joins or aggregates
  • Combined with event sourcing or event-driven integration
Avoid it when
  • Simple CRUD — this doubles the model for no gain
  • Users must always read their own write instantly and you can't design around it

Trade-offs

  • ✅ Each side scales and is optimised independently.
  • ✅ New read models can be added later by replaying events.
  • ❌ Eventual consistency in the UI. Mitigate with "read your writes" tricks (return the new state from the command, or poll).
  • ❌ More moving parts: projectors, rebuilds, monitoring projection lag.

Sources & further learning

Videos, courses, docs and books I recommend for this topic.

Related topics