Skip to main content

State Machines

State machines in rstmdb define the possible states and transitions for your data. They provide a formal model for managing state that ensures only valid transitions occur.

Definition Format

A state machine definition is a JSON object with the following structure:

{
"states": ["state1", "state2", "state3"],
"initial": "state1",
"transitions": [
{"from": "state1", "event": "EVENT_NAME", "to": "state2"},
{"from": "state1", "event": "OTHER_EVENT", "to": "state3", "guard": "ctx.condition"}
],
"meta": {
"description": "Optional metadata"
}
}

Fields

FieldTypeRequiredDescription
statesstring[]YesList of valid state names
initialstringYesThe starting state for new instances
transitionsTransition[]YesList of allowed transitions
metaobjectNoArbitrary metadata

Transition Object

FieldTypeRequiredDescription
fromstring | string[]YesSource state(s)
eventstringYesEvent name that triggers the transition
tostringYesTarget state
guardstringNoCondition expression

Creating a Machine

Use the PUT_MACHINE command via CLI or API:

rstmdb-cli put-machine -n order -v 1 '{
"states": ["draft", "submitted", "approved", "rejected"],
"initial": "draft",
"transitions": [
{"from": "draft", "event": "SUBMIT", "to": "submitted"},
{"from": "submitted", "event": "APPROVE", "to": "approved"},
{"from": "submitted", "event": "REJECT", "to": "rejected"},
{"from": "rejected", "event": "RESUBMIT", "to": "submitted"}
]
}'

Versioning

Machines are identified by name and version:

  • Name - A unique identifier for the machine type (e.g., "order", "user", "document")
  • Version - An integer version number (1, 2, 3, ...)

Instances are bound to a specific machine version at creation time. This allows you to evolve machine definitions while maintaining compatibility with existing instances.

# Create version 1
rstmdb-cli put-machine -n order -v 1 '{"states": ["a", "b"], ...}'

# Create version 2 with new states
rstmdb-cli put-machine -n order -v 2 '{"states": ["a", "b", "c"], ...}'

# New instances can use either version
rstmdb-cli create-instance -m order -V 1 -i old-order ...
rstmdb-cli create-instance -m order -V 2 -i new-order ...

Multi-Source Transitions

A transition can originate from multiple states:

{
"states": ["pending", "processing", "completed", "failed", "cancelled"],
"initial": "pending",
"transitions": [
{"from": "pending", "event": "START", "to": "processing"},
{"from": "processing", "event": "COMPLETE", "to": "completed"},
{"from": "processing", "event": "FAIL", "to": "failed"},
{"from": ["pending", "processing", "failed"], "event": "CANCEL", "to": "cancelled"}
]
}

The CANCEL event can be applied from pending, processing, or failed states.

Guard Conditions

Transitions can have guard conditions that must be satisfied:

{
"states": ["pending", "approved", "rejected", "escalated"],
"initial": "pending",
"transitions": [
{
"from": "pending",
"event": "APPROVE",
"to": "approved",
"guard": "ctx.amount <= 1000"
},
{
"from": "pending",
"event": "APPROVE",
"to": "escalated",
"guard": "ctx.amount > 1000"
},
{
"from": "pending",
"event": "REJECT",
"to": "rejected"
}
]
}

See Guards for the full expression language.

Best Practices

Use Meaningful State Names

// Good - clear and descriptive
"states": ["draft", "pending_review", "approved", "published"]

// Bad - unclear abbreviations
"states": ["d", "pr", "a", "p"]

Use Consistent Event Naming

// Good - consistent verb format
"transitions": [
{"event": "SUBMIT", ...},
{"event": "APPROVE", ...},
{"event": "REJECT", ...}
]

// Bad - inconsistent naming
"transitions": [
{"event": "submit", ...},
{"event": "APPROVED", ...},
{"event": "rejection", ...}
]

Model Terminal States Explicitly

Include states that represent completion:

{
"states": ["active", "completed", "cancelled", "expired"],
"initial": "active",
"transitions": [
{"from": "active", "event": "COMPLETE", "to": "completed"},
{"from": "active", "event": "CANCEL", "to": "cancelled"},
{"from": "active", "event": "EXPIRE", "to": "expired"}
]
}

Version for Breaking Changes

When you need to make incompatible changes:

  1. Create a new version with the updated definition
  2. New instances use the new version
  3. Existing instances continue with their original version
  4. Optionally migrate instances (create new, copy context, delete old)

Examples

Order Lifecycle

{
"states": ["cart", "checkout", "paid", "processing", "shipped", "delivered", "returned", "cancelled"],
"initial": "cart",
"transitions": [
{"from": "cart", "event": "CHECKOUT", "to": "checkout"},
{"from": "checkout", "event": "PAY", "to": "paid"},
{"from": "checkout", "event": "ABANDON", "to": "cart"},
{"from": "paid", "event": "PROCESS", "to": "processing"},
{"from": "processing", "event": "SHIP", "to": "shipped"},
{"from": "shipped", "event": "DELIVER", "to": "delivered"},
{"from": "delivered", "event": "RETURN", "to": "returned"},
{"from": ["cart", "checkout", "paid"], "event": "CANCEL", "to": "cancelled"}
]
}

Document Approval

{
"states": ["draft", "submitted", "under_review", "approved", "rejected", "published"],
"initial": "draft",
"transitions": [
{"from": "draft", "event": "SUBMIT", "to": "submitted"},
{"from": "submitted", "event": "ASSIGN_REVIEWER", "to": "under_review"},
{"from": "under_review", "event": "APPROVE", "to": "approved"},
{"from": "under_review", "event": "REJECT", "to": "rejected"},
{"from": "approved", "event": "PUBLISH", "to": "published"},
{"from": "rejected", "event": "REVISE", "to": "draft"}
]
}

IoT Device

{
"states": ["offline", "connecting", "online", "error", "maintenance"],
"initial": "offline",
"transitions": [
{"from": "offline", "event": "CONNECT", "to": "connecting"},
{"from": "connecting", "event": "CONNECTED", "to": "online"},
{"from": "connecting", "event": "TIMEOUT", "to": "offline"},
{"from": "online", "event": "DISCONNECT", "to": "offline"},
{"from": "online", "event": "ERROR", "to": "error"},
{"from": "error", "event": "RECOVER", "to": "online"},
{"from": "error", "event": "DISCONNECT", "to": "offline"},
{"from": ["online", "error"], "event": "MAINTAIN", "to": "maintenance"},
{"from": "maintenance", "event": "COMPLETE", "to": "offline"}
]
}