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 |
|
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:
SessionProjectorSessionFsmSessionStateSessionEvent
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:
SessionStateSessionStatusSessionEventSessionFsmSessionProjector
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