Event Sourcing and Durable History

Day 017: Event Sourcing and Durable History

Event sourcing treats the story of the system as durable truth, and the current state as something you can always rebuild from that story.


Today's "Aha!" Moment

Imagine a support engineer looking at an order record that currently says status = refunded. That answers one narrow question: what is true now? But it does not answer the questions operators and product teams actually ask under stress. Was the order paid before it was cancelled? Was it shipped and then refunded, or refunded before fulfillment? Did the refund happen because of a duplicate charge, a customer complaint, or an inventory failure?

This is the intuition behind event sourcing. A mutable row captures the latest shape of the world. An event history captures how the world got there. Instead of treating history as an optional audit trail glued onto the side, event sourcing makes the ordered sequence of domain facts the primary durable record.

That shift matters because many business problems are really history problems. Disputes, audits, debugging, replay after a bug, rebuilding read models, or asking "what did we believe at 10:03?" all depend on preserving the sequence of meaningful changes. In an event-sourced system, the present is not the only thing worth storing. The path to the present is first-class.

Signals that event sourcing is the real topic:

The common mistake is to confuse event sourcing with generic logs. Log lines help humans inspect a system. Domain events are durable business facts the system itself can replay and build on.


Why This Matters

State-only models are compact and often sufficient, but they throw away context unless you bolt it back on with extra audit tables, ad hoc logs, or fragile reconstruction scripts. That usually works until the day you need to explain a dispute, rebuild a projection, correct a bug retroactively, or answer a temporal question precisely.

Event sourcing matters because it gives those questions a native place in the design. If the system preserves OrderPlaced, PaymentCaptured, OrderShipped, and RefundIssued, it can rebuild current state, derive specialized views, and explain why the state looks the way it does. The same durable history can feed customer views, finance reports, search indexes, fraud analysis, and recovery tooling.

That does not make event sourcing universally correct. It adds design discipline and operational cost. But when history itself is part of the product's truth, storing only the latest snapshot is often the more awkward model, not the simpler one.


Learning Objectives

By the end of this session, you will be able to:

  1. Explain the core inversion in event sourcing - Describe why an ordered history of domain facts can become the primary source of truth.
  2. Distinguish event history from projections - Explain why current-state views are derived models, not the durable truth itself.
  3. Reason about the operational cost - Recognize when replay, snapshots, versioning, and event-contract discipline are worth the added complexity.

Core Concepts Explained

Concept 1: The Durable Truth Becomes the Sequence of Domain Facts

Use a simple order lifecycle:

OrderPlaced
PaymentCaptured
OrderPacked
OrderShipped
RefundIssued

In a traditional CRUD model, you might only end up with:

order_id = 8472
status   = refunded

That is useful, but it hides the path. Event sourcing changes the authority model. The system persists the facts that happened in order, and current state is reconstructed by applying them.

def apply(state, event):
    if event["type"] == "OrderPlaced":
        state["status"] = "placed"
    elif event["type"] == "PaymentCaptured":
        state["paid"] = True
    elif event["type"] == "OrderPacked":
        state["status"] = "packed"
    elif event["type"] == "OrderShipped":
        state["status"] = "shipped"
    elif event["type"] == "RefundIssued":
        state["status"] = "refunded"
    return state

That replay loop is the conceptual heart of the pattern. You are not trusting one mutable snapshot as the only truth. You are trusting an append-only history of meaningful business facts.

This gives you something a current-state row cannot: explanation. You know not only what the order is now, but why it is there. That is what makes temporal debugging, audits, and correction workflows much more natural.

The trade-off is straightforward. You gain reconstructability, traceable intent, and richer downstream uses of history. You pay by making history durable and therefore harder to treat casually.

Concept 2: Projections Turn One History into Many Practical Views

If every read had to replay every event from the beginning, event sourcing would be elegant but painful. Projections solve that. They materialize specialized views from the same underlying history.

For the order system, one event stream can feed several projections:

event stream
    -> current order status view
    -> customer order timeline
    -> finance settlement report
    -> search index
    -> fraud or anomaly view

An ASCII sketch makes the separation clearer:

commands
   |
   v
[ event store ] ---> [ order status projection ]
        |            [ customer timeline view ]
        |            [ finance reporting view ]
        +--------->  [ search / analytics views ]

