AlgoMaster Logo

Event Sourcing

Last Updated: January 8, 2026

Ashish

Ashish Pratap Singh

Traditional databases store the current state of data. When you update a customer's address, the old address is gone. The database tells you what is, but not how it got there.

Event sourcing takes a fundamentally different approach. Instead of storing current state, you store a sequence of events that describe everything that happened. The current state is derived by replaying these events. Nothing is ever deleted or updated in place. Every change is recorded as a new event.

In this chapter, you will learn:

  • How event sourcing differs from state-based storage
  • The structure and storage of event streams
  • How to rebuild state from events
  • Snapshots and performance optimization
  • Event schema evolution
  • When event sourcing is the right choice

State-Based vs Event-Sourced

Let us compare the two approaches with a concrete example: a shopping cart.

State-Based Approach

The database stores the current cart contents. When the user adds an item, you update the row. When they remove an item, you update the row. History is lost.

Event-Sourced Approach

Every action is stored as an immutable event. Current state is derived by replaying all events.

Key Differences

AspectState-BasedEvent-Sourced
StorageCurrent state onlySequence of events
UpdatesOverwrite previous stateAppend new events
HistoryLost unless explicitly loggedPreserved automatically
AuditRequires separate audit tableBuilt-in audit trail
StateStored directlyDerived from events
Schema changesMigrate existing dataEvents are immutable

Event Stream Structure

What is an Event?

An event is an immutable record of something that happened.

Event Structure:

Event Streams

Events are grouped into streams, typically one per aggregate or entity:

Storage Technologies

TechnologyCharacteristics
EventStoreDBPurpose-built for event sourcing, projections built-in
Apache KafkaDistributed log, high throughput, retention policies
PostgreSQLJSONB columns, familiar SQL, ACID transactions
DynamoDBManaged, scalable, append-only patterns
Amazon KinesisManaged streaming, integrates with AWS

Rebuilding State from Events

In event sourcing, the database doesn’t store “the order.” It stores everything that happened to the order.

To get the current order, you derive it by replaying those events.

This process is usually called:

  • Rehydration: rebuilding an aggregate’s in-memory state from its event stream
  • Projection: deriving a stateful view from a sequence of events (often used for read models too)

Same idea: events go in, state comes out.

Basic Projection

Think of an aggregate (like Order) as a pure function of its history:

CurrentState = fold(apply, initialState, events)

Where apply(state, event) is your event handler.

Replay Process

When the system needs the current state of order-456, it does something like this:

  1. Load event stream for order-456 from the event store
  2. Initialize state (empty / null)
  3. Apply events in order (by version)
  4. The final state is the current aggregate state

This is why event ordering and versioning matter. You’re rebuilding reality by replaying history.

Practical Example

Snapshots

Replaying events is elegant, but it has a performance cliff.

If an aggregate has thousands of events, rebuilding it on every load becomes expensive.

The Problem

An order with heavy editing history (item changes, address updates, partial refunds) might have 10,000 events.

Loading that order means:

  • read 10,000 events
  • apply 10,000 handlers
  • potentially hundreds of milliseconds

That’s too slow for interactive systems.

Snapshot Solution

A snapshot is a saved checkpoint of the aggregate state at a particular event version.

Loading with Snapshots

To load current state:

  1. Find latest snapshot (e.g., version 200)
  2. Load snapshot as starting state
  3. Load events after version 200
  4. Apply only those events

With 10,000 events, snapshot at 9,900:

  • Load snapshot (1 read)
  • Apply 100 events (not 10,000)
  • Time: ~10ms instead of 500ms

Snapshot Strategies

StrategyWhen to Snapshot
Every N eventsAfter every 100 events
Time-basedEvery hour
On commandWhen explicitly requested
On readIf more than N events since last snapshot

Best practice:

  • Snapshot every 100-500 events
  • Store snapshots separately from events
  • Events remain the source of truth
  • Snapshots are optimization, can be rebuilt

Handling Commands

In event sourcing, commands do not directly modify state. They produce events that are then stored.

Command Processing Flow

Optimistic Concurrency

Because the event store is append-only, concurrency control is usually done with versions.

Each aggregate stream has a version:

  • version 5 means 5 events have been recorded so far
  • appending event 6 requires that you still believe you’re at version 5

Example

  • User A loads order at version 5
  • User B loads order at version 5
  • User A appends an event with expectedVersion=5 → succeeds → stream is now version 6
  • User B tries append with expectedVersion=5 → fails (conflict)

Then user B:

  • reloads events (now version 6)
  • re-applies logic
  • tries again with expectedVersion=6

Why this matters

  • Prevents overwriting changes
  • Forces conflict detection at the boundary where it belongs: the aggregate stream

Event Schema Evolution

Unlike traditional databases where you migrate data, events are immutable. How do you handle schema changes?

The Challenge

