Structural · many familiar types
Facade
A is a structural pattern that gives callers one simple entry point into a group of classes that already have working APIs. The facade sequences those classes so the rest of the app does not have to remember every step.
A concrete example: checkout needs Inventory, Payable, InvoiceService, and Mailer. Controllers should call CheckoutFacade.placeOrder(cart) instead of wiring those four steps themselves.
Inventory
reserve(lines)
Payable
pay(total, id)
InvoiceService
issue(cart, paid)
Mailer
sendReceipt(email, invoice)
One entry point
CheckoutFacade.placeOrder(cart)
The rest of the app does not call the four services directly.
Controller / API
one collaborator, not four
Core question
Can I give callers one simple door so they never have to orchestrate the messy rooms behind it?
The classes behind the door keep their interfaces. You are not translating a foreign SDK — that is Adapter. You are hiding how many objects it takes to finish a use case. fits this well: a controller talks to the facade, not to every service inside it.
The sequence — still four objects
Facade does not delete Inventory. It stops every controller from remembering the order of steps. Each step still lives where the data lives.
- 01
Reserve
inventory.reserve(cart.lines()) — stock is Inventory’s job.
- 02
Charge
payment.pay(cart.total(), cart.id()) — money is Payable’s job (maybe an adapter behind it).
- 03
Invoice
invoices.issue(cart, paid) — documents are not the payment SDK’s job.
- 04
Mail
mailer.sendReceipt(cart.email(), invoice) — SMTP is not checkout policy.
Java sketch: one door into checkout
Keep the facade . It sequences calls. It should not become a CheckoutManager that owns pricing formulas, SQL, and HTML.
class CheckoutFacade {
private final Inventory inventory;
private final Payable payment;
private final InvoiceService invoices;
private final Mailer mailer;
CheckoutFacade(Inventory inventory, Payable payment,
InvoiceService invoices, Mailer mailer) {
this.inventory = inventory;
this.payment = payment;
this.invoices = invoices;
this.mailer = mailer;
}
Receipt placeOrder(Cart cart) {
inventory.reserve(cart.lines());
PaymentResult paid = payment.pay(cart.total(), cart.id());
Invoice invoice = invoices.issue(cart, paid);
mailer.sendReceipt(cart.email(), invoice);
return new Receipt(invoice.id(), paid);
}
}
// controller: return checkout.placeOrder(cart);
// not: inventory.reserve; payment.pay; invoices.issue; mailer.sendThin door (the pattern)
- Delegates every step to a collaborator that already owns it.
- placeOrder is a use-case verb, not a vague process(Object).
- The rest of the app imports CheckoutFacade, not the four internals.
- Tests can replace one facade or inject four fakes at the constructor — both are honest.
named Facade
- Computes tax, writes JDBC, and builds MIME in the same class.
- Grows a private method per new product rule.
- Callers still sneak around it to “just reserve once.”
- You hid a Manager, not a subsystem.
Same idea in another domain: hotel check-in
Reservations, billing, and housekeeping already have fine APIs. The pain is that every kiosk would have to remember the check-in sequence. The front desk does not rename those APIs. It stops the guest from meeting them one by one.
class HotelFrontDesk {
private final Reservations rooms;
private final Billing billing;
private final Housekeeping housekeeping;
KeyCard checkIn(Guest guest, Dates stay) {
Room room = rooms.allocate(stay);
billing.openFolio(guest, room);
housekeeping.prepare(room);
return room.keyCard();
}
}A subsystem, not a rename
| Smell | Count of types behind the wrap | Pattern |
|---|---|---|
| Caller must remember four steps | Many, all already yours | Facade |
| SDK method names do not match Payable | One foreign class | Adapter |
| Need log + retry on the same Notifier | One, same interface | Decorator |
| Do not load the 12MB image yet | One, same interface, delayed creation | Proxy |
A “StripeFacade” that only renames charge to pay is an adapter with a louder name. Facade is for a . They can compose: the facade’s Payable field may be a Stripe adapter. That is healthy. See Adapter vs Facade.
Common mistakes
A God object named Facade
Calling a one-class rename a Facade
Callers still import the internals
A method named process(Object)
How to explain this in an interview
“Inventory, Payable, Invoice, and Mailer already have fine APIs. The controller should not know the sequence, so CheckoutFacade.placeOrder sequences them. This is not an adapter — we did not change a foreign signature. Payable might still be a Stripe adapter behind the door.”
- List the four calls a naive checkout controller would make. Draw the facade method that replaces them.
- Where does tax policy live if the facade must stay thin?
- Logging framework: Logger facade in front of appenders and formatters — or a decorator on one appender? When is each honest?
Takeaway
Facade gives many familiar classes one simpler method. Sequence the work; do not absorb all the policy. One SDK with the wrong method names is Adapter, not a facade.