Docs / From Event Sourcing

Migration Guide

Migrate from Event Sourcing to ATOMiK

Event sourcing gives you a complete history. ATOMiK gives you instant current state. This guide shows how to evaluate replacing hot event replay paths with accumulator-model state reconstruction, reduce snapshot pressure, and simplify fit-model undo -- and when to keep your event log.

The fundamental shift

Event sourcing stores every change and replays them to reconstruct state. ATOMiK accumulates changes into a single value and reconstructs state in one operation: current_state = initial_state ⊕ accumulator. In fit workloads, you trade history for a faster hot-state path: accumulator reads instead of replay, fixed-width accumulator state instead of linear hot-state growth, and algebraic undo via self-inverse deltas.

Event Sourcing to ATOMiK Mapping

Some event sourcing concepts have accumulator-model counterparts in ATOMiK.

Event SourcingATOMiKNotes
Event LogAccumulatorEvent sourcing appends every event to an ordered log (linear growth). In the fit model, ATOMiK XOR-accumulates deltas into a fixed-width accumulator value.
Event Replayctx.read()ES replays all events since the last snapshot to reconstruct state: O(n). ATOMiK reconstructs with one accumulator read in the fit model: initial_state XOR accumulator.
Snapshotctx.swap()ES periodically snapshots to bound replay cost. ATOMiK swap() atomically captures current state and resets the accumulator -- no serialization needed.
Compensating EventRe-apply same deltaES undo requires publishing a compensating event with reversal logic. ATOMiK undo is algebraic: XOR is self-inverse, so re-applying the same delta reverses it.
Projection / Read Modelctx.read()ES projects events into read-optimized views (CQRS). ATOMiK read() returns current state directly -- no separate read model needed.
Event Schema EvolutionFixed-size deltaES events require versioning and upcasting as schemas evolve. ATOMiK deltas are fixed-size integers -- no schema to evolve.

Pattern 1: Reduce Hot-Path Event Replay

The most impactful migration candidate is a hot read path where replay latency or snapshot pressure is already measurable.

Before: Event Sourcing

class Account:
    def rebuild_from_events(self, event_store):
        # Load latest snapshot (if any)
        snap = event_store.latest_snapshot(self.id)
        if snap:
            self.state = snap.state
            start = snap.version
        else:
            self.state = 0
            start = 0

        # Replay all events since snapshot
        events = event_store.load(
            self.id, since=start
        )
        for e in events: # O(n)!
            self.apply(e)

        # Maybe snapshot for next time
        if len(events) > 1000:
            event_store.save_snapshot(
                self.id, self.state
            )

After: ATOMiK

from atomik_core import AtomikContext

class Account:
    def __init__(self):
        self.ctx = AtomikContext()
        self.ctx.load(0)

    def apply_change(self, delta):
        self.ctx.accum(delta)

    def current_state(self):
        # Accumulator-model read; no event replay here.
        return self.ctx.read()

    def checkpoint(self):
        # Atomic snapshot + reset
        return self.ctx.swap()

# Event store can remain for audit/history.
# Hot read path avoids replay scheduling.

Pattern 2: Simplify Fit-Model Compensation

In event sourcing, undo requires designing and publishing a compensating event with reversal semantics. In ATOMiK, undo is algebraic when the state change is represented as a fit XOR delta.

Before: Compensating Events

# To undo a transfer, publish a reversal
def cancel_transfer(original_event):
    # Must design reversal logic
    compensation = TransferReversed(
        from_acct=original_event.to_acct,
        to_acct=original_event.from_acct,
        amount=original_event.amount,
        reason="cancellation",
        ref=original_event.id
    )
    event_store.publish(compensation)

    # Log grows. Complexity grows.
    # Every event type needs a reversal.

After: ATOMiK Self-Inverse

# To undo: apply the same delta again
def cancel_transfer(ctx, original_delta):
    ctx.accum(original_delta)
    # For fit XOR deltas, that's the accumulator undo.
    #
    # XOR is self-inverse:
    # state XOR delta XOR delta = state
    #
    # No reversal logic to design.
    # No compensating event schema.
    # No additional storage.
    # Validate domain semantics before replacing events.

# Algebraic property:
# a XOR a = 0 (proven in Lean4)

Trade-off Comparison

Event sourcing and ATOMiK optimize for different things. This table shows where each approach wins.

DimensionEvent SourcingATOMiK
State reconstructionO(n) -- replay from last snapshotO(1) in the accumulator model -- initial XOR accumulator
Storage growthLinear -- every event stored foreverFixed-width accumulator for hot state; audit history must stay elsewhere
Undo / CompensationManual compensating eventsAlgebraic for fit XOR deltas -- self-inverse
Full audit trailYes -- every event preserved with orderingNo -- deltas are accumulated, not stored individually
Temporal queriesYes -- replay to any point in timeNo -- only current state is available
CQRS patternNative -- separate command and query modelsHot read model can be simplified when the state fits the accumulator model
Ordering requirementsStrict -- events must be totally orderedAccumulator deltas commute; business ordering remains workload-specific
Infrastructure complexityHigh -- event store, projectors, snapshot storeSmall accumulator API; integration and audit needs remain workload-specific

When Event Sourcing Is Still Better

ATOMiK is not a universal replacement. These are legitimate reasons to keep your event log.

Regulatory audit trails

Financial services, healthcare, and compliance domains often require a complete, ordered, tamper-evident record of every state change. ATOMiK accumulates deltas into a single value -- individual events are not preserved. If you must answer "what happened at 14:32:07 on March 3rd?", keep your event log.

Temporal queries / time travel

Event sourcing lets you reconstruct state at any historical point by replaying events up to that timestamp. ATOMiK provides only the current state. If your domain requires "show me the account balance as of last Tuesday", event sourcing is the right tool.

Complex domain event choreography

If your system relies on event-driven sagas, process managers, or cross-aggregate reactions where the semantics of individual events matter (OrderPlaced triggers InventoryReserved triggers PaymentCharged), ATOMiK's opaque deltas cannot replace meaningful domain events.

Existing projections that work well

If your CQRS read models are performant and your event store handles representative load without issues, migration cost may exceed benefit. ATOMiK is most valuable when replay latency, snapshot overhead, or storage growth are actual pain points.

The Hybrid Approach

You do not have to choose exclusively. Many teams keep event sourcing for audit-critical aggregates while using ATOMiK for hot-path state where performance matters most.

# Hybrid: event sourcing for audit trail,
# ATOMiK for real-time state queries

class HybridAggregate:
    def __init__(self):
        self.event_store = EventStore() # audit trail
        self.ctx = AtomikContext()          # hot-path state
        self.ctx.load(0)

    def apply(self, event, delta):
        # Write path: both systems
        self.event_store.append(event) # for compliance
        self.ctx.accum(delta)          # for speed

    def current_state(self):
        # Hot read path via ATOMiK accumulator
        return self.ctx.read() # no replay on this path

    def audit_at(self, timestamp):
        # Time travel: via event store
        return self.event_store.replay_to(timestamp)

Key takeaway

Event sourcing optimizes for understanding the past. ATOMiK optimizes for knowing the present. If you need both, use both -- ATOMiK on the hot read path, event sourcing for audit and temporal queries. Validate the write-path overhead and replay reduction against the actual workload before making a production claim.

Historical articles and technical notes may include exploratory examples, synthesis figures, or modeled comparisons. Treat performance, power, savings, customer, production, and deployment claims as public-safe only when they are linked to measured artifacts or explicit evidence labels. Start with the current docs or evidence-label definitions.