Home › Low-Level Design › Facade Pattern
F

Facade Pattern

One clean door into a messy room. The Facade gives callers a single, simple entry point so they never have to touch the tangle of classes behind it. I reach for it more than almost any other structural pattern, not because it is clever, but because it is quiet: it makes code readable without changing how anything works underneath.

Low-Level DesignEasy9 min read

If you remember five things

The whole pattern in one glance. Everything below is the defense of these five calls.

  1. Intent. Wrap a multi-class subsystem behind one small, friendly method so callers talk to one object, not six.
  2. It coordinates, never computes. A Facade delegates to existing classes. The moment it grows real business logic, it has stopped being a Facade.
  3. Composition, not inheritance. The Facade holds references to the subsystem and delegates. The subsystem classes never hear about the Facade and stay reusable on their own.
  4. Convenience, not a wall. It simplifies the common path. An advanced caller can still reach a subsystem class directly.
  5. The classic gotcha. It quietly becomes a God object: validation, then pricing, then caching all pile in until one class owns everything. Keep it thin.

This is a Gang of Four structural pattern, and its whole reason to exist is the Law of Demeter: a caller should talk to one friend, not to the friend's friend's friend. By the end of this page you can draw it, code it, and defend it when an interviewer pushes.

1The problem it solves#

A subsystem of five or six classes is powerful but hard to call correctly. Each class does one job, so getting something useful done means talking to all of them, in the right order, with the right data passed between them.

Picture a "place an order" flow in an online store. One order needs four steps, in sequence:

  1. Check the item is in stock (InventoryService)
  2. Charge the card (PaymentService)
  3. Book a courier (ShippingService)
  4. Email a receipt (NotificationService)

Now spread that four-step dance across your codebase. Say the order logic lives in 3 controllers, 2 background jobs, and a dozen tests:

  • That is the same 4-step sequence copy-pasted in 17 places.
  • Add a fraud check between steps 2 and 3, and you hunt through all 17 callers to patch every one.
  • You will miss one. You always miss one.

The real pain is coupling. Every caller depends on 4 classes plus the exact order they run in:

  • That is 4 reasons every caller might break when the subsystem changes.
  • It is a direct Single Responsibility Principle violation: the controller now owns both "handle the HTTP request" and "know the order-placement choreography."
The one-sentence intent

A Facade exists to turn "call these 6 classes in this exact order" into "call this one method," so the choreography lives in exactly one place and every caller depends on exactly one thing.

2The idea (mental model)#

Think of the front desk at a hotel. You want a late checkout, a taxi, and your bags sent up:

  • You do not call housekeeping, then the garage, then the bell desk yourself.
  • You talk to the front desk and say what you want. They coordinate the rest.
  • The front desk does not replace housekeeping or the garage. Those still exist and still do the real work.
  • It just gives you one number to call and hides the org chart.

That is the whole pattern. A Facade is a class that wraps a complicated subsystem and exposes a small, friendly method or two that do the common thing. The subsystem classes stay exactly as they are. You are only adding a simpler way in.

Two things worth locking in:

  • It adds no new behavior of its own. It orchestrates existing behavior. If it starts computing things, it has stopped being a Facade.
  • It does not block direct access. If an advanced caller needs a subsystem class directly, they still can. The Facade is a convenience, not a wall.
From the interviewer's chair

When I ask "how does a caller actually trigger all of this?", I am checking whether you think about API surface and coupling, not just boxes. The answer "I put a Facade in front so the controller calls one method" lands well. Then I will immediately test whether you know where to stop stuffing logic into it.

3Structure#

The cast is small. Three roles, and only one arrow direction matters:

  • Subsystem classes do the real work.
  • The Facade is the simple front.
  • The Client wants the job done and talks only to the Facade.

The Facade holds references to the subsystem classes and delegates to them. Nothing inherits anything: this is composition, not inheritance. The Facade is composed of the pieces it coordinates, which is "favor composition" in practice.

classDiagram class Client class OrderFacade { +placeOrder(order) String } class InventoryService { +reserve(itemId) boolean } class PaymentService { +charge(amount) boolean } class ShippingService { +schedule(order) String } class NotificationService { +sendReceipt(email) void } Client --> OrderFacade OrderFacade *-- InventoryService OrderFacade *-- PaymentService OrderFacade *-- ShippingService OrderFacade *-- NotificationService
Notice the arrows point one way. The Client has a plain association to the Facade (it just calls it). The Facade is composed of the four services (the filled diamonds). Crucially, the services have no arrow back to the Facade: they do not know it exists, which is why a second Facade like BulkOrderFacade could reuse the exact same four services.

Here is the same thing as a runtime flow, which is what an interviewer usually wants you to walk through:

