Skip to content

Facade Pattern

The problem

A subsystem often grows into a tangle of interdependent classes, each responsible for one narrow slice of behavior. To accomplish anything useful, a caller has to know the order in which to call them, which state to set on each one, and what to do if any step fails. The caller ends up duplicating that coordination logic everywhere it needs the same workflow.

The Facade pattern cuts that knot by putting a single object in front of the subsystem. The facade knows the sequence, owns the coordination, and exposes only the verbs a caller actually needs. The subsystem classes stay intact and are still accessible directly when needed. The facade is a convenience layer, not a lock.

Structure

classDiagram
class HomeTheaterFacade {
+watchMovie(title)
+endMovie()
}
class Projector {
+on()
+off()
+setInput(input)
}
class SoundSystem {
+on()
+off()
+setVolume(level)
}
class StreamingPlayer {
+on()
+off()
+play(title)
+stop()
}
class Lights {
+dim(level)
+on()
}
HomeTheaterFacade --> Projector
HomeTheaterFacade --> SoundSystem
HomeTheaterFacade --> StreamingPlayer
HomeTheaterFacade --> Lights

When to use

  • A subsystem has grown complex enough that callers have to learn several classes just to accomplish a routine task.
  • You want a simpler default API without preventing callers from using the subsystem directly when they need full control.
  • You are wrapping a legacy system and want to expose a clean surface to new code.
  • You are integrating a third-party library and want to isolate your code from its API changes.

Implementation

All three show four subsystem classes (Projector, SoundSystem, StreamingPlayer, Lights) and a HomeTheaterFacade that sequences them into two high-level operations (watchMovie / endMovie). Each implementation takes subsystem instances through the facade constructor so they can be replaced in tests. The Python version uses plain classes with no interface declarations (duck typing provides the testing seam). The Go version holds concrete struct pointers in the facade; swap those fields for interface types if you need to inject fakes in tests.

idle
Click Run TS to execute. First run downloads Babel (~400 KB, cached after that).

Tradeoffs

ProCon
Reduces coupling between callers and subsystemFacade can become a “god object” if it grows too large
Provides a simpler API for common workflowsSubsystem is still accessible directly; facade is not enforcement
Easy to swap the subsystem behind the facadeA thin facade that does nothing useful is just indirection
Good for legacy system integrationHides complexity that callers might legitimately need

Gotchas

  • The facade does not prevent callers from using the subsystem directly. It offers a convenience layer, not a guard. Use a Proxy if you need access control.
  • A facade that grows to 20+ methods covering every subsystem operation has absorbed too much. Split by workflow or introduce multiple facades.
  • In Go, a facade often wraps an interface rather than a concrete subsystem so the subsystem can be swapped in tests.
  • Python’s duck typing means you don’t need to define subsystem interfaces unless you want testability. The facade itself is the testing seam.

References

  • Design Patterns, the full GoF catalog
  • Proxy, another structural wrapper, but focused on access control rather than simplification
  • Adapter, makes incompatible interfaces compatible rather than simplifying a subsystem
  • Functional Core, Imperative Shell, a related idea: one clean boundary between callers and a complex interior