Skip to main content
Articles/architecture/event-sourcing-pattern-auditability-replay-and-versioning
Series: Event-Driven Architecturesβ€’Part 3 of 3
Scenario Article

Event Sourcing Pattern: Auditability, Replay, and Evolution of Domain State

Why Storing Events Instead of State Changes Everything

Abstract Algorithmsβ€’Mar 13, 2026
XLinkedInReddit
πŸ’‘ Executive Summary

Event sourcing pays off when regulatory audit history and replay are first-class requirements β€” but it demands strict schema evolution, a snapshot strategy, and a framework that owns aggregate lifecycle.

🎯 System Scenario & Requirements

System Goal

Event sourcing pays off when regulatory audit history and replay are first-class requirements β€” but it demands strict schema evolution, a snapshot strategy, and a framework that owns aggregate lifecycle.

Actors & Components

  • Application or API tier
  • Core service under design
  • Operator or SRE observer

Functional Requirements

  • TLDR: Event sourcing pays off when regulatory audit history and replay are first-class requirements β€” but it demands strict schema evolution, a snapshot strategy, and a framework that owns aggregate lifecycle.
  • Spring Boot + Axon Framework is the fastest production-grade path on the JVM.
  • In 2017, a GitLab database administrator ran rm -rf on the wrong production server.

Constraints & Assumptions

⚑ Execution Stages & Decision Trace

1

Stage 1 β€” The Four Building Blocks of an Event-Sourced System

Action / Decision:

Every production event-sourced system has four roles: 1.

Component Behavior:

Command β€” an intent to change state; validated against current aggregate state before writing.

Observable Output: Aggregate β€” the consistency boundary; enforces invariants, emits events, and advances its internal state machine.
Read Full Stage in Deep Dive ↓
πŸ“–

Deep Dive

Detailed Architectural Analysis

Scenario context

Event sourcing pays off when regulatory audit history and replay are first-class requirements β€” but it demands strict schema evolution, a snapshot strategy, and a framework that owns aggregate lifecycle. Spring Boot + Axon Framework is the fastest production-grade path on the JVM.

In 2017, a GitLab database administrator ran rm -rf on the wrong production server. They had no event log β€” just nightly snapshots. Si…

This walkthrough focuses on Why Storing Events Instead of State Changes Everything.

Before making a design choice, clarify:

  • In 2017, a GitLab database administrator ran rm -rf on the wrong production server.
  • They had no event log β€” just nightly snapshots.
  • Six hours of user data was lost permanently, and thousands of repositories were irrecoverable.

Concepts in play: event-driven-architecture, jvm-internals-and-garbage-collection, consistency-replication, design-principles-solid

Stage 1 β€” The Four Building Blocks of an Event-Sourced System

  • Every production event-sourced system has four roles: 1.
  • Command β€” an intent to change state; validated against current aggregate state before writing.
  • Aggregate β€” the consistency boundary; enforces invariants, emits events, and advances its internal state machine.
  • Event Store β€” the append-only log; events are immutable, each aggregate instance owns a stream by ID.

Observable outcome: Event Store β€” the append-only log; events are immutable, each aggregate instance owns a stream by ID.

Stage 2 β€” How a Command Flows into an Auditable Event Stream

  • Solid arrows show the live command path.
  • Dashed arrows show replay β€” the TrackingEventProcessor resets its token to reconstruct the query model for audit at any historical timestamp.
  • The aggregate never writes directly to the query model.
  • It emits events; projections consume them independently.
flowchart TD
    C[Client Command] --> CH["Command Handler (SubscriptionAggregate)"]
    CH -->|"validates invariants applies event"| ES[(Event Store Append-Only Log)]
    ES -->|"event published on event bus"| P["BillingHistoryProjection (Event Handler)"]
    P --> QM[(Query Model BillingHistoryRepository)]
    QM -->|"query response"| Q[GetBillingHistoryQuery]

    ES -. "token-based replay" .-> RP["Replay Processor (TrackingEventProcessor)"]
    RP -. "rebuilds view for audit dispute" .-> QM

    style ES fill:#f5f5f5,stroke:#555
    style QM fill:#e8f4e8,stroke:#555
    style RP fill:#fff3e0,stroke:#f90,stroke-dasharray: 5 5

