CQRS

Command Query Responsibility Segregation — separate the model that mutates state from the model that reads it, often with separate datastores. Writes go through a domain model with invariants; reads come from a denormalised projection shaped to fit the screen.

When to reach for it

  • Your read and write workloads have wildly different shapes and your indexes are at war with each other
  • The natural shape of the write model — rich aggregates, invariants — produces terrible read queries with seven joins
  • You need multiple read shapes (customer view, admin view, search index) and projecting once per view is cheaper than denormalising on the fly
  • You're already on an event-driven backbone, so projections come almost for free
  • Audit and replayability matter — you want to rebuild a read model from history when you discover a bug

What it actually costs

Eventual consistency becomes a UX problem — a user creates an order, navigates to the list, doesn't see it. You need projection workers, lag monitoring, replay tooling and an answer for 'why is the data different in these two places'. The team must internalise that there are two models for the same domain and contributing to both is the norm. CQRS without events still works, but most teams pair it with event sourcing, doubling the conceptual load.

The failure mode nobody mentions

Projection drift. A bug in the projection handler silently miscounts something for a week, the read and write models disagree, and you find out because finance reconciliation fails — not because anything blew up. By then the bad data is in three downstream systems that fed off the projection, and 'just replay from events' takes a weekend because the projection logic has changed five times since.

When not to use it

Your read and write models look the same and a single relational schema with sensible indexes serves both — CQRS gives you two of everything to solve a problem you don't have.