The important idea is that the event store is the durable history, while each projection is a disposable, rebuildable view optimized for a particular question. That is why event sourcing often pairs naturally with multiple read models: one truth, many useful interpretations.

This also explains a common failure mode. Teams sometimes start treating a projection as if it were authoritative because it is the easiest thing to query. That breaks the whole model. A projection is allowed to lag, be rebuilt, or be replaced. The event history is the thing that must remain durable and coherent.

The trade-off is attractive when one domain history needs several downstream uses. You gain flexibility and replayability. You pay by managing lag, rebuilds, and the operational reality that not every view updates at the same time.

Concept 3: The Hard Part Is Operational Discipline, Not the Replay Loop

The replay loop is easy to understand. Running the pattern well in production is harder.

A real event-sourced system has to answer questions like:

This is why event sourcing is as much a discipline as a data model. Event names and payloads are durable contracts. If you emit vague technical events like RowUpdated, you lose most of the benefit. If you emit specific domain facts like PaymentCaptured or ShippingLabelPurchased, you preserve business meaning that future consumers can still understand.

The pattern also makes mistakes durable. A badly designed event is not just one awkward field in one table. It is part of a history that projections, audits, and future consumers may depend on.

That is why production systems usually add supporting mechanisms:

The trade-off is that event sourcing can be extremely powerful where history matters, but it demands long-term care. The team is choosing to preserve a business narrative, and narratives are harder to change than rows.


Troubleshooting

Issue: "Events are just log messages we happen to keep longer."
Why it happens / is confusing: Both logs and domain events are append-like records of something that happened.
Clarification / Fix: Logs are primarily for observation. Domain events are part of the system's durable business model and must be meaningful enough to replay and build projections from.

Issue: "We can event-source everything because history is always useful."
Why it happens / is confusing: The pattern is powerful, so it is tempting to apply it uniformly.
Clarification / Fix: Use it where history, replay, auditability, or multiple derived views justify the added operational discipline. Small CRUD state often does not need it.

Issue: "The event store is enough; projections are optional polish."
Why it happens / is confusing: The conceptual elegance of the event history can make read-side work look secondary.
Clarification / Fix: Real systems need practical views. Projections, snapshots, and rebuild workflows are part of making the pattern usable, not optional extras.


Advanced Connections

Connection 1: Accounting Ledgers <-> Event-Sourced Aggregates

The parallel: Both prefer append-only records of what happened over destructive mutation of one current total.

Real-world case: A balance is usually more trustworthy when it can be explained from a ledger of durable transactions than when it exists only as one mutable field.

Connection 2: Event Sourcing <-> Git History

The parallel: Both preserve an ordered sequence of meaningful changes and let different views be reconstructed from that history.

Real-world case: Git commits, diffs, and derived working trees illustrate the same deeper pattern: preserve change history durably, then materialize the view you need.


Resources

Optional Deepening Resources


Key Insights

  1. Event sourcing changes what counts as truth - The durable source of truth becomes the ordered history of domain facts, not only the latest snapshot.
  2. Views are derived, not authoritative - Projections make the history practical, but they remain rebuildable interpretations of the event stream.
  3. The real cost is long-term discipline - Snapshots, versioning, rebuilds, and meaningful event contracts are what make the pattern succeed or fail in production.

Knowledge Check (Test Questions)

  1. What changes most when a system adopts event sourcing?

    • A) It stops storing current state anywhere.
    • B) It treats the ordered history of domain events as the durable truth and derives current state from it.
    • C) It removes the need for read models entirely.
  2. Why are projections so important in an event-sourced system?

    • A) Because they turn one event history into practical views for specific queries without becoming the primary source of truth.
    • B) Because they replace the event store as the authoritative record.
    • C) Because they make event naming irrelevant.
  3. What is one real production cost of event sourcing?

    • A) Event contracts, replay, and schema evolution must be maintained carefully over time.
    • B) Current state can never be reconstructed.
    • C) Every read must always replay the entire history from the beginning.

Answers

1. B: The pattern inverts the authority model. Durable facts come first, and current state becomes something the system can rebuild from them.

2. A: Projections make event history usable for real reads while preserving the idea that the history, not the projection, is the durable truth.

3. A: Much of the engineering cost lives in snapshots, compatibility, rebuild tooling, and treating events as long-lived business contracts.



← Back to Learning