Sign in
TodayMapLearnPracticeReview
Library
12 MINadvancedDesign PatternsNot started

CQRS

CQRS (Command Query Responsibility Segregation) separates the model used to *write* data from the model used to *read* data. Instead of one shared schema optimized for neither, you have a write model optimized for transactional integrity and a read model optimized for queries — kept in sync by events. The two models can live in different stores (e.g., Postgres for writes, Elasticsearch for reads), be scaled independently, and use different shapes (normalized vs denormalized). The trade-off is operational complexity: you now have two systems to keep consistent instead of one.

Why this matters

Most systems have wildly asymmetric read/write ratios — a social feed might be 99% reads, 1% writes. A single model forces compromise: either writes are slow (because reads need denormalization) or reads are slow (because writes need normalization). CQRS breaks the compromise: optimize each side for its own workload. It also unlocks patterns like event sourcing (write model = event log), materialized views (read model = precomputed projections), and independent scaling (more read replicas, fewer write replicas).

Prerequisites
  • Event Sourcing
  • SQL vs NoSQL
Related
  • Event Sourcing
  • Materialized View
  • Denormalization
Used in
  • Event Sourcing
  • Materialized View
Lesson

How it works

In a typical CRUD application, one model serves both writes (insert/update/delete) and reads (select). This works for simple cases but breaks down when:

  • Read patterns differ from write patterns (reads need denormalized views; writes need normalized tables).
  • Read volume vastly exceeds write volume (you need many read replicas but few write nodes).
  • Reads need full-text search, graph traversal, or aggregations the relational store can't do efficiently.
  • Different teams own different views of the same data.

CQRS splits the model:

  • Command side (writes): the source of truth. Validates business rules, applies state changes, emits events. Optimized for transactional integrity, normalization, consistency. Usually a relational database or an event log.
  • Query side (reads): projections of the write-side data, optimized for queries. Often denormalized, indexed differently, or in a different store entirely (Elasticsearch, Redis, a denormalized Postgres view, a graph database).
  • Synchronization: the command side emits events when state changes; the query side subscribes and updates its projections. Eventually consistent — the read model lags the write model by milliseconds (or seconds, under load).

The key insight: a single model is a compromise. Two models cost more but each can be optimal for its workload.

CQRS is powerful but expensive. Use it when:

  • Read and write workloads differ significantly — e.g., 100× more reads than writes, or reads require complex joins/search the write store can't do efficiently.
  • You need different read shapes — same data, viewed differently: by user, by tenant, by status, by date range, full-text. Each is a different projection.
  • You need independent scaling — read replicas can scale reads, but they share the write store's schema. CQRS lets you use a different store entirely.
  • You're already doing event sourcing — CQRS is the natural companion: write side is an event log, read sides are projections.
  • Multiple bounded contexts need the same data — each context owns its own read model.

Do NOT use it when:

  • The system is simple CRUD with similar read/write loads — the complexity isn't worth it.
  • Team is small and can't operate two stores and a sync pipeline.
  • Strong consistency is required on reads (CQRS is fundamentally eventually consistent).
  • The write model would just be a thin wrapper over a relational store — you're adding indirection for no benefit.

A common CQRS anti-pattern: applying it everywhere by default. Most domains have one part that genuinely benefits (e.g., the activity feed) and ten parts that are simple CRUD. Apply CQRS surgically, not fleet-wide.

CQRS + Event Sourcing

CQRS and Event Sourcing are often paired but are independent patterns. Event Sourcing stores writes as an append-only log of events (OrderCreated, OrderShipped) — the write model is the log, not the current state. CQRS separates read and write models. Combined: the write model is the event log, and read models are projections rebuilt from the log. Without event sourcing, CQRS can still use a traditional database as the write model — but the combination is what unlocks time-travel queries, audit trails, and rebuilding read models from scratch.

The defining property of CQRS is that the read model is eventually consistent with the write model. After a write, reads may briefly return stale data.

This is usually fine:

  • A user updates their profile and sees the old version for 50ms? No one notices.
  • A search index lags the database by 200ms under load? Acceptable for most queries.

But it's NOT fine for:

  • Read-your-writes consistency: the same user who just wrote should see their write immediately. Solutions: route the user's next read to the write store, or wait for the read model to catch up before responding.
  • Cross-aggregate transactions: writing Order A and updating Customer B's balance in the same transaction — possible in a relational DB, impossible across CQRS without sagas.
  • Real-time requirements: stock trading, auctions, locks — these need strong consistency, not eventual.

Practical mitigations:

  • Versioned reads: include a version/etag; the client knows if it's stale.
  • Sticky routing: send a user's reads to the same region/replica that handled their write, so they see their own writes immediately.
  • Read-after-write from the write store: for critical reads, bypass the read model.
  • Synchronous projection: update the read model in the same transaction (defeats much of the scalability benefit, but possible for low-volume critical paths).

Always ask: 'how stale can a read be?' If the answer is 'zero,' CQRS is probably the wrong pattern.

Check yourself
interview

A system has 99% reads, complex full-text search queries, and writes that need transactional integrity. Which pattern is most appropriate?

Pick one answer.

Check yourself
interview

After a user updates their profile in a CQRS system, they refresh the page and see the old profile. What is the most likely cause?

Pick one answer.

Engineering mental model

