Examples Search docs

Aggregates & CQRS

Aggregates are single-writer actors. Tests prove pure business rules and concurrency semantics without a cloud account.

What to unit-test first

ConcernAssert
Happy path decideGiven history + command → expected facts
Invariant rejectAGGREGATE_INVARIANT (or domain equivalent) — not retryable
OCC conflictStale expectedSeq → conflict; reload & retry succeeds
FenceStale/missing fence fails closed — not treated as OCC
IdempotencySame key does not double-apply effects
Fold purityFold(events) has no I/O

Pattern

history facts → fold state → decide(command) → facts
append(expectedSeq, fenceToken?) → store accepts/rejects
projection.fold(facts) → read model

Use in-memory EventStore + ActorHost for pure tests; use SQLite partition tests when proving durability (see Durability).

Multi-aggregate

Cross-aggregate workflows are eventually consistent. Test sagas with correlation ids and failure compensation — do not pretend distributed transactions exist.

Anti-patterns

  • Calling wall-clock inside pure decide
  • Treating STALE_FENCE as “retry with same token”
  • Asserting on projection without replaying from facts
  • Sharing one aggregate stream across unbounded entity ids

Data plane · ActorHost · Unit tests