E-Commerce Order State Machine in TLA+

Table of Contents

Overview

This specification models the state transitions in a typical e-commerce system (inspired by the OpenAPI PetStore example). We explore how Cart, Order, Shipment, and Status can become misaligned in a distributed architecture.

The Problem: State Synchronization

In microservices architectures, entities often span multiple services:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Cart      │───►│   Order     │───►│  Shipment   │───►│   Status    │
│   Service   │    │   Service   │    │   Service   │    │   Service   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
      │                  │                  │                  │
      ▼                  ▼                  ▼                  ▼
   CartDB             OrderDB           ShipmentDB         StatusDB

Each service maintains its own state, leading to potential inconsistencies:

  • Cart shows items, but order failed to create
  • Order exists, but shipment never created
  • Shipment delivered, but status shows "processing"

State Definitions

Cart States

---- MODULE ECommerceStates ----
EXTENDS Integers, Sequences, FiniteSets

CONSTANTS Items, MaxQuantity

\* Cart states
CartStates == {"empty", "active", "abandoned", "converted"}

\* Order states
OrderStates == {"pending", "confirmed", "paid", "cancelled", "refunded"}

\* Shipment states
ShipmentStates == {"none", "preparing", "shipped", "in_transit", "delivered", "returned"}

\* Status (customer-facing aggregated state)
StatusStates == {"browsing", "checkout", "processing", "shipped", "completed", "failed"}

Variables

VARIABLES
    cart,       \* Current cart state
    order,      \* Current order state
    shipment,   \* Current shipment state
    status,     \* Customer-facing status
    items,      \* Items in cart/order
    history     \* Audit trail of state changes

vars == <<cart, order, shipment, status, items, history>>

State Transitions

Cart Actions

\* Add item to cart
AddToCart(item) ==
    /\ cart \in {"empty", "active"}
    /\ items' = items \union {item}
    /\ cart' = "active"
    /\ status' = "browsing"
    /\ UNCHANGED <<order, shipment>>
    /\ history' = Append(history, <<"AddToCart", item>>)

\* Remove item from cart
RemoveFromCart(item) ==
    /\ cart = "active"
    /\ item \in items
    /\ items' = items \ {item}
    /\ cart' = IF items' = {} THEN "empty" ELSE "active"
    /\ UNCHANGED <<order, shipment, status>>
    /\ history' = Append(history, <<"RemoveFromCart", item>>)

\* Abandon cart (timeout or explicit)
AbandonCart ==
    /\ cart = "active"
    /\ cart' = "abandoned"
    /\ status' = "browsing"
    /\ UNCHANGED <<order, shipment, items>>
    /\ history' = Append(history, <<"AbandonCart">>)

Order Actions

\* Convert cart to order (checkout)
CreateOrder ==
    /\ cart = "active"
    /\ items # {}
    /\ order = "pending" \/ order = "none"
    /\ cart' = "converted"
    /\ order' = "pending"
    /\ status' = "checkout"
    /\ UNCHANGED <<shipment, items>>
    /\ history' = Append(history, <<"CreateOrder", items>>)

\* Confirm order (inventory reserved)
ConfirmOrder ==
    /\ order = "pending"
    /\ order' = "confirmed"
    /\ UNCHANGED <<cart, shipment, status, items>>
    /\ history' = Append(history, <<"ConfirmOrder">>)

\* Payment successful
PayOrder ==
    /\ order = "confirmed"
    /\ order' = "paid"
    /\ status' = "processing"
    /\ UNCHANGED <<cart, shipment, items>>
    /\ history' = Append(history, <<"PayOrder">>)

\* Cancel order
CancelOrder ==
    /\ order \in {"pending", "confirmed"}
    /\ order' = "cancelled"
    /\ status' = "failed"
    /\ UNCHANGED <<cart, shipment, items>>
    /\ history' = Append(history, <<"CancelOrder">>)

Shipment Actions

\* Create shipment after payment
CreateShipment ==
    /\ order = "paid"
    /\ shipment = "none"
    /\ shipment' = "preparing"
    /\ UNCHANGED <<cart, order, status, items>>
    /\ history' = Append(history, <<"CreateShipment">>)

\* Ship the order
ShipOrder ==
    /\ shipment = "preparing"
    /\ shipment' = "shipped"
    /\ status' = "shipped"
    /\ UNCHANGED <<cart, order, items>>
    /\ history' = Append(history, <<"ShipOrder">>)

\* In transit
InTransit ==
    /\ shipment = "shipped"
    /\ shipment' = "in_transit"
    /\ UNCHANGED <<cart, order, status, items>>
    /\ history' = Append(history, <<"InTransit">>)

\* Delivered
Deliver ==
    /\ shipment = "in_transit"
    /\ shipment' = "delivered"
    /\ status' = "completed"
    /\ UNCHANGED <<cart, order, items>>
    /\ history' = Append(history, <<"Deliver">>)

\* Return initiated
ReturnOrder ==
    /\ shipment = "delivered"
    /\ shipment' = "returned"
    /\ order' = "refunded"
    /\ UNCHANGED <<cart, status, items>>
    /\ history' = Append(history, <<"ReturnOrder">>)

Invariants: Detecting State Mismatches

Consistency Invariants

