Skip to content

Event Sourcing with Commutative Handlers

Event-processing architecture — instantiates Asynchronous Replica Convergence

Records changes as an append-only log of events and applies them through handlers designed so that replay, late arrival, and reordering all fold to the same state.

Event Sourcing with Commutative Handlers stores state not as a mutable value but as an append-only log of events, and — the distinguishing constraint — designs the event handlers so that the order and grouping in which events are applied do not change the result. Because folding the events is commutative and associative, two replicas that have absorbed the same events converge to the same state even when those events arrived interleaved, batched, or out of order. The log is the source of truth and current state is a replayable projection of it, which also means any prior or corrected state can be rebuilt from the record rather than reconstructed by guesswork.

Example

A retailer runs warehouses in three regions, each accepting stock adjustments while offline during network splits: +200 received in the EU, -5 damaged in the US, +50 returned in APAC. Rather than replicate a single mutable "on-hand count" — where a stale overwrite could erase an adjustment — each region appends adjustment events to a shared log. The handler for "adjust by N" is addition, which commutes and associates, so when the regions exchange logs the count folds to the same total regardless of arrival order or which batch merged first. One operation, though, does not commute: "set count to the results of a physical recount" must supersede everything before it, so it is entered in the sequencing-exception registry and applied with an explicit ordering barrier. When a corrupt projection is later found, the team rebuilds the count by replaying the log from the last good checkpoint. Nothing is overwritten; everything is accumulated.

How it works

The distinguishing method is algebraic discipline on the handlers, plus an honest registry of the exceptions:

  • Model changes as events. Store immutable facts ("adjusted by −5"), not mutations of a value.
  • Constrain the handlers. Design each handler so applying events in any order (commutative) and any grouping (associative) yields the same state — the algebra that makes convergence automatic.
  • Register the exceptions. Enumerate the operations that genuinely depend on order and give them explicit sequencing, rather than pretending they commute.
  • Rebuild by replay. Reconstruct current state, a past state, or a repaired state by folding the log (optionally from a snapshot) — the log is the restore path.

Tuning parameters

  • Event granularity — fine-grained deltas versus coarse composite events; finer events commute more easily but multiply log volume.
  • Handler algebra — which operations are required to commute and associate; the stricter the requirement, the more convergence is free, but the more the domain must be expressed as accumulations rather than overwrites.
  • Sequencing-exception policy — how order-sensitive operations are handled (causal barriers, per-key ordering); loose policy risks silently misapplying them.
  • Snapshot cadence — how often projections are checkpointed; frequent snapshots cut replay cost but add storage and snapshot-consistency work.
  • Log retention — how much history is kept; longer retention preserves rebuild-from-scratch and audit, but the log grows without bound if never compacted.

When it helps, and when it misleads

Its strength is threefold: order-independent convergence across replicas, a complete history for audit, and state that can be rebuilt or corrected by replay rather than in-place surgery. But not every operation commutes.[1] "Set to X" depends on order; "withdraw only if balance ≥ N" needs a global view no local fold can provide — and forcing such logic into a commutative handler yields a legal-but-wrong result. The log also grows without bound if retention and compaction are neglected. The classic misuse is declaring an order-sensitive operation commutative to dodge coordination, then shipping convergence to the wrong value. The discipline is to keep the sequencing-exception registry honest — enumerate every operation that truly needs ordering and give it real sequencing — rather than sweeping it into the commutative bucket.

How it implements the components

  • commutative_operation_rule — handlers are designed so applying the same events in any order produces the same state.
  • associative_grouping_rule — partial folds combine correctly regardless of batching, which is what lets replicas exchange and merge grouped logs.
  • sequencing_exception_registry — the explicit catalog of operations that do not commute and must be ordered, together with how each is sequenced.
  • rollback_or_restore_path — the append-only log is the restore path: any past, current, or repaired state is reconstructed by replaying (optionally from a checkpoint).

It does not suppress duplicate deliveries at the effect boundary (idempotence_guard, side_effect_boundary) — that is Deduplicating Message Consumer; it does not mint the operation_identity_key — that is Idempotency Keys; and it does not reconcile two ad-hoc divergent copies with human review (state_merge_policy, state_equivalence_test) — that is Data Diff and Merge Tool.

Notes

This shares its algebra with CRDT-Like State Merge, but keeps a different thing. A CRDT retains only the merged value; event sourcing retains the log of events and derives the value from it — buying rebuildable history and audit at the cost of storing and replaying every change. The two are complementary: CRDT-shaped state can itself be the projection an event log folds into.

References

[1] Operations that are commutative and associative form a commutative monoid; when they are also idempotent they form a join-semilattice — the algebra underlying conflict-free replicated data types. Where those laws hold, convergence follows from the algebra rather than from agreeing on an order; where they do not, no amount of replay makes an order-dependent result safe.