Facts, not mutations
Significant state begins as a typed event. A fold projects those facts into the state required by the interface. Replaying the same accepted history must produce the same state and output.
#[derive(Clone)]
enum TaskEvent {
Created { id: u64, title: String },
Completed { id: u64 },
}
fn reduce(state: &mut Tasks, event: &TaskEvent) {
match event {
TaskEvent::Created { id, title } => state.insert(*id, title),
TaskEvent::Completed { id } => state.complete(*id),
}
}Progressive operating modes
- 01
Static only
No event history is required. Author complete content and deterministic output.
- 02
Local history
Append events locally and prove live-versus-replay projection parity.
- 03
Durable outbox
Record commands and pending effects before transport or retry.
- 04
Verified sync
Accept remote history only through typed, contiguous, verified receipts.
Replay parity
Every event-native starter should test three paths: live append, restore from snapshot plus tail, and replay from genesis. Their canonical projection bytes must match.
assert_eq!(live_state, replay(&events));
assert_eq!(live_state, restore(snapshot, &tail));Effects become evidence
Reducers do not call clocks, randomness, networks, filesystems, or models. They emit a command; an effect runner performs external work; the observed result returns as an event or receipt.