Event Sourcing
Event sourcing stores state as an ordered append-only sequence of domain events, deriving current state by replay and enabling audit, replay, and projections.
Definition
Event sourcing persists every state change as an immutable domain event in an append-only log. The current state is a fold over the events for an aggregate. Instead of updating a row in place, the system appends what happened, such as OrderPlaced or PaymentCaptured. Projections build read models from the event stream, and the log provides a complete audit history.
Context and scope
Event sourcing is used in systems that need an auditable history, temporal queries, replayable projections, or multiple read models from the same write model. It is common in financial, order, and collaboration domains.
Common misconceptions
- Event sourcing means storing everything forever — retention, compaction, and snapshots are explicit design choices
- The event log is the only database — projections, snapshots, and indexes are still needed for efficient reads
- Events can be changed freely — events are facts; corrections should be new compensating events, not edits
Anti-patterns
- Using event sourcing for simple CRUD without an audit or replay requirement
- Publishing events before the append transaction commits
- Building read models without tracking the last processed event position
Related concepts
Related examples
Authoritative source
State is a fold over events
In a traditional model, an order row is updated to paid. In an event-sourced model, the system appends OrderPlaced and PaymentCaptured. Current state is the result of applying those events in order. The append is atomic for the aggregate, and the events are immutable facts.
CREATE TABLE events (
aggregate_id text NOT NULL,
version bigint NOT NULL,
event_type text NOT NULL,
payload jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (aggregate_id, version)
);
The (aggregate_id, version) primary key enforces optimistic concurrency: two writers appending at the same expected version conflict, and one must retry. Load an aggregate by reading its events from the last snapshot and folding forward. Snapshots bound replay time; they are an optimization, not the source of truth.
Reads need projections
Folding every event for every query does not scale. Projections consume the event stream and write denormalized read models. Each projection tracks its position so it can resume after a restart. A projection may be rebuilt from the log, which is valuable when a read model is wrong or a new view is needed.
The event stream and the projection are eventually consistent. A user who writes and immediately reads may see a stale projection. The UI must either read from the write model for consistency-critical actions or communicate the delay. Do not pretend the projection is synchronous.
Events are contracts
Events are consumed by projections, other services, and analytics. Renaming a field or changing its meaning breaks consumers. Version the event schema, keep additive changes compatible, and use upcasters to translate old events at read time. Never rewrite historical events in place; append a correction event instead.
When to use it
Use event sourcing when audit history, replay, or multiple read models are core requirements and the team can operate projections and schema evolution. The advantage is a durable, queryable history; the drawback is complexity: eventual consistency, storage growth, snapshots, and event versioning. Compared to CRUD plus an audit table, event sourcing makes history first-class but requires a different mental model for every write and read.