sequenceDiagram Client->>OrderFacade: placeOrder(order) OrderFacade->>InventoryService: reserve(itemId) InventoryService-->>OrderFacade: true OrderFacade->>PaymentService: charge(amount) PaymentService-->>OrderFacade: true OrderFacade->>ShippingService: schedule(order) ShippingService-->>OrderFacade: trackingId OrderFacade->>NotificationService: sendReceipt(email) OrderFacade-->>Client: confirmation
All the ordering and the pass-the-data-forward logic lives inside the Facade. The Client sees one request and one confirmation. Everything between those two lines is choreography the caller never has to know.

4Implementation in Java#

Keep the subsystem classes honest but minimal. Imagine each one has real logic inside.

// --- Subsystem classes. Each knows one job and nothing about the others. ---

class InventoryService {
    boolean reserve(String itemId) {
        System.out.println("Reserved " + itemId);
        return true;
    }
}

class PaymentService {
    boolean charge(String cardToken, double amount) {
        System.out.println("Charged " + amount);
        return true;
    }
}

class ShippingService {
    String schedule(String itemId, String address) {
        System.out.println("Courier booked to " + address);
        return "TRK-" + itemId.hashCode();
    }
}

class NotificationService {
    void sendReceipt(String email, String trackingId) {
        System.out.println("Receipt to " + email + ", track " + trackingId);
    }
}

Now the Facade. One method, one clear job, all the orchestration hidden inside it.

class OrderFacade {

    private final InventoryService inventory = new InventoryService();
    private final PaymentService payment = new PaymentService();
    private final ShippingService shipping = new ShippingService();
    private final NotificationService notifier = new NotificationService();

    // The client calls only this.
    public String placeOrder(String itemId, String cardToken,
                             double amount, String address, String email) {

        if (!inventory.reserve(itemId)) {
            return "FAILED: out of stock";
        }
        if (!payment.charge(cardToken, amount)) {
            return "FAILED: payment declined";
        }

        String trackingId = shipping.schedule(itemId, address);
        notifier.sendReceipt(email, trackingId);

        return "CONFIRMED: " + trackingId;
    }
}

And the client, which is now blissfully dumb:

public class Store {
    public static void main(String[] args) {
        OrderFacade orders = new OrderFacade();

        String result = orders.placeOrder(
            "BOOK-42", "tok_abc", 499.0,
            "12 MG Road", "buyer@example.com"
        );

        System.out.println(result);
    }
}

Notice what the client does not know:

  • That there are four services at all.
  • The order they run in.
  • That stock is checked before the card is charged.

The day I add a fraud check, I touch one file: the Facade. One file instead of 17.

Pause before you read on: in the code above I write `new InventoryService()` inside the Facade. What does that cost me, and what would a senior reviewer flag?
Reveal the answer
Hard-wiring `new` inside the Facade makes it almost impossible to unit test. You cannot hand it a fake PaymentService that returns false to check the "payment declined" path without charging a real card. The fix is dependency injection: accept the services through the constructor. That one change turns a hard-to-test class into an easy one, and naming it unprompted is a clear signal you have actually shipped code like this.

The fix is constructor injection. Pass the services in so you can swap mocks in tests:

class OrderFacade {
    private final InventoryService inventory;
    private final PaymentService payment;
    // ... accept the rest via constructor too

    OrderFacade(InventoryService inventory, PaymentService payment /* ... */) {
        this.inventory = inventory;
        this.payment = payment;
    }
}

Now a unit test can hand the Facade a PaymentService stub that returns false and assert you get back "FAILED: payment declined" without touching a real gateway.

5A real-world example#

You already use Facades every day. The order flow above is the textbook case, but the pattern hides inside tools you ship with:

  • SLF4J and logging. Your app calls logger.info(...). The name says it: SLF4J is the Simple Logging Facade for Java. Behind one call, a subsystem picks the backend (Logback, java.util.logging), formats the message, and routes it. Swap Logback for Log4j2 and not one logger.info call changes.
  • JDBC-style data access. A UserRepository with findById and save hides connection pooling, prepared statements, result-set mapping, and transactions. The caller just wants a user, not 5 steps of boilerplate.
  • Compiler and build tools. One build command kicks off parsing, dependency resolution, compilation, and packaging in order. You are not calling each phase yourself.
  • Payment integrations. A Payments.charge(...) wrapper hides Stripe, retries, idempotency keys, and currency conversion. The rest of the app does not know Stripe exists, so adding a second gateway touches one class.

In an LLD setting, the tell is a "do the whole thing" operation. Any time your problem fans a single verb (place order, start a ride, book a movie seat, publish a post) out into several subsystems, a Facade is the natural coordinator.

6When to use it, and when not to#

