CQRS logo

Architecture & Methodology

Official site

CQRS

CQRS — splitting command and query models so writes and reads scale, evolve, and stay optimised independently.

Overview

CQRS separates the write model (commands that change state) from the read model (queries that serve views). It is not a silver bullet — it is the right tool when the read and write sides genuinely differ in shape or load.

I apply it most often alongside event sourcing and DDD: commands drive domain aggregates and emit events, while dedicated read models are projected and denormalised for fast, predictable queries. The two sides communicate only through the event/command contracts.

When over-applied it adds machinery; applied to the right seams it removes whole classes of contention — read models tuned for their consumers, writes kept small and transactional, and no query shape ever forcing a domain model compromise.

The fundamental pay-off is that the write and read models evolve independently. The write side stays a disciplined domain model where invariants hold; the read side becomes a projection purpose-built for a screen or an API — no more bending the aggregate to serve a dashboard query.

Commands are the only thing that mutates state. I model them with the verb and the payload, dispatch them through a command bus or handler, and treat each as a unit with its own authorisation and validation — so every write to the system is a named, audited intention.

Queries hit the read model and never touch the domain. That separation lets the read side scale and cache freely, add materialised views when queries get real, and be replaced without disturbing a single command handler.

The bridge between the two sides is the event stream. Aggregates publish events; projections consume them to rebuild read models. That is the exact seam where CQRS meets event sourcing, and it makes the read model always derivable from the write history.

Consistency is eventual and the cost is bounded. I decide explicitly what must be strongly consistent and what is fine to be async — account money is strong, a report is eventual — and I document that decision rather than pretend everything is synchronous.

I apply CQRS where the asymmetry is real: a service with heavy, varied reads and a lean transactional core. Where reads and writes are mirror images, a single model is simpler and I say so. CQRS is a decision backed by a reason, not a default.

How I Work With CQRS

I check for asymmetry before I reach for CQRS. The pattern earns it only when the read and write models genuinely differ in shape or load — otherwise a single model is simpler and I say so.

Commands are the only writers. I model each write as a named intention with its own authorisation and validation, dispatched through a handler so every mutation is an audited, testable unit.

The read model is a projection, not a compromise. Queries hit a denormalised model built for the screen or API they serve, so no query ever bends the domain to its shape.

The event stream is the bridge. Aggregates publish events, projections consume them to rebuild read models, and that seam is where CQRS meets event sourcing cleanly.

Consistency is decided explicitly. I name what must be strongly consistent and what is fine to be eventually consistent — money is strong, a report is eventual — and I document it rather than pretend everything is synchronous.

Every projection is idempotent and rebuildable. Because it derives from the event stream, a projection can be re-run from scratch, which doubles as the simplest disaster recovery of the read side.

I keep the write model lean. With reads moved to projections, the write side stays a pure domain model where invariants hold — no read-shaped hacks leaking in.

Materialised views come where they help. Whether a dashboard needs a roll-up or a search index needs a feed, the projection pattern makes adding a view a contained, testable addition.

I measure the operational cost honestly. Event handling, rebuilds, and eventual consistency are real machinery, and I only carry it where the asymmetry genuinely pays.

Key Capabilities

Independent command and query models, each optimised for its workload.
Pairs naturally with event sourcing via rehydrated projections.
Read models denormalised for consumer-specific view shapes.
Fits DDD aggregates where writes stay transactional and reads stay fast.
Wise default against — applied only where the write/read asymmetry earns it.