The Facade pattern
“I have many subsystems, but I want one easy function that hides how messy they are.”

The Facade pattern is a classic design pattern used to provide a simple, unified API on top of one or more complex subsystems. In Rust, where types, modules, and ownership models naturally encourage small and well-defined components, it’s common for an application to accumulate several structs or modules that each handle their own responsibilities.

Over time, calling these components in the correct order, passing the right data between them, or coordinating their side-effects can become noisy and repetitive in the calling code. This is exactly where the Facade pattern shines.
Instead of exposing every subsystem directly to the rest of your application, you create a single struct—called a facade—that contains or coordinates those subsystems internally. The facade presents a small, ergonomic set of functions that represent higher-level operations. When the caller invokes a simple method like media.play_movie() or gateway.infer_request(), the facade executes all the required logic across multiple internal components: initializing a service, loading resources, validating input, triggering other services, cleaning up state, and so on. The caller never needs to know the details.
// --- Subsystem 1 ---
struct AudioSystem;
impl AudioSystem {
fn init(&self) { println!("Audio init"); }
fn play(&self) { println!("Audio play"); }
}
// --- Subsystem 2 ---
struct VideoSystem;
impl VideoSystem {
fn load(&self) { println!("Video load"); }
fn render(&self) { println!("Video render"); }
}
// --- FACADE ---
struct MediaFacade {
audio: AudioSystem,
video: VideoSystem,
}
impl MediaFacade {
fn new() -> Self {
Self {
audio: AudioSystem,
video: VideoSystem,
}
}
// the simplified, unified API
fn play_movie(&self) {
self.audio.init();
self.video.load();
self.audio.play();
self.video.render();
println!("Movie playing 🎬");
}
}
// --- Client code ---
fn main() {
let media = MediaFacade::new();
media.play_movie(); // only ONE call needed
}
A common misconception is that the Facade pattern is similar to using an enum of implementations (like the Strategy pattern). But a facade doesn’t choose between different implementations. Instead, it wraps and orchestrates multiple components behind a single, easy-to-use API. You use Strategy when you want to switch one of many algorithms; you use Facade when you want to hide complexity.
For Rust developers, the Facade pattern fits nicely with module organization, dependency injection via struct fields, and encapsulation enforced by pub or pub(crate) visibility. It keeps your calling code clean, predictable, and expressive—especially in large systems such as microservices, AI gateways, game engines, or data pipelines, where many parts need to cooperate but don’t all need to be exposed.
// --- Subsystem 1 ---
struct AudioSystem;
impl AudioSystem {
fn init(&self) { println!("Audio init"); }
fn play(&self) { println!("Audio play"); }
}
// --- Subsystem 2 ---
struct VideoSystem;
impl VideoSystem {
fn load(&self) { println!("Video load"); }
fn render(&self) { println!("Video render"); }
}
// --- FACADE ---
struct MediaFacade {
audio: AudioSystem,
video: VideoSystem,
}
impl MediaFacade {
fn new() -> Self {
Self {
audio: AudioSystem,
video: VideoSystem,
}
}
// the simplified, unified API
fn play_movie(&self) {
self.audio.init();
self.video.load();
self.audio.play();
self.video.render();
println!("Movie playing 🎬");
}
}
// --- Client code ---
fn main() {
let media = MediaFacade::new();
media.play_movie(); // only ONE call needed
}
Credit to Alexander Shvets, Refactoring.Guru for letting me have a free copy of his ebook a while ago – it’s a great resource for design patterns…
