190 lines
3.7 KiB
Markdown
190 lines
3.7 KiB
Markdown
---
|
|
name: "Adr 0009 Replay Engine Projection Separation"
|
|
description: "Generic replay engine separated from domain projections"
|
|
depth: 2
|
|
links: ["../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:
|
|
|
|
```kotlin
|
|
Projection<SessionState>
|
|
```
|
|
|
|
and is executed by the generic replay engine.
|
|
|
|
---
|
|
|
|
### 3. Replay flow
|
|
|
|
Replay is defined as:
|
|
|
|
```text
|
|
EventStore
|
|
↓
|
|
EventReplayer<S>
|
|
↓
|
|
Projection<S>
|
|
↓
|
|
State
|
|
```
|
|
|
|
For sessions:
|
|
|
|
```text
|
|
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
|