CQRS Pattern
Read/write separation.
CQRS in plain terms
CQRS accepts that read and write paths can diverge: separate models, separate stores, separate scaling. The pattern carries real operational weight, so it pays back only when read and write workloads have genuinely different shapes.
- Command-Query split. Separate write and read paths per domain; different models, different stores, different scaling characteristics.
- Canonical write model. Single write model per domain enforces business rules; reads come from optimised projections built from the write log.
- Real complexity cost. Operational burden per domain; justified only when read and write workloads diverge enough that a unified model becomes a bottleneck.
- Explicit boundary per domain. Named CQRS scope catches scope creep; "let's CQRS the whole app" is a warning sign.
When CQRS pays
CQRS pays when reads dwarf writes or consistency requirements diverge. Be specific about which domain and why; "we might need it later" is not a reason.
- Read-heavy workloads. 100x reads per write per domain; e-commerce product catalogues, analytics dashboards, leaderboards.
- Different consistency requirements. Strict-write, eventual-read split per domain; writes need strict consistency, reads can tolerate eventual.
- Audit and event-sourcing. Event-sourced fit per domain; commands produce events, events build read projections, audit trail comes for free.
- Per-domain latency budget. Read-path SLO justifies projection cost; without a real latency target, projections are premature.
When not
CQRS is wrong for most applications. Be honest about when not to reach for it; the cost is real and the benefit only materialises at specific shapes.
- CRUD applications. No-complex-query case; CQRS on a simple form-and-database app multiplies complexity for marginal benefit.
- Small teams or early-stage. Operational-burden warning per team; multiple models, sync mechanisms, and eventual consistency are real.
- Read-after-write strict consistency. No-eventual-consistency rule per domain; some domains need read-after-write strict consistency that CQRS makes harder.
- Documented rationale per decision. Named alternative per decision catches premature CQRS adoption driven by pattern fashion.
Implementation patterns
Implementation patterns vary widely in weight. Read replicas are the lightweight version; separate stores are the middle path; event-sourced CQRS is the heavyweight option that produces the strongest properties.
- Single database with read replicas. Lightweight CQRS per domain; writes to primary, reads from replicas, eventual consistency at replication lag.
- Separate stores. Postgres-for-writes plus Elasticsearch-for-reads pattern per domain; async pipeline syncs, heavier but more flexible.
- Event-sourced CQRS. Write log as canonical store per domain; projections rebuilt by replaying events. Most complex, most powerful.
- Failure-mode catalogue per implementation. Documented failure modes per implementation support operational reviews and incident response.
Operating CQRS
Operating CQRS is a discipline. Read projection lag becomes a first-class metric, replay capability becomes a recovery tool, schema migration paths diverge between read and write models.
- Read projection lag. First-class lag metric per projection; SLO on lag, alerting when it grows beyond the budget.
- Replay capability. From-event-log rebuild per projection; when a projection bug ships, replay rebuilds the state.
- Independent schema migration. Versioning and migration per model; read models migrate independently from write models.
- Named owner per projection. Responsible team per projection supports operational reviews and incident response.