In-Memory FSM
1. Context
Section titled “1. Context”A service needs stateful, mode-dependent behavior for the lifetime of a session or a single logical flow — for example, a calculator whose next operation depends on which mode it is currently in — but that state has no independent persisted identity worth storing in a database.
2. Problem
Section titled “2. Problem”How do you model behavior that depends on “which mode am I in” without persisting a database entity purely to hold a status field?
3. Forces
Section titled “3. Forces”- Consistency — state must still change only through defined transitions, even without a database backing it.
- Reuse — the same event and state vocabulary as an entity-backed FSM should stay usable, so behavior can move to persistence later without a redesign.
- Complexity — entry and exit hooks and validation still need a defined place to run.
- Persistence — explicitly out of scope: state resets between instances or deployments unless mirrored elsewhere.
4. Solution
Section titled “4. Solution”Declare an @fsm with in-memory instead of controls <Entity>.<field>,
list its states and default state directly, and use on enter,
on exit, and on event blocks the same way an entity-backed FSM would.
Pair it with a service-local @context for values that should survive
across events within the same session, such as the last computed mode.
5. Structure
Section titled “5. Structure”stateDiagram-v2 [*] --> NoOp NoOp --> Adding: setOperation(add) NoOp --> Subtracting: setOperation(sub) Adding --> NoOp: setOperation(nop) Subtracting --> NoOp: setOperation(nop) Adding --> Adding: calculate Subtracting --> Subtracting: calculate6. DSL Sketch
Section titled “6. DSL Sketch”@context
CalcFsmContext mode : String (default=no operation)
@fsm
CalcFSM in-memory states : NoOp, Adding, Subtracting default : NoOp
event setOperation in(op:String) out(result:String) event calculate in(a:Float & b:Float) out(r:Float)
use context CalcFsmContext as fsmCtx use expression CalcAdd as add use expression CalcSub as sub
state Every on event setOperation : logic if op == "add" then next Adding : result = "ADD" end next NoOp
state NoOp on enter : logic update fsmCtx.mode = "No-operation mode." on event calculate : logic emit r = 0
state Adding on enter: update fsmCtx.mode = "Adding mode." on event calculate: emit r = add(a, b)This is a minimal illustrative fragment, not a complete duplicated example — see Working Examples for the full, runnable model.
7. Consequences
Section titled “7. Consequences”Benefits
Section titled “Benefits”- No database or entity overhead for behavior that is genuinely transient.
- The same event and transition vocabulary as Entity-Backed FSM keeps the two patterns easy to move between as requirements change.
- A service-local context gives a small amount of state that survives across events without persistence.
Trade-offs
Section titled “Trade-offs”- State does not survive a process restart or move across instances unless explicitly synchronized elsewhere.
- Not appropriate once the behavior needs to be queried, audited, or resumed by identity outside the current session.
8. Alternatives
Section titled “8. Alternatives”- If the modeled behavior must be queryable, auditable, or resumed by identity across sessions, use Entity-Backed FSM instead.
9. Related DSL
Section titled “9. Related DSL”- FSM DSL Reference —
in-memorymode, states, and default state. - Context DSL Reference — service-local state that survives across events.
- Service DSL Reference — using an in-memory FSM from a service.
10. Working Examples
Section titled “10. Working Examples”- In-Memory Calculator FSM — an in-memory FSM whose active state selects calculator behavior and updates shared context.
11. Related Patterns
Section titled “11. Related Patterns”- Entity-Backed FSM — the alternative when the behavior does have a persisted identity to control.