On this page
Description
Facade is the structural pattern for giving a complicated subsystem one simple front door: a class with a few task-shaped methods (placeOrder, exportVideo) that coordinates many subsystem classes on the client's behalf. It's how service layers, logging APIs and SDKs keep callers from repeating the same fragile choreography. This skill covers the pattern, how to design one, and how to keep it from turning into a god object.
Placing an order touches four services, and they must be called in the right order:
Java
// In the web controller... and the mobile API... and the admin tool...
if (!inventory.reserve(sku, qty)) throw new OutOfStock();
String txn = payments.charge(card, amount);
if (txn == null) { inventory.release(sku, qty); throw new PaymentDeclined(); }
if (!shipping.schedule(sku, address)) { payments.refund(txn); inventory.release(sku, qty); throw new CannotShip(); }
notifications.sendConfirmation(email);
Python
# In the web controller... and the mobile API... and the admin tool...
if not inventory.reserve(sku, qty):
raise OutOfStock()
txn = payments.charge(card, amount)
if txn is None:
inventory.release(sku, qty)
raise PaymentDeclined()
if not shipping.schedule(sku, address):
payments.refund(txn)
inventory.release(sku, qty)
raise CannotShip()
notifications.send_confirmation(email)
Every client repeats this choreography, including the tricky rollback steps. Miss one release() in one copy and stock slowly leaks away.
We'll go from simple to more technical:
- The pattern: one task-shaped method instead of a choreography.
- Designing a facade, step by step - including rollback.
- Where you'll see facades, from SLF4J to API gateways.
- Keeping it small and leaving an escape hatch, and choosing among the structural patterns.
The pattern
A Facade is one class with a small, task-shaped interface that coordinates a complicated subsystem on the client's behalf.
Java
class OrderFacade {
private final Inventory inventory;
private final Payments payments;
private final Shipping shipping;
private final Notifications notifications;
OrderResult placeOrder(String sku, int qty, Card card, Address address) {
if (!inventory.reserve(sku, qty)) return OrderResult.failed("out of stock");
String txn = payments.charge(card, priceOf(sku, qty));
if (txn == null) {
inventory.release(sku, qty);
return OrderResult.failed("payment declined");
}
if (!shipping.schedule(sku, address)) {
payments.refund(txn);
inventory.release(sku, qty);
return OrderResult.failed("cannot ship to this address");
}
notifications.sendConfirmation(card.email());
return OrderResult.confirmed(txn);
}
}
Python
class OrderFacade:
def __init__(self, inventory: Inventory, payments: Payments,
shipping: Shipping, notifications: Notifications):
self._inventory = inventory
self._payments = payments
self._shipping = shipping
self._notifications = notifications
def place_order(self, sku: str, qty: int, card: Card, address: Address) -> OrderResult:
if not self._inventory.reserve(sku, qty):
return OrderResult.failed("out of stock")
txn = self._payments.charge(card, self._price_of(sku, qty))
if txn is None:
self._inventory.release(sku, qty)
return OrderResult.failed("payment declined")
if not self._shipping.schedule(sku, address):
self._payments.refund(txn)
self._inventory.release(sku, qty)
return OrderResult.failed("cannot ship to this address")
self._notifications.send_confirmation(card.email)
return OrderResult.confirmed(txn)
Clients now make one call. The subsystem classes don't know the facade exists.
How it works, step by step
- Identify the common task clients keep performing (placing an order, exporting a video).
- Create a facade class that holds references to the subsystem objects.
- Give it methods named after tasks, not after subsystem steps.
- Put the ordering and rollback logic in those methods - once.
- Leave the subsystems public: clients with unusual needs can still use them directly.
Where you'll see it
- SLF4J is literally the "Simple Logging Facade for Java": one tiny API in front of Logback, Log4j and others.
- Spring's
JdbcTemplatehides connections, statements, result sets and cleanup behindquery(...). - In Python,
requests.get(url)is a facade over sessions, connection pools and transport adapters, andshutil.copytreehides a whole directory walk ofoscalls. - The service layer of most applications is a set of facades over repositories and integrations.
- At system scale, an API gateway is a facade in front of many microservices.
Choosing among the structural patterns
| If you need to... | Reach for | What it changes |
|---|---|---|
| Make an existing class fit the interface your code expects | Adapter | Changes the interface; the behavior stays the same. |
| Add behavior to an object without changing its class or interface | Decorator | Same interface, extra behavior, and wrappers can be stacked. |
| Give a complicated subsystem one simple front door | Facade (this lesson) | A new, simpler interface over many classes. |
| Treat a single item and a group of items the same way | Composite | A tree: groups contain items or other groups. |
| Control access to an object (lazy loading, caching, security, remote calls) | Proxy | Same interface, but it decides when and whether the real object is used. |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| One facade for everything (a "god facade") | fifty methods every team edits | one facade per use case or area |
| Forbidding direct access to the subsystem | advanced needs bloat the facade | keep the subsystem usable directly |
| Business rules inside the facade | domain logic scattered into orchestration | keep rules in domain objects; the facade orchestrates |
| Rolling back in the wrong order, or not at all | leaked stock, charges without orders | compensate completed steps in reverse order |
| Calling payment or email APIs inside a database transaction | a charge survives the rollback | commit first, then call out (or use an outbox) |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. Do the subsystem classes know that a facade exists?
Show answer
No. A facade is one-directional: it calls the subsystem, the subsystem never refers back to it, and clients with unusual needs can still use the subsystem directly.
2. Shipping fails after the payment went through. What should an order facade do?
Show answer
Undo the completed steps in reverse order - refund the payment, then release the reserved stock - and report the failure. That compensation logic belongs in the facade, once.
3. Your facade has grown to 60 methods that every team edits. What next?
Show answer
Split it by use case (OrderFacade, ReturnsFacade, ...). A facade should simplify one area, not become the application's single point of contact.
Key takeaways
- A facade gives clients one simple, task-shaped method instead of a choreography of subsystem calls.
- Ordering and rollback logic live in one place instead of in every client.
- The subsystems don't know about the facade, and clients may still use them directly.
- Split a growing facade by use case before it becomes a god object.
- Adapter changes one interface to fit; Facade simplifies many.