\* Cart and Order consistency
CartOrderConsistency ==
    \* If order exists, cart must be converted or active
    (order # "none" /\ order # "pending") => cart \in {"converted", "active"}

\* Order and Shipment consistency
OrderShipmentConsistency ==
    \* Shipment only exists for paid orders
    (shipment # "none") => order \in {"paid", "refunded"}

\* Shipment and Status consistency
ShipmentStatusConsistency ==
    \* If delivered, status must be completed
    (shipment = "delivered") => status = "completed"

\* No orphaned shipments
NoOrphanedShipments ==
    \* Cannot have shipment without a paid order
    (shipment \in {"preparing", "shipped", "in_transit", "delivered"}) =>
        order \in {"paid", "refunded"}

\* Items consistency
ItemsConsistency ==
    \* If cart is empty, no items should exist
    (cart = "empty") => items = {}

Temporal Properties (Safety)

\* Safety: Order never goes backwards
OrderNeverGoesBackwards ==
    [](order = "paid" => [](order # "pending"))

\* Safety: Delivered items stay delivered (unless returned)
DeliveredStaysDelivered ==
    [](shipment = "delivered" =>
        [](shipment \in {"delivered", "returned"}))

\* Safety: Cancelled orders never ship
CancelledNeverShips ==
    [](order = "cancelled" => [](shipment = "none"))

Temporal Properties (Liveness)

\* Liveness: Paid orders eventually ship
PaidOrdersEventuallyShip ==
    (order = "paid") ~> (shipment = "shipped")

\* Liveness: Shipped orders eventually complete
ShippedEventuallyCompletes ==
    (shipment = "shipped") ~> (status = "completed")

Failure Scenarios

Network Partition Examples

\* Scenario: Order service confirms, but shipment service doesn't receive message
PartitionAfterOrderConfirm ==
    /\ order = "paid"
    /\ shipment = "none"  \* Stuck - shipment never created
    \* This violates: PaidOrdersEventuallyShip

\* Scenario: Shipment delivered, but status service unreachable
PartitionAfterDelivery ==
    /\ shipment = "delivered"
    /\ status = "processing"  \* Stale status
    \* This violates: ShipmentStatusConsistency

Race Conditions

\* Scenario: Customer cancels while shipment is being created
RaceCancelAndShip ==
    \* Two concurrent actions:
    \* 1. Customer clicks "Cancel Order"
    \* 2. Warehouse starts preparing shipment
    \*
    \* Possible outcomes:
    \* - Order cancelled, shipment cancelled (correct)
    \* - Order cancelled, shipment proceeds (BAD: shipped cancelled order)
    \* - Shipment created, cancel fails (acceptable: customer can return)

    \* The invariant that catches this:
    /\ order = "cancelled"
    /\ shipment # "none"
    \* This violates: CancelledNeverShips

Model Configuration

\* Initial state
Init ==
    /\ cart = "empty"
    /\ order = "none"
    /\ shipment = "none"
    /\ status = "browsing"
    /\ items = {}
    /\ history = <<>>

\* All possible transitions
Next ==
    \/ \E item \in Items: AddToCart(item)
    \/ \E item \in items: RemoveFromCart(item)
    \/ AbandonCart
    \/ CreateOrder
    \/ ConfirmOrder
    \/ PayOrder
    \/ CancelOrder
    \/ CreateShipment
    \/ ShipOrder
    \/ InTransit
    \/ Deliver
    \/ ReturnOrder

\* Specification
Spec == Init /\ [][Next]_vars /\ WF_vars(Next)

\* Properties to check
THEOREM Spec => []CartOrderConsistency
THEOREM Spec => []OrderShipmentConsistency
THEOREM Spec => []ShipmentStatusConsistency
THEOREM Spec => []NoOrphanedShipments

====

Running the Model Checker

TLC Configuration

Create a ECommerceStates.cfg file:

CONSTANTS
    Items = {i1, i2, i3}
    MaxQuantity = 5

INIT Init
NEXT Next

INVARIANTS
    CartOrderConsistency
    OrderShipmentConsistency
    ShipmentStatusConsistency
    NoOrphanedShipments
    ItemsConsistency

PROPERTIES
    PaidOrdersEventuallyShip
    ShippedEventuallyCompletes

Expected TLC Output

Model checking completed. No error has been found.
  Estimates of the progress of the simulation:
  Coverage: 847 states generated, 234 distinct states found
  Depth: 12 states deep

Checking temporal properties...
  Property PaidOrdersEventuallyShip is satisfied.
  Property ShippedEventuallyCompletes is satisfied.

Lessons for System Design

Key Insights

  1. State machines are implicit - Most e-commerce systems have these states, but they're scattered across services without formal specification.
  2. Invariants catch bugs early - The consistency checks would have caught issues before production.
  3. Temporal properties matter - Liveness properties ensure the system makes progress.

Recommendations

Issue TLA+ Detection Production Symptom
Orphaned shipment NoOrphanedShipments Customer charged, nothing ships
Stale status ShipmentStatusConsistency Customer sees "processing" for delivered item
Cancel race CancelledNeverShips Cancelled order still ships
Stuck order PaidOrdersEventuallyShip Paid order never ships

Integration with OpenAPI/PetStore

The PetStore API defines similar states:

Order:
  status:
    enum: [placed, approved, delivered]

Pet:
  status:
    enum: [available, pending, sold]

This TLA+ spec extends those simple enums into a complete state machine with transition guards and consistency invariants.

References

Author: Jason Walsh

j@wal.sh

Last Updated: 2026-01-11 18:46:49

build: 2026-01-11 18:52 | sha: 52cb6b6