State Transitions
ARO handles state machines elegantly: define states as OpenAPI enums,
transition with the <Accept> action, and let the runtime validate everything.
States Are Just Enums
No special state machine syntax. No transition tables. Define your states in OpenAPI,
use <Accept> to transition, and get automatic validation with clear error messages.
State Machine Example
Consider an order lifecycle. Orders flow through these states:
Defining States in OpenAPI
States are defined as string enums in your openapi.yaml:
# openapi.yaml
openapi: 3.0.3
info:
title: Order Management
version: 1.0.0
components:
schemas:
OrderStatus:
type: string
enum:
- draft
- placed
- paid
- shipped
- delivered
- cancelled
Order:
type: object
properties:
id:
type: string
status:
$ref: '#/components/schemas/OrderStatus'
customerId:
type: string
required:
- id
- status
The Accept Action
Use <Accept> to validate and apply state transitions.
The syntax clearly shows the expected transition:
(* Transition from draft to placed *)
<Accept> the <transition: draft_to_placed> on <order: status>.
(* Transition from placed to paid *)
<Accept> the <transition: placed_to_paid> on <order: status>.
(* Transition from paid to shipped *)
<Accept> the <transition: paid_to_shipped> on <order: status>.
The syntax uses _to_ as the separator because to is a reserved preposition in ARO.
Complete Example
Here's how state transitions work in practice with order management:
(placeOrder: Order Management) {
<Extract> the <order-id> from the <pathParameters: id>.
<Retrieve> the <order> from the <order-repository>.
(* Accept state transition from draft to placed *)
<Accept> the <transition: draft_to_placed> on <order: status>.
<Store> the <order> into the <order-repository>.
<Emit> to <Send Order Confirmation> with <order>.
<Return> an <OK: status> with <order>.
}
(payOrder: Order Management) {
<Extract> the <order-id> from the <pathParameters: id>.
<Retrieve> the <order> from the <order-repository>.
(* Must be placed to accept payment *)
<Accept> the <transition: placed_to_paid> on <order: status>.
<Store> the <order> into the <order-repository>.
<Return> an <OK: status> with <order>.
}
(shipOrder: Order Management) {
<Extract> the <order-id> from the <pathParameters: id>.
<Retrieve> the <order> from the <order-repository>.
(* Must be paid to ship *)
<Accept> the <transition: paid_to_shipped> on <order: status>.
<Store> the <order> into the <order-repository>.
<Emit> to <Send Shipping Notification> with <order>.
<Return> an <OK: status> with <order>.
}
Error Handling
If the current state doesn't match the expected state, the runtime throws a clear error:
This follows ARO's "Code Is The Error Message" philosophy. The error tells you exactly what was expected and what was found.
Why This Approach?
Simplicity
No new keywords. No special constructs. Just OpenAPI enums + <Accept>.
Use standard control flow (if, match) for complex logic.
Clarity
The transition is explicit in the code. Reading
<Accept> the <transition: draft_to_placed> on <order: status>
tells you exactly what's happening.
Safety
The runtime validates:
- Current state matches expected
fromstate - Target state is a valid enum value
- Field exists on the object
Learn More
See the full state objects specification in ARO-0013: State Objects.