Facade Pattern in Dart: Simplify Complex Subsystems
The facade pattern in Dart lets you wrap a messy subsystem — auth, cache, analytics, storage — behind one simple interface. Here's a modern Dart 3 take with typed results and a cache-then-network example.
The best class you'll write this year might be the one that does almost nothing. Most facade pattern Dart tutorials stop at the video-converter; this guide is more boring and more useful. A facade is a single class that stands in front of a messy subsystem — auth, caching, analytics, storage — and gives the app one clean doorway in. No clever algorithms, just delegation done well. Yet most Flutter codebases never build one, and the cost is visible: the same five service calls copy-pasted into every screen, error handling that changes with the author's mood, and a new teammate needing a week to learn which classes wire a login. Here we build a modern facade in Dart 3.13 — typed results, cache-then-network, the works. What you'll build in this guide A plain-English understanding of what a facade is (and what it is not) A Result type using Dart 3 sealed classes, so failures are values — not exceptions A SessionFacade that coordinates auth, cache, network, and analytics behind one method A cache-then-network flow running through the facade, with runnable output A decision table for facade vs adapter vs plain dependency injection The four mistakes that turn facades into the "God objects" everyone warns about All code targets Dart 3.13 (ships with Flutter 3.47 ) and runs with plain dart run — no packages needed. The problem: talking to the subsystem directly Imagine a login screen. To sign a user in and show their profile, the widget needs four collaborators: an AuthService , a UserCache , a UserApi , and Analytics . Without a facade, the screen (or its view-model) talks to all four directly: // The subsystem, called directly: every caller repeats this dance. final userId = await authService.signIn(email, password); if (userId == null) {…