CQRS Pattern
Read/write separation.
CQRS in plain terms
Command-Query Responsibility Segregation: separate paths for writes (commands) and reads (queries). Different models, different stores, different scaling.
Writes go through a single canonical model that enforces business rules. Reads come from optimised projections built from the write log.
Sounds elegant; adds significant complexity. Justified when read and write workloads diverge enough that a unified model becomes a bottleneck.
When CQRS pays
Read-heavy workloads with complex queries: e-commerce product catalogue, analytics dashboards. Reads are 100x writes; specialised read models help.
Different consistency requirements. Writes need strict consistency; reads can tolerate eventual consistency for performance.
Audit and event-sourcing requirements. CQRS pairs naturally with event sourcing: commands produce events; events build read projections.
When not
CRUD applications without complex query needs. Adding CQRS to a simple form-and-database app multiplies complexity for marginal benefit.
Small teams or early-stage products. The operational burden of multiple models, sync mechanisms, and eventual consistency is real.
When eventual consistency is unacceptable. Some domains need read-after-write strict consistency; CQRS makes this harder.
Implementation patterns
Single database with separate read replicas: lightweight CQRS. Writes to primary; reads from replicas. Eventual consistency at replication lag.
Separate stores: Postgres for writes, Elasticsearch for reads. Async pipeline syncs. Heavier but more flexible.
Event-sourced CQRS: write log as the canonical store, projections built by replaying events. Most complex, most powerful.
Operating CQRS
Read projection lag is a first-class metric. SLO on lag; alerting when it grows.
Replay capability for projections. When a projection bug ships, replay from the event log to rebuild.
Schema migration for read models is independent from writes. Versioning and migration strategies needed for each.