What is CQRS and what problem does it solve?
CQRS = Command Query Responsibility Segregation. It splits an application into two clearly separated halves:
- Commands — change state. Have business rules and invariants.
- Queries — read state. Are pure, no side effects.
Each half has its own model and code path.
The problem it solves
Traditional CRUD uses ONE model for both. The same Order class is asked to:
- Validate input on
POST(write — needs business rules) - Save to DB (write — needs change tracking)
- Return on
GET(read — should be flat and fast) - Render lists with joins (read — needs denormalized shape)
- Generate reports (read — needs aggregations)
One class can't satisfy all five well. You end up with:
- Bloated DTOs with 30+ fields
- N+1 queries fighting normalized schemas on reads
- Validation logic tangled with rendering logic
- Cannot scale reads without scaling writes
- Cannot cache aggressively without breaking write consistency
CQRS fix
Write side Read side
───────── ─────────
PlaceOrderCommand GetOrderQuery
CancelOrderCommand ListOrdersQuery
ShipOrderCommand OrderDashboardQuery
Validates + applies Pure SQL, optimised
business rules for the UI's shape
Writes normalized DB Reads any DB or
denormalized view
The two sides can:
- Use different DBs (write: Postgres, read: Elastic)
- Scale independently (10 read replicas, 1 write primary)
- Use different shapes (write: aggregate root, read: flat DTO)
- Have different caching strategies (write: never cache, read: aggressive)
Important — CQRS does NOT require two databases
Most production CQRS systems use ONE database with separate read and write code paths. Same physical schema, two different query patterns. That's already 80% of the benefit with none of the eventual-consistency pain.
Concrete benefits
- Single-responsibility handlers — each command/query has one clear purpose
- Optimised read DTOs shaped exactly for the UI
- Independent scaling of reads vs writes
- Easier security — authorize per command, not per controller method
- Easier audit — the named command IS the audit log entry
When CQRS is wrong
- Simple CRUD admin tools (table-of-rows + edit modal)
- Pre-product-market-fit MVPs
- Apps with no real business rules — just store/retrieve JSON
Interview rule of thumb
"Use CQRS when the read side and write side have genuinely different concerns. Skip it when you're just persisting JSON."
In .NET, the standard implementation is MediatR + clearly separated command/query handlers. Pair with pipeline behaviors for logging, validation, transactions, and caching.