CQRS and event sourcing
CQRS (command query responsibility segregation) splits a system’s data model in two: one model handles commands that change data, and a separate model serves queries that read it. Each side can be designed, stored and scaled for its own job.
Event sourcing stores every change to the system as an immutable event (“FundsDeposited”, “AddressChanged”), and derives the current state by replaying those events. The two patterns are independent, but they are often used together: the event log feeds the read models.
Diagram
Section titled “Diagram”flowchart LR
client[Client] -- command --> cmd[Command handler]
cmd --> store[(Event store)]
store -- events --> proj[Projections]
proj --> read1[(Read model: account summary)]
proj --> read2[(Read model: search index)]
client -- query --> read1
client -- query --> read2
How it works
Section titled “How it works”- Commands express intent (“withdraw 50”). The write side validates them against business rules.
- Events record what happened as a result. With event sourcing, they are the system of record and are never updated or deleted.
- Projections read the events and build query-friendly views, each shaped for one screen or use case.
- Read models are disposable. Because they are derived, they can be rebuilt from the event log at any time.
- Optimized reads and writes: each side uses the storage and shape that suits it.
- Complete audit trail: event sourcing records every change, who made it and when.
- Time travel: reconstruct the state of the system at any point in the past.
- New views from old data: add a read model later and populate it by replaying history.
- Significant complexity: two models, projections and an event store are much harder than one table.
- Eventual consistency: read models lag behind writes, which users may notice.
- Event schema evolution: old events stay forever, so every version must remain readable.
- Unfamiliar to most teams: debugging and querying require new skills and tools.
When to use it
Section titled “When to use it”- Domains where the history of changes is itself valuable: finance, accounting, compliance, logistics tracking.
- Very different read and write workloads, such as heavy reporting over a transactional system.
- Collaborative domains where many users change the same data and conflicts must be understood.
When to avoid it
Section titled “When to avoid it”- Simple CRUD applications. This is one of the most frequently over-applied patterns.
- Apply it to the specific parts of a system that need it, not to the whole system by default.
Common pitfalls
Section titled “Common pitfalls”- Using it everywhere. Most modules in a system don’t need it.
- Events that describe data changes, not business facts. “RowUpdated” carries no meaning; “PaymentRefunded” does.
- No snapshot strategy. Replaying millions of events per request becomes slow; periodically store snapshots.
- Treating the event store as a message bus. Internal events and published integration events have different audiences and should be versioned separately.
Related patterns
Section titled “Related patterns”- Event-driven architecture
- Microservices
- Transactional outbox
- Materialized view
Glossary: CQRS, eventual consistency
Further reading
Section titled “Further reading”- Martin Fowler, “CQRS” and “Event Sourcing” (martinfowler.com)
- Greg Young’s writing and talks on CQRS and event sourcing
Written and edited by Abhinav Taduka, enterprise software architect.