Skip to main content

Events

Events trigger state transitions in rstmdb. When an event is applied to an instance, the state machine evaluates possible transitions and moves to the appropriate next state.

Event Structure​

An event application includes:

{
"instance_id": "order-001",
"event": "PAY",
"payload": {
"payment_id": "pay-123",
"amount": 99.99,
"method": "card"
}
}

Fields​

FieldTypeRequiredDescription
instance_idstringYesTarget instance
eventstringYesEvent name
payloadobjectNoData to merge into context

Applying Events​

Basic Event​

rstmdb-cli apply-event -i order-001 -e PAY

With Payload​

The payload is merged into the instance context:

rstmdb-cli apply-event -i order-001 -e PAY -p '{
"payment_id": "pay-123",
"amount": 99.99
}'

With Idempotency​

rstmdb-cli apply-event -i order-001 -e PAY -p '{}' --idempotency-key "pay-order-001-attempt-1"

Transition Resolution​

When an event is applied:

  1. Find matching transitions - Look for transitions where from matches current state and event matches the applied event
  2. Evaluate guards - For each matching transition, evaluate the guard expression (if any)
  3. Select transition - Use the first transition whose guard passes (or has no guard)
  4. Apply transition - Update state, merge payload into context, record in WAL
  5. Broadcast - Notify any active subscriptions

Example​

Given this machine:

{
"states": ["pending", "approved", "escalated", "rejected"],
"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"}
]
}

And an instance with context {"amount": 500}:

# This will transition to "approved" (amount <= 1000)
rstmdb-cli apply-event -i request-001 -e APPROVE

With context {"amount": 5000}:

# This will transition to "escalated" (amount > 1000)
rstmdb-cli apply-event -i request-002 -e APPROVE

Event Response​

A successful event application returns:

{
"status": "ok",
"result": {
"previous_state": "pending",
"current_state": "approved",
"transition": {
"from": "pending",
"event": "APPROVE",
"to": "approved"
}
},
"meta": {
"wal_offset": 12345,
"server_time": "2024-01-15T10:30:00Z"
}
}

Error Cases​

Invalid Transition​

When no matching transition exists:

{
"status": "error",
"error": {
"code": "INVALID_TRANSITION",
"message": "No transition from 'approved' on event 'APPROVE'"
}
}

Guard Failed​

When a guard expression evaluates to false:

{
"status": "error",
"error": {
"code": "GUARD_FAILED",
"message": "Guard 'ctx.amount <= 1000' failed"
}
}

Instance Not Found​

{
"status": "error",
"error": {
"code": "INSTANCE_NOT_FOUND",
"message": "Instance 'order-999' not found"
}
}

Payload Merging​

Payloads are shallow-merged into the instance context:

# Context before: {"a": 1, "b": 2}
rstmdb-cli apply-event -i inst-001 -e UPDATE -p '{"b": 3, "c": 4}'
# Context after: {"a": 1, "b": 3, "c": 4}

Nested Objects​

Nested objects are replaced, not deep-merged:

# Context before: {"user": {"name": "alice", "role": "admin"}}
rstmdb-cli apply-event -i inst-001 -e UPDATE -p '{"user": {"name": "bob"}}'
# Context after: {"user": {"name": "bob"}} (role is lost!)

To preserve nested fields, include them in the payload:

rstmdb-cli apply-event -i inst-001 -e UPDATE -p '{"user": {"name": "bob", "role": "admin"}}'

Batch Events​

Apply multiple events atomically:

# Via API (not CLI)
{
"op": "BATCH",
"params": {
"mode": "atomic",
"operations": [
{"op": "APPLY_EVENT", "params": {"instance_id": "order-001", "event": "PAY", "payload": {}}},
{"op": "APPLY_EVENT", "params": {"instance_id": "order-002", "event": "PAY", "payload": {}}}
]
}
}

Modes:

  • atomic - All operations succeed or all fail
  • best_effort - Apply as many as possible, report failures

Best Practices​

Use Descriptive Event Names​

# Good - clear intent
apply-event -e ORDER_PLACED
apply-event -e PAYMENT_RECEIVED
apply-event -e SHIPMENT_DISPATCHED

# Bad - ambiguous
apply-event -e UPDATE
apply-event -e NEXT
apply-event -e DONE

Include Audit Data in Payloads​

rstmdb-cli apply-event -i order-001 -e APPROVE -p '{
"approved_by": "alice",
"approved_at": "2024-01-15T10:30:00Z",
"reason": "Within budget"
}'

Handle Event Failures Gracefully​

# Check response status
result=$(rstmdb-cli apply-event -i order-001 -e PAY -p '{}' 2>&1)
if echo "$result" | grep -q "INVALID_TRANSITION"; then
echo "Order is not in a payable state"
fi

Use Idempotency for Retries​

# Safe to retry - same result each time
rstmdb-cli apply-event -i order-001 -e PAY \
--idempotency-key "pay-order-001-$(date +%Y%m%d)"