Observable outcome: It emits events; projections consume them independently.

Stage 3 β€” Deep Dive: Inside the Aggregate: State Machines, Snapshots, and Schema Evolution

  • Internals: Aggregate State Reconstruction An aggregate's state exists only in memory during command processing.
  • Before handling a command, the framework loads the aggregate by replaying every past event for that aggregate ID in sequence.
  • Each @EventSourcingHandler method advances internal state β€” status flags, counters, IDs β€” until the aggregate is fully current.
  • The command handler then checks invariants against that reconstructed in-memory state.

Observable outcome: The command handler then checks invariants against that reconstructed in-memory state.

Stage 4 β€” Axon Framework and EventStoreDB: Event Sourcing on the JVM

  • Axon Framework is a Spring Boot-native Java framework that manages the full event-sourcing lifecycle: aggregate command handling, event persistence, snapshotting, replay, upcasting, and projection tracking.
  • EventStoreDB is a purpose-built append-only database with server-side projections and persistent subscription support β€” the recommended backend for production Axon deployments requiring audit-grade storage.
  • These tools solve the event-sourcing problem by owning the infrastructure that makes aggregates deterministic: Axon's @CommandHandler / @EventSourcingHandler pattern enforces the strict separation between command valida…
  • Teams write domain logic; Axon owns the replay machinery.
<dependency>
  <groupId>org.axonframework</groupId>
  <artifactId>axon-spring-boot-starter</artifactId>
  <version>4.9.3</version>
</dependency>
<!-- Optional: EventStoreDB connector replaces the default JPA event store -->
<dependency>
  <groupId>org.axonframework.extensions.eventstored</groupId>
  <artifactId>axon-eventstoredb-spring-boot-starter</artifactId>
  <version>0.1.0</version>
</dependency>

Observable outcome: Teams write domain logic; Axon owns the replay machinery.

Stage 5 β€” Real-World Applications

  • Event sourcing earns its complexity where audit trails and replay are first-class business requirements.

Observable outcome: Event sourcing earns its complexity where audit trails and replay are first-class business requirements.

Stage 6 β€” Subscription Billing: Building the Aggregate, Projection, and Replay

  • Scenario: A billing platform tracks the lifecycle of each subscription β€” CREATED β†’ ACTIVATED β†’ SUSPENDED β†’ CANCELLED .
  • Every state transition is an immutable domain event appended to the subscription's event stream.
  • When a customer disputes a charge, the support team replays the event stream to reconstruct exactly what the account looked like at the moment of the disputed transaction.
  • Maven Dependency Domain Events (Immutable Value Objects) Each event is a value object with no setters.
<dependency>
  <groupId>org.axonframework</groupId>
  <artifactId>axon-spring-boot-starter</artifactId>
  <version>4.9.3</version>
</dependency>
public record SubscriptionCreatedEvent(
    String subscriptionId, String tenantId, String planId, Instant occurredAt) {}

public record SubscriptionActivatedEvent(
    String subscriptionId, String tenantId, Instant occurredAt) {}

public record SubscriptionSuspendedEvent(
    String subscriptionId, String tenantId, String reason, Instant occurredAt) {}

public record SubscriptionCancelledEvent(
    String subscriptionId, String tenantId, String reason, Instant occurredAt) {}

Observable outcome: Maven Dependency Domain Events (Immutable Value Objects) Each event is a value object with no setters.

End-to-end outcome

In 2017, a GitLab database administrator ran rm -rf on the wrong production server.

Alternatives considered

Decision Guide: When Event Sourcing Earns Its Complexity

Decision Guide: When Event Sourcing Earns Its Complexity

Failure and degraded modes

Trade-offs & Failure Modes in Practice

  • Detection: Trade-offs & Failure Modes in Practice
  • User or operator impact: Trade-offs & Failure Modes in Practice

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit