Beginner11 min readDesign Patternslive prototype

Facade

Put one simple front door on a complex subsystem. Instead of every caller driving six parts in the right order (amplifier on, DVD on, screen down, lights dim, projector on, play), a `HomeTheaterFacade` exposes one method — `watchMovie()` — that runs the whole sequence. The parts still exist and stay reachable; most callers just never need to touch them.

The idea

What it is

Facade gives you one simple front door to a complex subsystem. That is the entire intent, in one sentence. Behind the door there may be six classes, ten setup steps, and a strict order to follow — but the caller sees one object with a couple of friendly methods and calls exactly one of them.

Picture a home theater. To watch a movie the hard way, you drive six machines yourself: turn the amplifier on, turn the DVD player on, lower the screen, dim the lights, turn the projector on, and finally press play. Do it out of order and something breaks — a projector with no signal, a screen glowing in a bright room. A HomeTheaterFacade wraps all of that behind one method: watchMovie(). It runs the six steps, in the right order, every time. When you're done, endMovie() shuts everything down in reverse. You could still walk up to the amplifier and fiddle with it — the facade doesn't lock the parts away. It just means you almost never have to.

The one sentence to remember

A Facade is a single simple class that sits in front of a complicated subsystem and offers the common tasks as easy methods. It simplifies; it does not hide by force — the subsystem stays reachable for callers who need the full controls.

You already use facades every day

Every time you call one tidy method and a lot happens underneath — fetch(url), client.sendEmail(...), orderService.checkout(cart) — you are on the friendly side of a facade. The pattern is less something you learn and more something you finally notice.

Mechanics

How it works

One object that knows the subsystem so you don't have to

A facade is structurally the simplest of all patterns. It is just a class that holds references to the subsystem objects and orchestrates them inside a few high-level methods. There is no clever inheritance, no special interface — only delegation in the right order:

  • It holds the parts — the facade keeps a reference to each subsystem object (amp, dvd, screen, lights, projector).
  • It knows the choreography — the correct order of calls, the right settings, the cleanup. That knowledge lives in one place instead of being copy-pasted into every caller.
  • It exposes the common cases — one method per everyday task: watchMovie(), endMovie(). Rare or advanced needs can still go straight to the parts.
typescript
class HomeTheaterFacade {
  constructor(
    private amp: Amplifier,
    private dvd: DvdPlayer,
    private screen: Screen,
    private lights: Lights,
    private projector: Projector,
  ) {}

  watchMovie(film: string): void {   // one call — six steps, right order
    this.amp.on();
    this.dvd.on();
    this.screen.down();
    this.lights.dim(10);
    this.projector.on();
    this.dvd.play(film);
  }

  endMovie(): void {                 // and one call to undo it all
    this.dvd.stop();
    this.projector.off();
    this.lights.on();
    this.screen.up();
    this.dvd.off();
    this.amp.off();
  }
}

Notice what the facade does not do. It does not forbid direct access — if a power user wants to turn the amplifier up mid-movie, amp.setVolume(11) still works. It does not add new behaviour of its own — every line inside watchMovie() is a plain call to a subsystem the caller could have made. Its only job is to reduce what callers must know: from six classes and a fragile order down to one class and one method. That is [[coupling-and-cohesion]] in action — clients are coupled to one small facade instead of six churning subsystems — and it is the friendliest way to honour the [[law-of-demeter]]: callers talk to one near neighbour instead of reaching through a chain of strangers.

Real facades you've already met

An OrderService.checkout(cart) that runs payment, inventory, and email behind one call. An SDK client class like stripe.charges.create(...) fronting a whole HTTP API. fetch() fronting sockets, TLS, redirects, and HTTP parsing. In every case: many moving parts behind one calm method.

Facade vs Adapter — easy to mix up, easy to separate

Both patterns are 'a class in front of another class', so beginners blur them. The test is why the wrapper exists. An [[adapter]] changes an interface so it matches what some caller already expects — usually wrapping one object, converting calls one-for-one. A facade simplifies many interfaces into one — wrapping a whole subsystem and collapsing a multi-step workflow into a single friendly method. Adapter is about fit; Facade is about simplicity.

The subsystem doesn't disappear

A common misreading is 'Facade hides the subsystem so nobody can use it'. Wrong on both counts: the parts still exist, and direct access is still allowed. The facade is a default path for the common case, not a wall. If you find yourself forwarding every single subsystem method through the facade 'for consistency', you're building a god object, not a facade.

Interactive prototype

See it. Build it. Break it.

A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.

About this simulation

A home theater with six parts: amplifier, DVD player, screen, lights, projector, playback. In WITHOUT Facade, you are the client — you must click all six cards yourself, in the right order. Click one too early and it shakes red: the projector has no signal because the DVD player isn't on yet. Get all six right and the 🎬 banner lights up — and the counter reads calls you made: 6. Flip to WITH Facade and the cards go quiet: one button, watchMovie(), fires the same six steps in sequence while you watch. Same movie, same parts, but the counter reads 1. That gap — six careful calls versus one — is the whole pattern.

Hands-on

Try these yourself

Open the prototype above, predict what happens, then verify.

try 01

Be the client in the WITHOUT world

