Snapshot Pattern
establishedSummary
Periodically capture the full materialized state of an event-sourced aggregate into a snapshot record, so that subsequent reads reconstruct the aggregate from the most recent snapshot plus a short tail of events: rather than replaying the entire event history from the beginning.
Problem
In event-sourced systems, aggregate load time grows proportionally to event history length. Aggregates with millions of events take minutes to load, making them operationally unusable in synchronous request paths. Snapshots bound the aggregate load time to O(events_since_last_snapshot) rather than O(total_events).
Description
Event sourcing stores state as an append-only log of events. Reconstructing current state requires replaying all events from the beginning of the log. For aggregates with long histories (thousands or millions of events), this replay becomes progressively slower: eventually taking seconds to load a single aggregate.
The snapshot pattern solves this by periodically writing a complete state checkpoint. After a snapshot is taken, only events after the snapshot's version need to be replayed: reconstruct(state) = apply_events(snapshot.state, events[snapshot.version + 1:]).
Snapshot frequency is tunable: snapshot every N events (e.g., every 1000), every N hours, or when a performance threshold is crossed. More frequent snapshots reduce replay time but increase write amplification. Less frequent snapshots reduce write overhead but allow replay time to grow between snapshots.
Snapshots coexist with the full event log: they are a read optimization, not a replacement for events. The event log remains the authoritative source; snapshots can always be regenerated by replaying. Old snapshots can be deleted when superseded; the events are retained for audit, time travel, and event projection needs.
Snapshots are commonly stored in the same event store (EventStore, DynamoDB, PostgreSQL) as a special event type, recording aggregate_id, version, state_bytes, and created_at. Loading with a snapshot means fetching the latest snapshot for the aggregate, deserializing its state if one exists, then loading and applying only the events after that snapshot's version; without a snapshot, loading falls back to the initial state plus every event from version 0.
Snapshot trigger policies are typically one of: every N events (snapshot when the current version is a multiple of N), time-based (snapshot every hour for active aggregates), or lazy (snapshot after loading, if replay time exceeded a threshold). Snapshot versioning stores a schema_version alongside the state bytes so deserialization can handle aggregate schema evolution; old snapshots must either remain deserializable under their original schema version or be migrated forward.
Tradeoffs
Bounded aggregate load time regardless of total event history length
Enables long-lived aggregates (accounts, documents) without performance degradation over time
The event log is preserved; snapshots are an optimization layer, not a replacement
Snapshot creation adds a write on top of event appends
Snapshot schema must evolve alongside aggregate schema; deserialization must handle all historical snapshot versions
The snapshot store is a new infrastructure dependency with its own operational requirements
Incorrect snapshot logic produces incorrect state reconstruction, and such bugs are harder to detect than in full-replay mode
When to use
Event-sourced aggregates accumulate more than 1,000 events over their lifetime
Below 1,000 events, full replay is fast enough that snapshots add complexity for minimal benefit
Aggregate load time is approaching user-facing latency budgets
If aggregate loading is a bottleneck in request handling, snapshots are the correct structural fix
Aggregates are long-lived (accounts, documents, orders with years of history)
Short-lived aggregates never accumulate enough events to require snapshotting
When not to use
Event history is bounded by design (e.g., order processed and closed within 24 hours)
Short event histories replay instantly; snapshot overhead is not justified
System is write-once, never read for reconstruction (pure audit logs)
If aggregates are never replayed for state reconstruction, snapshots serve no purpose
Operational Requirements
Monitor snapshot freshness
An aggregate with many events and no recent snapshot is a ticking performance degradation.
Test snapshot round-trips
Serialize, deserialize, and compare against full-replay state.
Implement snapshot migration scripts before deploying aggregate schema changes
Old snapshots must remain deserializable or be migrated forward.
Automate snapshot creation in the aggregate's command handler
Prefer this over an ad-hoc background job for predictable freshness.
Characteristics
Relationships
Complements
Basis
Snapshot pattern is a standard component of event sourcing architectures, documented in Vaughn Vernon's Implementing Domain-Driven Design, EventStore documentation, and Axon Framework documentation
Related Architecture Knowledge
Outbound: this entity affects
Snapshots are a performance optimization for event-sourced aggregates, providing bounded aggregate load time without changing the event sourcing model.
Full relationship →Snapshots bound the number of events that must be replayed to reconstruct aggregate state, reducing the read I/O required to serve aggregate loads compared to full event log replay.
Full relationship →