Event sourcing: store events, not just the current state

Instead of saving only the latest balance, event sourcing saves every deposit and withdrawal. That gives a full history, and some new problems.

A timeline of events building up a state

In a normal database you store the current state: a bank account row says balance = 700. When it changes, the old value is overwritten. Event sourcing stores the sequence of events instead: Deposited 1000, Withdrew 300. The current state is calculated by replaying them.

The event store

An append-only log of events, each with an ID, type, timestamp, data and the entity it belongs to. Events are immutable facts: you never edit or delete them. To correct a mistake, you add a new compensating event.

Rebuilding state

To get an account's balance, load its events in order and apply them: start at 0, add 1000, subtract 300, giving 700. For long histories, use snapshots: save the state every N events and replay only what came after.

Benefits

  • Complete audit trail for free. You know what happened and when, which suits finance, healthcare and security.
  • Time travel: rebuild the state as of any past moment, and debug by replaying real history.
  • New views later: create new read models or reports from old events.
  • Natural fit with CQRS: events feed read models and other services.

The costs

  • More complexity and a different way of thinking.
  • Event schema changes: old events live forever, so you need versioning and upcasting when the format changes.
  • Eventual consistency in read models.
  • Querying is hard directly on events, so you almost always need read models.
  • Privacy: immutable events conflict with "delete my data" requests. Plan for it, for example with encryption keys you can discard.

When to use it

Domains where history truly matters: payments, ledgers, orders, inventory, audit-heavy systems. For a simple blog or CRUD admin, a normal table plus an audit table is enough.

Takeaway: event sourcing makes history the source of truth. Use it where the history has real value, and be ready for versioning and read models.


System Design series, Part 2 (building blocks): topic 13 of 14. Previous: CQRS: separating reads from writes.

Keep reading