Facade
A checkout handler calls inventory.reserve, then payment.charge, then shipping.schedule, then emails the buyer, in that exact order. Every screen that places an order copies the same five-step dance and imports all five classes.
- ▸Call inventory, payment, and shipping in order and the caller depends on all three; one method shrinks it to one.
- ▸That one method adds no new behavior; it only runs the calls you were already making, in order.
- ▸It simplifies without sealing: worker classes stay public, so a power user can reach past it for a missing knob.
The handler behind a Buy button rarely does one thing. Placing an order means reserving stock, charging the card, scheduling a shipment, emailing a receipt, and writing an audit line, in that order:
function placeOrder(cart, card, addr) { const items = inventory.reserve(cart.skus); const receipt = payment.charge(card, cart.total); const label = shipping.schedule(addr, items); notify.email(cart.user, receipt, label); audit.log('order', cart.user, receipt.id); }
Five classes, five calls, and the order between them matters: you can't email a receipt you haven't created or schedule a shipment for stock you didn't reserve. This handler imports all five and encodes the right sequence.
Now the checkout page isn't the only caller. The mobile API places orders. The admin 're-run order' button places orders. A scheduled retry places orders. Each one copies the same five calls in the same sequence and grows its own import list of all five classes. When payment.charge gains a required argument, every one of those call sites has to change.
The work is genuinely five steps. The problem is that the knowledge of *which five, in what order* is duplicated into every caller, and each caller is now coupled to five moving parts instead of the one job it actually wanted: place an order.