About the order flow
- Order board (
/features/order-flow/app): the original board as a statechart, in 8 lessons: welcome and legend, the happy path, "what else could happen?", guards, crossing the line of visibility, hierarchy and context (subscription and pack), time, and questions for the team. - Learn (
/features/order-flow/app/learn): "State machines, from scratch". 16 lessons for non-technical readers, one concept each, every one with its own small live machine from the order domain. The course's own navigation runs on a state machine, drawn live in the last lesson.
Click an event chip on the board to send it. Undo (or Cmd+Z) steps back. Drag or two-finger scroll to pan, pinch to zoom.
Source
FigJam file V1PQF8WkLy2WMXUHhVjTHj, node 381:10052: "Order system – scoping", section "Giga flow chart order handling".
The model is built from that one board plus guesswork. Every rule in Assumed business rules was invented to make the machine runnable and must be checked.
The original board
Three swimlanes: Customer, Tenant sales and System (database), with a line of visibility between customer and admin. The board was drawn by hand and covers the happy path.
| Lane | What the board has |
|---|---|
| Customer | Gallery → asset preview → "Finding the license I need?". Yes: add to cart or buy now → checkout (items, VAT) → accept terms and pay → payment gateway (card or invoice) → payment successful → download. No: contact customer support (phone, email, chat). Shortcut: a subscription or pack covering the image goes straight to download. |
| Tenant sales | Starts at "Start ???" (undefined on the board). Custom license or negotiated price → notify the customer. Subscription or pack → create product or variant, name and description, select customer, product, licenses, duration or credit limit, price → email with pay link. |
| System | Customer profile, order registered in the order overview, download registered in the download log. |
Also on the board: two unconnected decisions ("Downloaded and charged for before?", "Would a pack or subscription be cheaper?"), and sticky notes on visibility control, needing a profile to contact support, editorial gating, adding items to a customer's cart, showing deals as a delight ("15% off"), workspaces, and how Mediebank, Getty and Alamy do it.
Not modelled: the right-hand side of the board (order history, receipts, credit notes, refunds, supplier and royalty reports, VAT on commissions) and the Graveyard.
The additions
The Additions switch in the order board's toolbar fades everything that isn't on the original board and shows the original box names. The additions came from standing in each state and asking "what else could happen here?":
- card declined, retry, cancel at the gateway
- invoice overdue, order cancelled
- offer declined, withdrawn or expired; inquiry declined ("can't help"); new inquiry
- pack used up, subscription expired
- back to cart
The UI refers to the original board neutrally ("the original board") and builds on it rather than correcting it.
The model
One parallel machine (-lib/machine/orderHandling.ts) with three regions. Each region is something both the customer side and admin touch:
| Region | Meaning | States |
|---|---|---|
order | What the customer is buying | asset preview → cart, checkout or waiting for support → payment (choosing method, card, declined, invoiced, overdue) → paid → done; or cancelled |
offer | What tenant sales prepares for one customer | inquiry received (was "Start ???") → drafting (custom price, or product → licenses → duration or credit limit → price) → sent → accepted or closed |
access | A paid subscription or pack | active → used up or expired |
Handoffs cross the line of visibility and are drawn dotted: support.contacted, offer.accepted, offer.closed, order.accessPaid. Each one is a notification, an email or a screen, so work on both sides.
Senders. Every arrow has a sender: customer (needs customer UI), sales (needs admin UI), system (needs backend work) or time (needs a scheduler or job). An arrow nobody sends is a gap.
Not modelled yet
- Editorial gating: visible but not buyable until customer service approves the customer.
- Workspaces: an enterprise customer picks which workspace an order is billed to.
- Visibility control and products limiting what a customer sees.
- Re-download without a new charge ("downloaded and charged before?").
- Upsell to a pack or subscription at checkout.
- Whether sales may add items to a customer's cart. The model sidesteps it: the customer accepts an offer.
- Post-purchase: refunds, license changes, credit notes, order history.
- Payment details: redirect vs webhook order, closing the tab on the gateway, timeouts.
Running it
It's part of this app: bun run dev, then open /features/order-flow/app or /features/order-flow/app/learn. Colour mode follows the sun/moon toggle in the header.
Tests: bun test src/routes/features/order-flow/app. They cover machine paths, every scene layout, every lesson played step by step against its real machine, and the course navigation machine.
Changing it
| File | What it is |
|---|---|
-lib/machine/orderHandling.ts | The order statechart (the thing to refine) |
-lib/machine/basics.ts | Small teaching machines for Learn |
-lib/scene.ts | Scene type: machine, layout, labels, toggles, context rows |
-lib/scenes/order.ts, basics.ts | Board layouts: positions, lanes, arrow routes |
-lib/courses/order.ts, basics.ts | Lesson text and steps; the Learn navigation machine |
-lib/board/ | Machine → nodes and edges, orthogonal arrow routing, state → code |
-lib/sim/simulator.ts | Wraps an actor: log, undo, fired arrows and actions, async events |
-lib/components/ | Course view, board, lesson panel, now panel, extras (quiz, ladder, combinations) |
Conventions the board depends on:
- Every drawn transition has
meta: { label, sender }. Transitions without meta aren't drawn but still work.added: truemarks what isn't on the original board. - States use
meta: { label, hint?, original?, added?, question?, hidden?, hideInitial? }.originalkeeps the board's wording;hiddenis for idle states before an entity exists (hiddenOriginsin the layout says where their arrows start). - Event types with a dot (
offer.accepted) are handoffs: dotted and not clickable. - Every visible state needs a position in the scene's
nodeRects, keyed by path (order.payment.card). Arrow route overrides are keyed${ownerPath}>${EVENT}#${index}; a test fails if one points at nothing. - Adding a state: add it to the machine, position it in
-lib/scenes/order.ts, run the tests, look at it in the browser.