Free — no signup required

The Mechanics of Event Sourcing

2 min read

Events are Immutable Facts

The most important property of an event is that it represents something that has already happened. It is a fact about the past and cannot be changed or deleted.

This distinction between a Command and an Event is fundamental:

  • Command: ChangeEmail("[email protected]") — A request to do something. It can be rejected (e.g., "email already in use").
  • Event: EmailChanged { newEmail: "[email protected]", timestamp: ... } — A fact that something happened. It cannot be rejected because it already occurred.

Commands are written in the imperative tense. Events are written in the past tense. This naming convention is not cosmetic — it reflects a real semantic difference in your domain model.

Rehydrating State from Events

To process a new command (e.g., "Withdraw $50"), the system needs the current account balance. In Event Sourcing, there is no stored balance — you derive it by replaying history. This process is called rehydration or reconstitution.

  1. Fetch: Load all past events for this Account ID from the Event Store.
  2. Replay: Apply each event in order using a pure "apply" function — no side effects, just state transitions.
    Start: Balance = 0 AccountCreated: Balance = 0 MoneyDeposited(100): Balance = 100 MoneyWithdrawn(20): Balance = 80 MoneyDeposited(50): Balance = 130 Current Balance: 130
  3. Validate: Is 130 >= 50 (the requested withdrawal)? Yes — command is valid.
  4. Append: Write a new MoneyWithdrawn { amount: 50 } event to the store.

The apply function must be deterministic and side-effect-free. Given the same sequence of events, it must always produce the same state. This is what makes Event Sourcing testable and predictable.

Snapshots: Solving the Performance Problem

If an account has 1 million events, replaying all of them on every command is prohibitively slow. Snapshots solve this.

Every N events (e.g., every 100), the system saves a snapshot: a serialized copy of the current state at that point, tagged with the event sequence number. On the next rehydration:

  1. Load the most recent snapshot (e.g., at event #999,900).
  2. Load only the events that occurred after that snapshot (events #999,901 to #1,000,000).
  3. Apply only those 100 events on top of the snapshot state.

Snapshots are an optimization, not a replacement for the event log. The event log remains the source of truth; snapshots are a cache of derived state.

This is one of 18 chapters

Every chapter is free — Kubernetes, Terraform, SRE, distributed systems, and more — with fast daily review built in.

Sign up free