Skip to main content

Glossary + mental model

The one sentence

Event sourcing = your database contains facts (events), not the latest state.

You rebuild "current state" by replaying the facts.

The 5 objects you should picture

  1. Command: "Please do X" (intent). Might be rejected.
  2. Event: "X happened" (fact). Must be true forever.
  3. Aggregate: Runs business rules, turns commands into events.
  4. Event stream: Ordered list of events for one aggregate instance.
  5. Projection: A read-optimized view built from events (can be rebuilt).

"Events" are overloaded — don't confuse these

  • Domain event (event sourcing): internal business fact, usually per aggregate stream.
  • Integration event (distributed systems): something you publish to other services.

They can be related, but don't assume they are the same message or shape.

What ES gives you (when done right)

  • Audit trail: You can answer "how did we get here?"
  • Rebuildability: You can delete and rebuild read models safely.
  • Concurrency that matches the model: streams + expected version.
  • Temporal queries: "state at time T" by replaying until T (or snapshots).

What ES costs you (always)

  • More moving parts: projection pipeline + schema evolution.
  • Eventual consistency (if you have separate read models).
  • You must design for idempotency in projections/integration.
  • You must treat events as contracts (versioning/upcasters).

Stream mental model (critical)

For each aggregate instance, you'll have:

  • stream id: bank-account-{accountId}
  • events: AccountOpened, MoneyDeposited, ...
  • stream version: 0..N (count of events, or last revision)

Appending is:

"Store these new events only if the stream is still at version N".

That's optimistic concurrency.

A tiny story (command → event)

Command:

Withdraw $50 from account 123.

Aggregate checks:

  • account exists?
  • not closed?
  • balance - 50 >= 0?

If valid, emit event:

MoneyWithdrawn { amount: 50 }

If invalid, reject the command:

InsufficientFunds