Add Snapshots to an Event-Sourced Aggregate
Persist periodic snapshots so rebuilding an aggregate does not replay thousands of events, and version snapshots so a schema change can invalidate them safely.
Task
Introduce snapshots to keep aggregate load time bounded as its event stream grows.
When to use this
An aggregate with a long history takes seconds to load because every event is replayed from the beginning on each command.
Prerequisites
- An append-only event store
- A snapshot store keyed by aggregate id
Steps
1. Write a snapshot every N events, storing the serialized state and the last event position.
Expected output: Loads start from the most recent snapshot.
2. On load, read the latest snapshot then replay only events after its position.
Expected output: Load time depends on events since the snapshot, not total history.
3. Version the snapshot schema and discard snapshots whose version is outdated.
Expected output: A format change does not corrupt state.
4. Keep snapshots optional: the aggregate can always be rebuilt from events alone.
psql -c "SELECT position FROM snapshots WHERE aggregate_id=$1 ORDER BY position DESC LIMIT 1;"Expected output: Snapshots are a performance cache, not a source of truth.
Troubleshooting
Problem
Snapshot format changes
Solution
Embed a version in the snapshot and fall back to full replay for mismatched versions.
Problem
Snapshot written before events are durable
Solution
Write the snapshot only after the events it summarizes are committed, so it never gets ahead of the log.
Limitations
- Snapshots add write amplification and storage; they only pay off for long-lived aggregates.
- An incorrect snapshot silently poisons the aggregate until a full rebuild corrects it.
Context
Introduce snapshots to keep aggregate load time bounded as its event stream grows. An aggregate with a long history takes seconds to load because every event is replayed from the beginning on each command.
Steps
- Write a snapshot every N events, storing the serialized state and the last event position. Expected: Loads start from the most recent snapshot.
- On load, read the latest snapshot then replay only events after its position. Expected: Load time depends on events since the snapshot, not total history.
- Version the snapshot schema and discard snapshots whose version is outdated. Expected: A format change does not corrupt state.
- Keep snapshots optional: the aggregate can always be rebuilt from events alone. Expected: Snapshots are a performance cache, not a source of truth.
SELECT state, position FROM snapshots
WHERE aggregate_id = $1
ORDER BY position DESC
LIMIT 1;
Verify
- Loads start from the most recent snapshot.
- Load time depends on events since the snapshot, not total history.
- A format change does not corrupt state.
- Snapshots are a performance cache, not a source of truth.
Limits
- Snapshots add write amplification and storage; they only pay off for long-lived aggregates.
- An incorrect snapshot silently poisons the aggregate until a full rebuild corrects it.