An order reserves inventory, charges payment, then times out waiting for reservation confirmation. Cancellation begins. A late inventory success arrives after the refund. Without a state model, stock remains held or payment is refunded twice.
Define events and invariants
OrderCreated -> InventoryReserved -> PaymentCharged -> Confirmed
| timeout
v
RefundRequested -> InventoryReleased -> Cancelled
invariant 1: terminal Confirmed implies one charge and one reservation
invariant 2: terminal Cancelled implies no net charge and no reservation
invariant 3: each compensation key produces at most one external effect
Build an in-memory simulator that permutes duplicate delivery, timeout, late success, refund failure, and process restart. Persist event IDs and state revisions; reject transitions from stale revisions.
| Injection | Expected terminal evidence |
|---|---|
| payment succeeds, reserve response lost | reconcile before refund |
| refund delivered twice | one provider effect |
| late reserve after cancel | release command emitted |
| compensation fails | nonterminal alertable state |
Property tests should generate event orders and assert invariants after quiescence. Keep a minimal failing sequence whenever one breaks. Do not claim exactly-once delivery; make effects idempotent and reconciliation repeatable.
This simulator does not reproduce production brokers, provider semantics, or network timing. It validates a protocol hypothesis. Which compensation in your current Saga has no durable idempotency key?
Top comments (0)