DEV Community
Follow
Designing Explicit State Machines for Money Movement
Payment systems often start with simple status columns like pending, successful, and failed. As products evolve, more nuanced states emerge, making the status field unreliable. An explicit state machine clarifies and enforces the payment lifecycle, ensuring statuses reflect business progress. Useful states should indicate system knowledge and valid next actions, with transitions being intentional. For instance, pending implies no commitment, processing means active fulfillment, and completed signifies satisfied obligations. Failed, cancelled, and expired represent unsuccessful outcomes or ended action windows, while disputed suspends normal rules for investigation. Transition rules should reside in the domain layer, with endpoints requesting state changes rather than directly assigning statuses. Terminal states like completed or cancelled require strong protection against re-opening by delayed events. Side effects, such as updating ledgers or notifying customers, should be coordinated by transitions to maintain consistency. Expiration should be managed by the backend as a state transition, not a UI timer. Background workers must re-check state before expiring transactions to avoid race conditions with late successes. Linked transactions need a single authority to manage their lifecycle and prevent duplicated actions. Preserving meaningful timestamps beyond a general update time is crucial for operational insight and dispute resolution. Ultimately, transaction status should represent business truth, changing only through named transitions that enforce prerequisites and coordinate effects.