Replace two checkouts that repeat four subsystem calls, one of them forgetting the undo, with one OrderDesk that owns the sequence and the reverse-order undo while the subsystems stay reachable.
A food app places an order through four subsystems: stock (Inventory), money (Payments), delivery slots (Delivery) and messages (Notifier). The app and the website each wrote their own checkout with the same four calls, and the website's copy forgets to give the stock back when the card is declined. Neither copy handles a full delivery schedule. The four subsystems are given and must not change. Put the sequence behind one facade, OrderDesk.
The behaviour to keep, exactly:
| Outcome | What place_order returns |
What must be true afterwards |
|---|---|---|
| Placed | placed, <qty> x <sku>, delivery <slot> |
the stock is reduced, the customer is charged qty * price, a slot is booked for the order id, and one message order <id> arrives at <slot> is sent |
| Out of stock | out of stock |
nothing else was touched: no charge, no booking, no message |
| Declined | declined |
the stock is back where it was, nothing was booked or sent |
| No delivery slot (new) | no delivery slot, charge refunded |
the charge is refunded and then the stock is released, nothing was sent |
Build this API (names are Python's; JavaScript and TypeScript use the same names):
OrderDesk(inventory, payments, delivery, notifier) with place_order(order_id, customer, sku, qty, address). It keeps no data of its own, and it names no concrete subsystem class: it uses whatever collaborators it is given. It translates Declined and NoSlot into the outcomes above and lets every other error through.The tests check every outcome and what is left behind, the order of the calls (with recording fakes written in the test), that a different set of collaborators plugs in, that the subsystem is still reachable (a restock straight on Inventory changes the next order), and that no copy of the checkout is left in the code.
Write your code, then run 9 example tests and 4 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.