Skip to content

Event-Sourced Projection

Log-derived read model — instantiates Access-Optimized Redundant Representation

Builds a read-optimized view by folding an append-only log of events, so the same history can be replayed to produce many views — or rebuild any of them from scratch.

An Event-Sourced Projection takes an append-only log of events as the source of truth and derives a read-optimized view by folding that log forward, event by event. Its defining move is that the authority is not a mutable table but the ordered history itself: current state is a computed consequence of every event that ever happened, not a row someone overwrote. Because the log is retained and the fold is deterministic, any number of differently-shaped views can be built from the same events, and any view can be thrown away and rebuilt by replaying from the beginning. The projection carries a marker of how far through the log it has read, which is both its freshness signal and its restart point.

Example

A bank stores account activity as an immutable stream — Deposited, Withdrawn, TransferReceived — rather than as a mutable balance column. The "account summary" screen needs a balance and the last ten transactions fast, so a projector folds the event stream into a small summary row per account: each event adjusts the running balance and prepends to the recent list. The events are the truth; the balance is derived and disposable. When product later wants a separate "available vs. pending funds" view, no migration is needed — replay the same log through a new fold. When a bug in the old fold is found, fix it and replay to regenerate a correct summary. Each projection records the event offset it has consumed (say, event #4,120,338), so readers can see how current it is and a restart resumes exactly there.

How it works

  • The log is the authority. Events are appended in order and never mutated; the projection is strictly subordinate to them.
  • Derive by folding. A projector applies events left-to-right into the read shape — a deterministic reduction of history into current state.
  • Checkpoint the position. The projection tracks the offset of the last event it applied, so it can report its lag and resume precisely after a restart or rebuild.

Tuning parameters

  • Projection granularity — one rich view vs. many focused ones. More views serve more access paths but multiply the folds to maintain.
  • Snapshot interval — how often to persist a folded snapshot so replay can start mid-log instead of at zero. Frequent snapshots cut rebuild time but cost storage.
  • Checkpoint frequency — how often the consumed offset is durably recorded; rarer checkpoints are faster but replay more on restart.
  • Ordering / partition key — what defines "in order" (per account, per aggregate), which bounds how much can be projected in parallel.

When it helps, and when it misleads

Its strength is optionality and repairability: from one honest history you can spin up many tuned views, audit exactly how a value arose, and rebuild any view by replay when a fold changes. It misleads when the log grows large and replay becomes slow — a cold rebuild can take hours without periodic snapshots to start from.[1] It also misleads anyone who reads a projection as if it were synchronous with the write: projections lag the log, sometimes visibly. The discipline is to expose each projection's offset and lag rather than implying it is live, and to snapshot early so "rebuild by replay" stays a minutes-not-hours operation.

How it implements the components

Event-Sourced Projection realizes the authority-and-derivation side of the archetype in its purest form — history as source, view as fold:

  • source_of_truth_reference — the append-only event log is the sole authority; the projection is an explicitly subordinate, disposable derivative.
  • derivation_and_duplication_mapping — the fold is the derivation: a deterministic mapping from the ordered events to the read shape, reproducible on demand.
  • version_token — the consumed-event offset the projection checkpoints, serving as both its freshness marker and its exact restart point.

It does not define the query-shaped read contract that a consumer binds to (that's CQRS Read-Model Projection), capture cross-system lineage as a first-class artifact (Data Lineage Capture does), or run the bulk replay/rebuild as an operational job (that's Backfill and Rebuild Job).

Notes

An append-only log resists hard deletion, which collides with erasure duties (the privacy_and_deletion_propagation_rule this projection does not itself implement): you usually cannot simply delete a person's events. The standard answer is crypto-shredding — encrypt per-subject data and destroy the key — but it has to be designed in from the first event, not retrofitted once a deletion request arrives.

References

[1] Snapshotting — periodically persisting the folded state so replay can resume from a recent point instead of the start of the log. It is the standard bound on replay cost in event-sourced systems; without it, rebuild time grows without limit as history accumulates.