Files
correx/docs/decisions/adr-0009-replay-engine-projection-separation.md
T

3.7 KiB

name, description, depth, links
name description depth links
Adr 0009 Replay Engine Projection Separation Generic replay engine separated from domain projections 2
../index.md
../modules/core-events-submodule-spec.md
../modules/core-sessions-submodule-spec.md

ADR 0009: Generic Replay Engine and Projection Separation

status: accepted date: 08.05.2026 deciders: Kami


context

Correx uses an append-only event store as the single source of truth.

Session state and future domain states are reconstructed entirely from ordered event streams. Replay determinism and projection purity are core system invariants.

During implementation of Epic 2 (Session Lifecycle FSM), a distinction emerged between:

  • generic replay infrastructure
  • domain-specific projection semantics

The system already contained reusable replay infrastructure in :core:events:

  • Projection<S>
  • StateBuilder<S>
  • EventReplayer<S>

At the same time, session lifecycle logic introduced:

  • SessionProjector
  • SessionFsm
  • SessionState
  • SessionEvent

A design decision was required to prevent:

  • replay logic duplication
  • infrastructure/domain coupling
  • projection semantics leaking into the event engine

content

The replay architecture is separated into two layers:

1. Generic replay engine (:core:events)

The :core:events module owns:

  • replay orchestration
  • projection execution contracts
  • state folding mechanics
  • ordered event replay infrastructure

This layer contains:

  • Projection<S>
  • StateBuilder<S>
  • EventReplayer<S>
  • replay implementations

This layer MUST remain domain-agnostic.

It MUST NOT contain:

  • session lifecycle logic
  • FSM semantics
  • domain state models
  • business transition rules

2. Domain projections (:core:sessions)

The :core:sessions module owns:

  • session lifecycle semantics
  • FSM transition logic
  • session state reconstruction
  • event interpretation for session domain

This layer contains:

  • SessionState
  • SessionStatus
  • SessionEvent
  • SessionFsm
  • SessionProjector

SessionProjector implements:

Projection<SessionState>

and is executed by the generic replay engine.


3. Replay flow

Replay is defined as:

EventStore
    ↓
EventReplayer<S>
    ↓
Projection<S>
    ↓
State

For sessions:

EventStore
    ↓
EventReplayer<SessionState>
    ↓
SessionProjector
    ↓
SessionState

4. Projection purity rules

All projections MUST be:

  • deterministic
  • side-effect free
  • replay-safe
  • stateless outside reduction input

Projections MUST NOT:

  • perform IO
  • access system clocks
  • mutate external state
  • depend on runtime execution order outside event sequence

Projection output MUST depend solely on:

  • previous state
  • current event

5. Session replay semantics

Session state is not persisted directly.

SessionState is reconstructed entirely from replaying ordered StoredEvent streams.

FSM transitions are interpreted from persisted EventPayload instances through session-specific mapping logic.


consequences

positive:

  • replay engine becomes reusable across domains
  • deterministic replay guarantees remain centralized
  • domain logic remains isolated from persistence mechanics
  • multiple independent projections can coexist over same event stream
  • session lifecycle logic remains testable in isolation
  • future projections can reuse replay infrastructure without duplication

negative:

  • introduces additional abstraction layers
  • domain projections require explicit wiring into replay engine
  • event interpretation requires mapper logic between payloads and FSM events
  • debugging replay chains may require traversing multiple layers

status

accepted