Facade
Provide a simplified interface to a complex subsystem. A Facade hides the complexity of a library, framework, or cluster of classes behind a single, easy-to-use entry point.
The Problem
You’re integrating a video conversion library. To convert a file you need to: initialise a VideoFile, create a CodecFactory, run BitrateReader.read(), call AudioMixer.fix(), then pass everything to MPEG4CompressionCodec, check error states on each call, and finally invoke VideoConverter.convertVideo(). That’s six classes and a dozen calls for one operation.
Client code shouldn’t need to understand all those subsystem details. Most of the time, users just want: “convert this file to MP4.”
The Solution
Create a Facade class that exposes a simple, high-level interface. It delegates to the subsystem under the hood. Clients that do need advanced access can still bypass the Facade — it’s an addition, not a replacement.
Real-World Analogy
A smart home app. You press “Goodnight” and it dims lights, locks the doors, lowers the thermostat, and arms the alarm. You didn’t interact with the lighting system, lock controller, HVAC, or alarm panel separately — the app’s Facade did it for you. You still can adjust each system individually if you want.
TypeScript Example
// Complex subsystem — many classes, many interactions required
class VideoFile {
constructor(readonly filename: string, readonly codec: string) {}
}
class CodecFactory {
static extract(file: VideoFile): string {
console.log(`[CodecFactory] Detected codec: ${file.codec}`);
return file.codec;
}
}
class BitrateReader {
static read(filename: string, codec: string): string {
console.log(`[BitrateReader] Reading bitrate stream from ${filename}`);
return `stream:${filename}:${codec}`;
}
static convert(stream: string, codec: string): string {
console.log(`[BitrateReader] Converting stream to ${codec}`);
return `${stream}→${codec}`;
}
}
class AudioMixer {
static fix(stream: string): string {
console.log(`[AudioMixer] Normalising audio`);
return `audio(${stream})`;
}
}
class MPEG4CompressionCodec {
readonly name = 'mp4';
}
class OggVorbisCodec {
readonly name = 'ogg';
}
// Facade — one clean interface for the whole subsystem
class VideoConverterFacade {
convert(filename: string, format: 'mp4' | 'ogg'): string {
console.log(`\n[Facade] Starting conversion of "${filename}" → .${format}`);
const file = new VideoFile(filename, 'avi');
const srcCodec = CodecFactory.extract(file);
const destCodec = format === 'mp4'
? new MPEG4CompressionCodec()
: new OggVorbisCodec();
let buffer = BitrateReader.read(file.filename, srcCodec);
buffer = BitrateReader.convert(buffer, destCodec.name);
buffer = AudioMixer.fix(buffer);
const outputFile = filename.replace(/\.\w+$/, `.${format}`);
console.log(`[Facade] Done → ${outputFile}\n`);
return outputFile;
}
}
// Client code — doesn't know or care about the subsystem
const converter = new VideoConverterFacade();
const mp4 = converter.convert('lecture.avi', 'mp4');
console.log('Output:', mp4);
/*
[Facade] Starting conversion of "lecture.avi" → .mp4
[CodecFactory] Detected codec: avi
[BitrateReader] Reading bitrate stream from lecture.avi
[BitrateReader] Converting stream to mp4
[AudioMixer] Normalising audio
[Facade] Done → lecture.mp4
Output: lecture.mp4
*/
const ogg = converter.convert('music.wav', 'ogg');
When to Use
- You want to provide a simple interface to a complex subsystem
- You want to layer a subsystem — the Facade is the entry point for most callers; power users bypass it
- You’re building an API over a library that’s complex or poorly documented
- You want to reduce dependencies: client code depends on one Facade instead of dozens of subsystem classes
Pros
- Isolates client code from subsystem complexity
- Reduces the number of objects clients deal with
- Easy to swap out the underlying subsystem — clients only see the Facade
- Promotes loose coupling between clients and subsystems
Cons
- A Facade can become a “god object” if it grows too large — keep it thin, delegate, don’t duplicate logic
- Doesn’t prevent clients from accessing the subsystem directly when needed (which is usually fine)
Related Patterns
- Adapter makes an existing interface usable; Facade creates a new, simpler interface
- Mediator mediates communication between objects; Facade mediates between client and subsystem
- Singleton — Facades are often implemented as Singletons (one entry point per subsystem)
- Abstract Factory can serve as a Facade for creating subsystem objects