Reach for it
Facade earns its place
Too many classes
A subsystem has grown enough that callers struggle to use it correctly.
Repeated dance
The same multi-step sequence is copy-pasted in many places.
Vendor lock-in
You want to wall off a third-party library so you can swap it. The Facade acts as an anti-corruption layer: the vendor's types stop at the door.
Layering
Each layer exposes one clean entry point to the layer above.
Skip it
Facade is just noise
Already simple
The subsystem is one or two classes. Wrapping two calls in a third class adds nothing.
Fine-grained control
Callers genuinely need direct access. Forcing a narrow door frustrates people, and they reach around it.
Logic creep
You are tempted to put business rules inside. If logic lives nowhere else, it is a God object wearing a disguise.

A Facade should feel like it could be deleted and the subsystem would still work. It is a convenience layer, not the brain.

7Common mistakes#

These are the ones that come up in interviews, and the first two fail candidates.

  • Turning it into a God object. The most common failure. The Facade starts thin, then someone adds validation, then pricing math, then a cache, and now 800 lines of unrelated logic live in one class. That breaks Single Responsibility hard. It delegates; it does not compute.
  • Forcing all access through it. A Facade simplifies the common path, it does not forbid the uncommon one. If an advanced caller needs InventoryService directly, let them. A sealed-off subsystem is a liability.
  • Confusing it with Adapter. Adapter changes an interface to match what a client expects (a square peg into a round hole). Facade simplifies interfaces that already work. Adapter is about compatibility, Facade about convenience. Giveaway: Adapter usually wraps exactly one class, Facade fronts several.
  • Confusing it with Mediator. Mediator sits between peers so they can talk without direct links. Facade sits in front of a subsystem so an outsider can talk to it easily. Mediator faces inward, Facade faces outward. Also: subsystem classes know about the Mediator, but they never know about the Facade.
  • Leaking subsystem types in the signature. If placeOrder returns an InventoryReservation full of internal fields, you just leaked the subsystem to the client, and now they depend on it anyway. Return simple results or your own clean DTO.
Red flag that fails you here

Putting real business rules inside the Facade and calling it clean design. If I ask "where does the fraud scoring live?" and your answer is "in the Facade," I have watched you rebuild the God object the pattern was meant to prevent. The Facade routes the call to a fraud service. It does not score the fraud.

8Where it shows up in interviews#

Facade rarely headlines, because it is Easy. It plays a supporting role inside a bigger design, and knowing to name it scores points.

  • Expect it when you design an order service, a ride-booking flow, a notification system, or a payment module.
  • You will sketch several services, then the interviewer asks "how does a caller actually trigger all of this?"
  • The answer: "I put a Facade in front so the controller calls one method." That shows you think about API surface and coupling.
Follow-ups the interviewer will ask (and your answers)
"How is this different from an Adapter?"
Adapter changes an interface so an incompatible client can use a class: it is about compatibility, and it usually wraps exactly one class. Facade simplifies a set of interfaces that already work fine: it is about convenience, and it usually fronts several classes. Different problems that happen to both involve wrapping.
"Where do you draw the line between a Facade and a God object?"
The Facade may route, order, and pass data between calls, but it must not own business rules. The moment it computes pricing, scores fraud, or caches results, that logic belongs in a dedicated class the Facade calls. My test: if I deleted the Facade, the subsystem should still do the real work. A God object deleted takes the logic with it.
"How would you make this testable?"
Inject the subsystem dependencies through the constructor instead of calling new inside. Then a unit test hands the Facade a PaymentService stub that returns false and asserts the declined path without hitting a real gateway. That one change turns a hard-to-test class into an easy one.
"Should the Facade stop callers from reaching the subsystem directly?"
No. A Facade is a convenience, not a wall. It handles the common path. If an advanced caller needs a subsystem class directly, they keep that access. Sealing the subsystem off just makes people reach around the Facade and build worse coupling elsewhere.
"Is this the same as Mediator?"
No. Mediator sits between peers so they coordinate without direct links, and the peers know about it: it faces inward. Facade sits in front of a subsystem so an outsider can call it simply, and the subsystem never knows it exists: it faces outward.

What you can now explain in the room

  • The intent in one sentence: one simple entry point in front of a multi-class subsystem.
  • Why it cuts coupling: callers depend on one stable interface, not on the shape of the subsystem.
  • That it is composition, not inheritance, and that the subsystem never knows the Facade exists.
  • When to reach for it and one honest case where it is just noise.
  • The God-object trap, and why the Facade routes but never computes.
  • How Facade differs from Adapter (compatibility) and Mediator (inward-facing peers).

Next, I would study the Adapter pattern (its most-confused cousin) and the Mediator pattern, then try applying Facade inside a full "design an order management system" LLD problem.

Get a prep plan built around your Facade Pattern interview

Tell us the role and your interview date. We will send back a dated, day by day plan. Free.

Thanks. Check your inbox, your Facade Pattern prep plan is on the way.