CQRS
CQRS separates read and write models so each can be optimized independently — write to a normalized database, read from a denormalized projection.
Quick answer: CQRS
CQRS is covered here as a practical system design concept. CQRS separates read and write models so each can be optimized independently — write to a normalized database, read from a denormalized projection. Use it to understand the Architecture Patterns idea, where it appears in production systems, and how to discuss it clearly in interviews.
Focus on the problem being solved, the main design choice, the failure mode, and the tradeoff you would defend in front of a senior engineer. This page is part of the DosiBridge system design knowledge graph, so it links related concepts, interview problems, tradeoffs, and references instead of leaving the topic isolated.
CQRS (Command Query Responsibility Segregation) splits a system into two sides: commands that change state and queries that read state. Each side gets its own data model, optimized for its purpose. The write side uses a normalized model for consistency; the read side uses denormalized projections for fast queries. This pattern shines in systems where read and write workloads have vastly different characteristics — like an e-commerce site where product catalog reads outnumber order writes 1000:1.
| Aspect | Details |
|---|---|
| What it is | A pattern that uses separate models for reading and writing data, allowing each to be optimized independently |
| When to use | Systems with asymmetric read/write ratios, complex queries on write-heavy data, or when read and write scaling needs differ |
| When NOT to use | Simple CRUD apps where reads and writes use the same model — CQRS adds complexity without benefit in these cases |
| Real-world example | Microsoft uses CQRS in Azure DevOps to serve billions of read queries from denormalized projections while writes go to a normalized event store |
| Interview tip | Pair CQRS with event sourcing — interviewers often ask about both together, and explaining eventual consistency between read/write models shows depth |
| Common mistake | Expecting immediate consistency between write and read sides — the read model is asynchronously updated, so queries may return stale data briefly |
| Key tradeoff | Scalability and query performance vs complexity and eventual consistency between the two models |
Why This Matters
In traditional architectures, one model serves both reads and writes. This forces a compromise: normalize for write consistency or denormalize for read performance. CQRS eliminates this tradeoff. You can write to a normalized PostgreSQL database for ACID guarantees while serving reads from a denormalized Elasticsearch index or Redis cache. Each side scales independently. The read model is rebuilt from events, so you can create multiple projections optimized for different query patterns without touching the write side.
The Building Blocks
- Command Model: Handles writes: validates business rules, persists state changes. Optimized for consistency — normalized, ACID-compliant. Rejects invalid mutations.
- Query Model: Handles reads: serves precomputed, denormalized views. Optimized for speed — flat tables, materialized views, search indexes. No business logic.
- Event Bus: Connects write to read side. After a command succeeds, an event is published. Read model projectors consume events and update denormalized views asynchronously.
- Projectors: Event handlers that transform domain events into read model updates. One event can update multiple projections (e.g., order-placed updates inventory view AND revenue dashboard).
- Eventual Consistency: The read model lags behind the write model by the event propagation delay (typically milliseconds). Queries may return slightly stale data.
Under the Hood
When a user places an order, the command handler validates the request (inventory check, payment authorization), writes to the orders table, and publishes an OrderPlaced event. The read-side projector receives the event and updates multiple views: the user's order history (denormalized with product names and images), the admin dashboard (aggregated revenue), and the inventory count (decremented).
The command model uses a normalized relational schema: separate orders, order_items, products, and users tables with foreign keys. The query model uses denormalized documents: a single order_summary collection with all data embedded. This means reads avoid JOIN queries entirely — a single document fetch returns everything the UI needs.
The key architectural decision is the projection mechanism. Synchronous projection (update read model in the same transaction as the write) provides strong consistency but couples the models. Asynchronous projection (via message queue) decouples them and allows independent scaling, but introduces eventual consistency. Most production CQRS systems use asynchronous projection with sub-second lag.
How Companies Actually Do This
Microsoft uses CQRS extensively in Azure DevOps. The write side stores work items in a normalized SQL database, while the read side serves queries from denormalized projections, handling billions of reads per day.
Walmart implemented CQRS for their product catalog. Writes (price updates, inventory changes) go to a normalized database, while the shopping UI reads from Elasticsearch projections for sub-50ms query responses.
Netflix uses a CQRS-like pattern for their content catalog. Metadata writes go to a primary store, while multiple read-optimized projections serve different clients (TV, mobile, web) with different data shapes.
Common Pitfalls
- Applying CQRS to simple CRUD applications where the same model works for both reads and writes — the added complexity is not justified
- Not handling projection failures — if a projector crashes, the read model becomes stale. You need replay capability to rebuild projections from events
- Ignoring the eventual consistency gap — users who create an entity and immediately query for it may not see it if the projection hasn't caught up yet
Interview Questions Worth Practicing
- When would you choose CQRS over a traditional CRUD architecture, and what complexity does it add?
- How do you handle the eventual consistency between write and read models in a CQRS system?
- How would you rebuild a corrupted read model projection from scratch?
The Tradeoffs
- Query Performance vs Complexity: Denormalized read models eliminate JOINs and serve queries in O(1), but maintaining projectors and handling consistency adds significant system complexity.
- Independent Scaling vs Operational Cost: Read and write sides can scale separately (e.g., 10 read replicas, 1 write database), but you now operate two data stores and a message bus.
- Flexibility vs Consistency: Multiple read projections serve different query patterns perfectly, but all lag behind the write model, requiring careful UX design for the consistency gap.
How to Explain This in an Interview
Here is how I would explain CQRS in a system design interview:
CQRS separates the system into command (write) and query (read) sides, each with its own optimized data model. I would use it when read and write patterns are fundamentally different — for example, an e-commerce site where writes are normalized for ACID (orders, inventory) but reads need denormalized documents (product pages with embedded reviews, ratings, pricing). The write side publishes events to a message queue, and read-side projectors build denormalized views. Each side scales independently. The main tradeoff is eventual consistency — after a write, the read model takes milliseconds to catch up. I handle this with optimistic UI updates on the client and always reading from the write model for just-created entities.
Related Topics
Original DosiBridge notes for CQRS
This section is written specifically for this page, not copied from a general system-design checklist. The useful way to study CQRS is to connect the concept to a production service where growth exposes the first weak assumption in the original design. That framing changes the design discussion from definitions to operating choices.
What makes this topic distinct
- Ownership Boundary: For CQRS, decide the ownership boundary before choosing tools; otherwise the design looks clean but fails when cqrs changes.
- Blast Radius: For CQRS, decide the blast radius before choosing tools; otherwise the design looks clean but fails when cqrs changes.
- Deployment Path: For CQRS, decide the deployment path before choosing tools; otherwise the design looks clean but fails when cqrs changes.
- Runtime Coupling: For CQRS, decide the runtime coupling before choosing tools; otherwise the design looks clean but fails when cqrs changes.
- Operational Handoff: For CQRS, decide the operational handoff before choosing tools; otherwise the design looks clean but fails when cqrs changes.
Production review question
If a team adopts CQRS, what observable signal proves it helped instead of only making the architecture diagram more fashionable?
Practical metric to track
Track cqrs health as a named dashboard or review item: combine one user-facing metric, one saturation metric, and one correctness metric. For CQRS, this keeps the discussion grounded in behavior that can be measured after deployment instead of generic advice that sounds good in an interview but never gets verified.