E-Commerce Order
This example demonstrates a complete e-commerce order flow with shopping cart, checkout, payment, and inventory management.
Single Machine Version
A simplified version with one machine handling the order:
flow
machine: @order
scenario: add to cart
given:
@customer is logged in
cart is empty
on> :add_item from @customer
$items: array adds $item
$total: number increases by $item.price
expect:
= $items contains $item
on> :remove_item from @customer
$items removes $item
$total decreases by $item.price
expect:
= $total is correct
scenario: checkout
given:
@customer is logged in
cart contains:
| product | price |
| Laptop | 1200 |
$total: number is 1200
on> :checkout from @customer
? cart is valid
emit :payment_request to @payment
with:
| order_id | $order_id |
| amount | $total |
order moves to #awaiting_payment
? cart is empty
emit :error to @customer
with:
| message | "Cart is empty" |
expect:
= order is in #awaiting_payment
on :payment_success from @payment
order moves to #paid
emit :reserve_stock to @inventory
expect:
= order is in #paid
on :payment_failed from @payment
order moves to #payment_failed
emit :payment_error to @customer
with:
| error | $error_message |
on :stock_reserved from @inventory
order moves to #fulfilled
emit :order_confirmed to @customer
send confirmation email
expect:
= order is in #fulfilled
on :out_of_stock from @inventory
emit :refund_request to @payment
order moves to #cancelled
emit :order_cancelled to @customer
expect:
= order is in #fulfilled
= @customer received :order_confirmedMulti-Machine System
A production-ready version with separate machines for each domain:
flow
system: e-commerce checkout
machine: @order
scenario: order lifecycle
given:
@customer is logged in
cart has items
$total: number is 1200
on> :checkout from @customer
? cart is valid
emit :payment_request to @payment
with:
| order_id | string | $order_id |
| amount | number | $total |
order moves to #awaiting_payment
expect:
= order is in #awaiting_payment
on :payment_success from @payment
order moves to #paid
emit :reserve_stock to @inventory
with:
| order_id | $order_id |
| items | $items |
expect:
= order is in #paid
on :payment_failed from @payment
order moves to #payment_failed
emit :payment_error to @customer
on :stock_reserved from @inventory
order moves to #fulfilled
emit :order_confirmed to @customer
expect:
= order is in #fulfilled
on :out_of_stock from @inventory
emit :refund_request to @payment
order moves to #cancelled
expect:
= order is in #fulfilled
= @customer received :order_confirmed
machine: @payment
scenario: payment processing
on :payment_request from @order
process card with $amount
? card is valid
$transaction_id: string becomes "TXN-12345"
emit :payment_success to @order
with:
| transaction_id | $transaction_id |
?
$error_message: string becomes "Card declined"
emit :payment_failed to @order
with:
| error | $error_message |
on :refund_request from @order
process refund
emit :refund_complete to @order
machine: @inventory
scenario: stock management
on :reserve_stock from @order
? stock is available
reserve items
emit :stock_reserved to @order
?
emit :out_of_stock to @orderState Diagram
@order
┌───────────────────────────────────────────────────────────┐
│ │
│ ┌────────┐ :add_item ┌────────┐ :checkout │
│ │ #empty │ ──────────────>│ #cart │──────────────────┐ │
│ └────────┘ └───┬────┘ │ │
│ ▲ │ │ │
│ │ :clear │ │
│ └───────────────────────┘ │ │
│ │ │
│ ▼ │
│ ┌────────────────────┐
│ │ #awaiting_payment │
│ └─────────┬──────────┘
│ │
│ ┌───────────┴───────────┐
│ │ │
│ :payment_success :payment_failed
│ │ │
│ ▼ ▼
│ ┌──────────┐ ┌────────────────┐
│ │ #paid │ │ #payment_failed│
│ └────┬─────┘ └────────────────┘
│ │
│ ┌────────────┴────────────┐
│ │ │
│ :stock_reserved :out_of_stock
│ │ │
│ ▼ ▼
│ ┌────────────┐ ┌────────────┐
│ │ #fulfilled │ │ #cancelled │
│ └────────────┘ └────────────┘
│ │
└───────────────────────────────────────────────────────────┘Lane Diagram
┌─────────────┬─────────────┬─────────────┬─────────────┐
│ @customer │ @order │ @payment │ @inventory │
├─────────────┼─────────────┼─────────────┼─────────────┤
│ │ │ │ │
│ :checkout │ │ │ │
│ ────────────┼──> │ │ │
│ │ │ │ │
│ │ validates │ │ │
│ │ cart │ │ │
│ │ │ │ │
│ │ :payment_ │ │ │
│ │ request │ │ │
│ │ ────────────┼──> │ │
│ │ │ │ │
│ │ │ processes │ │
│ │ │ card │ │
│ │ │ │ │
│ │ :payment_ │ │ │
│ │ success │ │ │
│ │ <───────────┼─────────────│ │
│ │ │ │ │
│ │ :reserve_ │ │ │
│ │ stock │ │ │
│ │ ────────────┼─────────────┼──> │
│ │ │ │ │
│ │ │ │ checks │
│ │ │ │ stock │
│ │ │ │ │
│ │ :stock_ │ │ │
│ │ reserved │ │ │
│ │ <───────────┼─────────────┼─────────────│
│ │ │ │ │
│ :order_ │ │ │ │
│ confirmed │ │ │ │
│ <───────────┼─────────────│ │ │
│ │ │ │ │
└─────────────┴─────────────┴─────────────┴─────────────┘Testing Scenarios
Successful Checkout
flow
scenario: successful checkout
given:
@customer is logged in
cart contains:
| product | price |
| Laptop | 1200 |
$total: number is 1200
on> :checkout from @customer
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :stock_reserved from @inventory
order moves to #fulfilled
expect:
= order is in #fulfilled
= @customer received :order_confirmedPayment Failure
flow
scenario: payment fails
given:
@customer is logged in
cart has items
payment will fail
on> :checkout from @customer
order moves to #awaiting_payment
on :payment_failed from @payment
order moves to #payment_failed
expect:
= order is in #payment_failed
= @customer received :payment_errorOut of Stock
flow
scenario: out of stock after payment
given:
@customer is logged in
cart has items
inventory is empty
on> :checkout from @customer
order moves to #awaiting_payment
on :payment_success from @payment
order moves to #paid
on :out_of_stock from @inventory
order moves to #cancelled
expect:
= order is in #cancelled
= :refund_request was emitted to @payment
= @customer received :order_cancelledRefund Flow
flow
scenario: process refund
given:
order is in #fulfilled
$days_since_purchase: number is 5
on> :request_refund from @customer
? days since purchase < 7
? order total < 100
instant refund to wallet
order moves to #refunded
? order total >= 100
process credit card refund
order moves to #refund_pending
? days since purchase < 30
? customer has premium status
partial refund (75%)
order moves to #partially_refunded
?
partial refund (50%)
order moves to #partially_refunded
? days since purchase < 90
store credit only
$credit_amount becomes $order_total
emit :credit_issued to @customer
order moves to #credit_issued
otherwise
refund denied
emit :refund_rejected to @customer
with:
| reason | "Refund period expired" |
on :refund_processed from @payment
emit :refund_confirmation to @customer
order moves to #refunded
expect:
= @customer received notification