Open the prototype with the toggle on ✕ WITHOUT Facade. You must start the movie yourself: click the six cards in order — 🔊 Amplifier, 💿 DVD Player, 🎞 Screen, 💡 Lights, 📽 Projector, ▶ Playback. Try clicking 📽 Projector first: the card shakes red and the panel tells you why it can't start. Order matters, and you have to know it.

try 02

Finish the sequence and read the counter

Click all six cards in the correct order until the 🎬 movie playing banner appears. Now look at the chip at the top: 'calls you made: 6'. Six clicks, six chances to get the order wrong — that's the cost every caller pays when there's no facade.

try 03

Flip to WITH Facade

Switch the toggle to ✓ WITH Facade. The six cards dim — you can't click them anymore, but they're all still there. Press the ▶ watchMovie() button and watch the same six lights turn green one by one, in the same order, ending on the same 🎬 banner. The counter reads 'calls you made: 1'. Press ⏹ endMovie() to shut everything down in reverse — still one call. Same subsystem, same steps; the only thing that changed is who knows the choreography.

In practice

When to use it — and what trips people up

Reach for a facade when the subsystem is bigger than the task

  • A complex subsystem with a simple common case — most callers want watchMovie(), not six device APIs. Give the 90% case a one-method door and let the 10% go direct.
  • Layering — give each layer of your system one entry point. Controllers call OrderService, not the payment gateway, the inventory table, and the mailer directly. The facade is the layer boundary.
  • Decoupling clients from subsystem churn — when the parts behind the facade get replaced (new payment provider, new DVD-turned-streaming source), only the facade changes. Every client keeps calling the same simple method.

When NOT to use it

  • Don't force every call through it — a facade is a convenience, not a checkpoint. If advanced callers need the amplifier's fine controls, let them have the amplifier.
  • Beware the god-object facade — a facade that mirrors every method of every subsystem class has simplified nothing; it's just a second copy of the API that must be kept in sync. Expose the few high-level tasks, not the whole surface.
  • Skip it when the subsystem is already simple — wrapping one small class in a facade adds a layer with no payoff. One door in front of one door is just a hallway.

What it gives you

  • Callers get radically simpler — one class, one or two methods, instead of six classes and a fragile call order.
  • The choreography lives in one place — the correct sequence is written once in the facade, not copy-pasted (and mis-ordered) across every caller.
  • Loose coupling to the subsystem — clients depend on the small stable facade, so internals can be swapped or refactored without touching client code.
  • Gentle and non-invasive — the subsystem needs zero changes, and direct access stays open for the rare caller who needs full control.

Common mistakes

  • Risk of a god object — an over-grown facade that knows about everything becomes its own maintenance problem and a magnet for unrelated logic.
  • Can hide too much — callers who only ever see watchMovie() may never learn what the subsystem can really do, or why it failed three layers down.
  • One more layer of indirection — trivial subsystems gain nothing but an extra hop and an extra file.
  • A leaky facade is worse than none — if callers routinely need to reach around it for everyday tasks, the 'simple front door' was drawn in the wrong place.

Reference

Code & further reading

A minimal reference implementation and pointers worth bookmarking.

// --- The subsystem: each part is simple, but there are many ---
class Amplifier { on() { console.log("Amp on"); }  off() { console.log("Amp off"); } }
class DvdPlayer {
  on()  { console.log("DVD on"); }
  off() { console.log("DVD off"); }
  play(film: string) { console.log("Playing " + film); }
  stop() { console.log("DVD stopped"); }
}
class Screen    { down() { console.log("Screen down"); } up() { console.log("Screen up"); } }
class Lights    { dim(p: number) { console.log("Lights " + p + "%"); } on() { console.log("Lights on"); } }
class Projector { on() { console.log("Projector on"); } off() { console.log("Projector off"); } }

// --- The facade: one front door, choreography in ONE place ---
class HomeTheaterFacade {
  constructor(
    private amp: Amplifier, private dvd: DvdPlayer,
    private screen: Screen, private lights: Lights,
    private projector: Projector,
  ) {}

  watchMovie(film: string): void {
    this.amp.on();          // 1. sound first
    this.dvd.on();          // 2. source next
    this.screen.down();     // 3. surface ready
    this.lights.dim(10);    // 4. set the mood
    this.projector.on();    // 5. picture (now it HAS a signal)
    this.dvd.play(film);    // 6. action!
  }

  endMovie(): void {        // same steps, reversed
    this.dvd.stop(); this.projector.off(); this.lights.on();
    this.screen.up(); this.dvd.off(); this.amp.off();
  }
}

const theater = new HomeTheaterFacade(
  new Amplifier(), new DvdPlayer(), new Screen(), new Lights(), new Projector(),
);
theater.watchMovie("Inception");   // one call — six steps happen correctly

References & further reading

5 sources

Knowledge check

Did it land?

Quick questions, answers revealed on submit. Sign in to save your best score.

question 01 / 05

What is the intent of the Facade pattern, in one sentence?

question 02 / 05

In the home theater example, what does HomeTheaterFacade.watchMovie() actually do?

question 03 / 05

A power user wants to change the amplifier volume mid-movie. Under the Facade pattern, what happens?

question 04 / 05

What is the key difference between Facade and Adapter?

question 05 / 05

In the prototype, the counter reads 6 in the WITHOUT world and 1 in the WITH world. What does that gap demonstrate?

0/5 answered