Architecture style

CQRS and Event Sourcing

CQRS splits the write model from the read model, and Event Sourcing stores not the current state but the full sequence of events. Together they give full history and fast reads at the cost of complexity.

CQRS with Event Sourcing: commands append events, a projector builds the read modelcommandappendeventsqueryClientCommand handlerEvent storeProjectorRead modelQuery handler
CQRS with Event Sourcing: commands append events, a projector builds the read model

CQRS: two models instead of one

Command Query Responsibility Segregation means writes and reads are served by different models. Commands (EnrollInCourse) check business rules and change state. Queries (GetMyDashboard) read from denormalized tables shaped for the screen.

Most products see 10 to 100 times more reads than writes. A separate read model can live in Redis, Elasticsearch or a dedicated replica and scale independently of writes.

Event Sourcing: state as a sum of events

Instead of a balance = 120 row, you store a journal: Deposited 100, Deposited 50, Withdrew 30. Current state is computed by replaying events. The journal is append only, nothing is overwritten.

Banks and accounting worked this way long before programming. You get a full audit trail, can rebuild state as of any date, and can build a new read model by replaying old events through a new projector.

To avoid replaying millions of events per request, you take snapshots: store the aggregate state every N events and replay only the tail.

Why not everywhere

The read model updates asynchronously, so right after a write the user may see stale data. That has to be designed into the UX, not hidden.

Events cannot simply be «fixed» in the database: mistakes are corrected with compensating events, and schema changes need upcasting of old events. For plain CRUD (profile, settings) this is needless complexity.

It fits domains where history has value: money, bookings, inventory, exchanges. LMAX, a trading platform, processed about 6 million orders per second on a single thread by keeping state in memory and rebuilding it from an event journal.

Pros

  • Full change history and audit with no extra tables.
  • Reads and writes scale independently.
  • New read models are built by replaying old events.
  • System state can be rebuilt as of any moment.

Cons

  • Much more code and concepts than CRUD.
  • The read model lags behind writes.
  • Evolving event schemas and fixing mistakes is hard.
  • Few developers have real experience with it.

When to choose it

  • Change history has business value: finance, bookings, inventory.
  • You need a legal audit trail of every change.
  • Reads and writes differ a lot in shape or load.
  • Apply it to one domain, not to the whole system.

Where it is used

  • LMAX: a trading platform with business logic on one thread, state in memory and an event journal (Martin Fowler's write-up, 2011).

Tools

Sources

Logos belong to their owners and are used for identification only. Icons: Simple Icons (CC0), Devicon (MIT).