Mental model. Think of CQRS as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does CQRS mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”

Design lens

Before choosing CQRS, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.

Original NO CAP systems visual for CQRS.
Image unavailable. Original NO CAP systems visual for CQRS.
CQRS: a compact system-thinking visual.— Original NO CAP visual.
// Pseudocode
request = receive()
result = cqrs(request)
return result

// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?
A minimal engineering sketch for reasoning about CQRS.

Back-of-the-envelope reasoning

Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.

Interactive sandboxdeterministic

Interactive thought experiment: CQRS

Change the variables below and predict what breaks first in CQRS. The production lab can later reuse these same inputs.

System pressure6%
Try this

Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.

Hint

If you are stuck on CQRS, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.

Check yourself
solid

You increase traffic by 10× in a system using CQRS. What should you inspect first?

Pick one answer.

Check yourself
interview

Which statement is the safest engineering habit when using CQRS?

Pick one answer.

Try this
interview

You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.

Production scenario: your system uses CQRS, traffic suddenly spikes, and p99 latency doubles. What is your first move?

Interview drill

Answer this without notes: When would you choose CQRS, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.

Engineering lens

A useful engineering lens for CQRS: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.

Numerical sanity check

Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.

Check yourself
interview

Do not optimize for a memorized definition. Reason from the workload and failure mode.

Imagine the simplest version of a system using CQRS. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?

Pick one answer.

Trade-offs

What you gain, what you pay

Pros
  • +Reads and writes can be independently optimized — different stores, different schemas, different scaling.
  • +Read models can be shaped exactly for their query (denormalized, indexed, cached) without affecting writes.
  • +Independent scaling — add read replicas without scaling the write store.
  • +Enables event sourcing and materialized-view patterns naturally.
  • +Read model failures don't break writes (and vice versa) — better fault isolation.
Cons
  • −Operational complexity — now you operate two (or more) stores and a sync pipeline.
  • −Eventually consistent reads — not suitable when reads must be strongly consistent.
  • −Read-your-writes requires extra work (sticky routing, version waits).
  • −Higher latency for the rare synchronous read-your-writes case.
  • −More moving parts to test, debug, and monitor — bugs in the sync pipeline are subtle.
Failure modes

How this breaks in production

  • Sync pipeline lag grows under load — read model falls further and further behind writes.
  • Sync pipeline drops events — read model permanently diverges from write model (requires rebuild).
  • Read model becomes a SPOF — if Elasticsearch goes down, no reads, even though writes still work.
  • Schema evolution breaks projections — a new write field requires updating all read model projections.
  • Replay storms — rebuilding a projection from the event log takes hours and stresses the write store.
  • Inconsistent ordering — events delivered out of order cause incorrect projections (need sequencing).
Common mistakes

Don't fall into these traps

  • •Applying CQRS everywhere by default — most domains are simple CRUD and don't need it.
  • •Forgetting to handle read-your-writes — users see stale data after their own writes.
  • •Not monitoring sync lag — silent staleness causes user-visible bugs.
  • •Using different technologies for write and read without team expertise in both.
  • •Not having a replay strategy — when the read model drifts, you need to rebuild it from the event log.
  • •Assuming CQRS == Event Sourcing — they're independent; CQRS can use a relational write store without events.
Where you see it

Real systems using this

E-commerce product catalogs — relational store for inventory writes, Elasticsearch for search reads.Activity feeds (Twitter, Facebook) — writes to a normalized store, reads from precomputed fan-out feeds.Booking systems (airlines, hotels) — transactional writes, search/analytics reads on different stores.Financial systems — append-only event log for writes, multiple read projections for reporting.Multi-tenant SaaS — per-tenant read models optimized for each tenant's query patterns.
Teardowns

How real systems implement this

  • Amazon DynamoDB + Elasticsearch (common AWS pattern) — Many AWS-based e-commerce systems use DynamoDB for transactional writes and stream changes via DynamoDB Streams into Elasticsearch for full-text search reads — a textbook CQRS implementation across two stores.
  • Facebook / Instagram activity feeds — Writes go to a normalized store; reads come from precomputed, denormalized fan-out feeds in Redis or specialized stores — each feed is a different read-model projection optimized for one user's timeline query.
  • Axon Framework (Java) — Axon is a Java framework that implements CQRS + Event Sourcing explicitly: commands update an event-sourced aggregate, events are published to a bus, and query-side projections subscribe to build read models — a textbook reference implementation.
Interview prompts

Practice saying it out loud

  • Q1What is CQRS, and when would you use it? When would you NOT use it?
  • Q2How does CQRS relate to Event Sourcing? Are they the same pattern?
  • Q3After a user writes data, they read it back and see the old version. What's happening, and how do you fix it?
  • Q4How would you design a system where 99% of traffic is complex full-text search and 1% is transactional writes?
  • Q5What are the operational costs of CQRS, and how do you decide they're worth paying?
Research

Further reading & references

System Design Primer
Open source
ByteByteGo — Scale from zero to millions
ByteByteGo
System Design Tutorial
GeeksforGeeks
System Design Roadmap
roadmap.sh
Design Patterns reference
Reference
Design Patterns reference
Reference
Design Patterns reference
Reference

Core explanations are original NO CAP material. External references are provided for deeper study and standards.

What next?

Mark as understood once the mental model clicks.

Next recommended

Event Sourcing