Command Query Responsibility Segregation (CQRS)
Before diving in, it helps to know what segregation means: it simply means the act of separating something into distinct groups or parts. In CQRS, what gets separated is responsibility — the system is split so that writing data and reading data are handled by completely independent parts of the codebase.
CQRS is an architectural pattern that divides every operation on your data store into one of two categories:
- Command — an intent to change state (create, update, delete). It returns nothing meaningful, only a success/failure signal.
- Query — a request to read state. It returns data but never mutates anything.
How it works
In a traditional architecture, a single model handles both reads and writes. CQRS breaks that into two explicit pipelines:
Client
│
├──► Command Bus ──► Command Handler ──► Write Model ──► Database (write store)
│
└──► Query Bus ──► Query Handler ──► Read Model ──► Database (read store / projection)
The write model owns all business logic and enforces data consistency. The read model is a denormalised, query-optimised projection — often rebuilt by listening to events emitted by the write side.
Why bother separating them?
In most CRUD applications the read-to-write ratio is heavily skewed toward reads. Forcing both through the same model means you either over-engineer your read paths (carrying full domain complexity) or under-engineer your write paths (optimised for speed at the cost of integrity).
| Concern | Write side | Read side |
|---|---|---|
| Primary goal | Correctness, business rules | Speed, simplicity |
| Data shape | Normalised, transactional | Denormalised, flat projections |
| Scales with | Write throughput | Read throughput |
| Typical tech | Relational DB, transactions | Cache, read replicas, NoSQL |
CQRS and Event Sourcing
CQRS is frequently paired with Event Sourcing, where every command that changes state emits a persistent event (e.g. OrderPlaced, UserRegistered). The read model is then derived by replaying those events rather than querying the write store directly. The two patterns complement each other but are independent — you can use CQRS without Event Sourcing.
When CQRS makes sense
CQRS adds real architectural complexity. It is worth that cost when:
- Read and write loads need to scale independently
- The domain has complex business logic on the write side that would pollute read models
- You are building on top of microservices where services own their own data
- You need multiple read projections of the same data (dashboards, reports, mobile views)
For simple CRUD apps, CQRS is almost always overkill. As Martin Fowler noted, it should be confined to specific bounded contexts of a system, not applied globally.
Trade-offs
- Eventual consistency — the read model may lag behind the write model for a short window after a command is processed.
- Increased complexity — two separate models, two pipelines, and potentially two databases to maintain.
- Harder debugging — a bug may live in the command handler, the event emitter, or the projection builder.
Related notes
- Architectural Styles vs Architectural Patterns
- Repository Pattern
- Microservices Architecture
- Transactions
- ACID Properties