Upcasting

You keep old events as-is, but when reading you transform them into the latest shape.

  • No rewriting history
  • Keeps event store immutable
  • Projection code can assume a “latest schema” after upcasting

Event Versioning

Include version in event type.

Copy-Transform

For complex migrations, copy events to new stream with transformation:

Event Sourcing with CQRS

Event sourcing pairs naturally with CQRS:

Write Side: Commands processed by aggregates, events stored in event store.

Read Side: Events projected into multiple read models optimized for queries.

Benefits of the Combination

CapabilityHow It Works
ReplayRebuild read models from scratch by replaying events
New projectionsAdd new read models by projecting historical events
Temporal queries"What was the state at time T?" - replay to that point
DebuggingSee exactly what happened and when

Advantages of Event Sourcing

Event sourcing flips the storage model:

  • Traditional: store the latest state
  • Event sourced: store the sequence of facts that led to the state

Instead of persisting order.status = "shipped", you persist: OrderCreated → ItemAdded → PaymentReceived → OrderShipped

That shift buys you several powerful capabilities.

Complete Audit Trail

Every change is recorded. You can see exactly what happened, when, and often why.

Example: Order 456 history

Temporal Queries

Because you have the full event stream, you can answer questions about past states.

Debugging and Replay

When something goes wrong, event sourcing gives you the exact sequence of steps that produced the outcome.

Rebuild Projections

In event sourcing, read models are typically projections derived from events. The huge advantage is:

If you need a new read model, you can build it by replaying history.

Scenario: You want a new dashboard showing order analytics.

Solution

  1. Create a new projection: order_analytics
  2. Replay historical order events
  3. The projection builds the complete dataset
  4. No backfills from state tables, no risky migrations, no ETL glue

This makes new features safer: you don’t mutate your “source of truth,” you derive from it.

Disadvantages of Event Sourcing

Event sourcing is powerful, but it’s not “free.”

Complexity

You’re no longer saving objects. You’re appending facts and reconstructing state.

Traditional:

Event Sourced:

More concepts to understand: events, aggregates, projections, snapshots.

Eventual Consistency

In most architectures, you write events to the event store and then update read models asynchronously.

So the timeline looks like:

  • append event → projector processes → read model updates → query reflects change

That gap might be milliseconds—or seconds—or longer during incidents.

You must design for “write succeeded, read still stale.”

Event Schema Evolution

Events are meant to be immutable: once recorded, they represent history.

If you don’t plan for this early, schema evolution becomes painful later.

Storage Growth

In a state-based system, you store the latest record. In an event-sourced system, you store the full history.

This is not necessarily a dealbreaker but you still need:

  • retention/archival policies (when allowed)
  • compaction strategies (snapshots)
  • tiered storage (hot vs cold)
  • careful indexing and partitioning

Query Complexity

Events are optimized for appending and replay, not ad-hoc querying.

State-based

Event-sourced

  • You usually cannot ask that question directly from the event stream.
  • You must project events into a queryable read model first.
  • Then query the projection.

Every query pattern needs a projection.

When to Use Event Sourcing

Good Fit

ScenarioWhy Event Sourcing Helps
Audit requirementsComplete history built-in
Complex domainAggregates, business rules, events map to DDD
Temporal queries"What was state at time X?"
Event-driven architectureEvents are the native currency
Debugging/analyticsFull history for investigation
Regulatory complianceImmutable audit trail

Poor Fit

ScenarioWhy Event Sourcing Hurts
Simple CRUDMassive overkill
Ad-hoc queriesNeed projections for every query
Strong consistencyRead models are eventually consistent
Simple domainComplexity not justified
Small teamLearning curve and maintenance overhead

Domain Indicators

Consider event sourcing when:

Avoid event sourcing when:

Real-World Examples

Banking and Finance

Banks have used this pattern (the ledger) for centuries.

E-Commerce Order Management

Every order state change is an event: created, paid, shipped, delivered, returned. Enables: order tracking, analytics, customer service history.

Healthcare

Patient records as events: visits, diagnoses, prescriptions, tests. Enables: complete medical history, temporal queries, compliance.

Summary

Event sourcing stores all changes as immutable events rather than overwriting current state:

  • Core concept: Events are the source of truth; current state is derived by replay
  • Event streams: Events grouped by aggregate, each with version and timestamp
  • Projections: Build read models by processing events
  • Snapshots: Optimize replay by checkpointing state
  • Schema evolution: Upcasting and versioning handle changing event structures
  • Advantages: Complete audit trail, temporal queries, debugging, replay capabilities
  • Disadvantages: Complexity, eventual consistency, storage growth, query limitations
  • Best for: Audit requirements, complex domains, event-driven systems

Event sourcing represents a fundamentally different approach to data persistence. It trades some simplicity for powerful capabilities around history, auditing, and temporal analysis.