ADR 0006: Persistence via Event Sourcing and Durable State
Status
Accepted
Context
Actors lose their state when they stop or crash. For many use cases (shopping carts, accounts, workflows), state must survive restarts. Two persistence patterns are established:
- Event sourcing: Persist a sequence of events. Rebuild state by replaying events. Full audit trail.
- Durable state: Persist the current state directly. Simpler, no event history.
Both patterns are valuable for different use cases. The persistence layer must integrate with the existing behavior system without modifying nexus-core.
Decision
Implement persistence as a behavior wrapper layer:
PersistenceEngine: Wraps anyBehavior<T>to add event sourcing. Handles recovery (load snapshot → replay events), event persistence, and snapshot management.Effecttype: Command handlers return effects instead of behaviors.Effect::persist(events)to persist,Effect::none()to skip, withthenReply()andthenRun()chaining.DurableStateEngine: Simpler variant that persists current state.DurableEffect::persist($state).- Two API styles: Functional (
EventSourcedBehavior::create()) and class-based (AbstractEventSourcedActor). - Snapshot strategies:
SnapshotStrategy::everyN(100)for periodic snapshots, reducing replay overhead. - Pluggable stores:
EventStore,SnapshotStore,DurableStateStoreinterfaces with in-memory, DBAL, and Doctrine ORM implementations.
Three packages:
nexus-persistence: Core interfaces, in-memory stores, engines, behaviors.nexus-persistence-dbal: Doctrine DBAL adapter (raw SQL, lightweight).nexus-persistence-doctrine: Doctrine ORM adapter (entity-based, full ORM integration).
Consequences
- Zero changes to
nexus-core— persistence is purely additive. - Recovery is automatic: actors rebuild state on startup.
- Event sourcing provides a complete audit trail of all state changes.
- Snapshot + event replay balances recovery speed with storage efficiency.
- The
Effecttype makes persistence explicit in the type system. - The DBAL adapter is ~3x faster than the ORM adapter for high-throughput scenarios.