intermediate~2h

Facade Pattern

Learn how the Facade pattern hides a multi-step coordination sequence across several subsystems behind one simple method, so client code stops needing to know every subsystem exists.

Learning objectives

  • Explain why duplicating a multi-service call sequence at every call site is a maintenance risk
  • Build a facade that owns construction of, and coordination across, several subsystems
  • Distinguish Facade's goal from Adapter's and Decorator's
  • Recognize when a facade is quietly turning into a god object

◆ Story

Watching a movie at home genuinely involves several separate systems: the projector has to turn on, the sound system has to switch to the right input, the lights have to dim, the streaming device has to start playing. A well-designed "Watch Movie" button on a universal remote hides every one of those individual steps behind a single press -- you don't need to understand or operate each subsystem separately to get the outcome you actually want.

This chapter builds that same idea around a small microservice scenario: three separate services -- UserService, OrderService, and PaymentService -- where a single client task, "show me the full order details," needs data pulled from all three.

◆ The problem

The straightforward approach has the client directly construct all three services and call each one in turn, combining their results itself. This runs fine and produces the right combined result -- so is it actually fine, if this exact three-call sequence needs to happen in a dozen different places across the codebase?

Written this way, the client is tightly coupled to three separate services -- it directly constructs and calls each one, a genuine dependency on the internal structure of the whole system, not just on the task it's actually trying to accomplish. The three calls, and the logic for combining their results, get duplicated everywhere this task is needed. And any internal change ripples outward: if a fourth microservice needs to be consulted, or the call order needs to change, every one of those call sites has to be found and updated by hand.

💻 Code example

class UserService { String getUserDetails(String userId) { return "User: " + userId; } } class OrderService { String getOrderDetails(String orderId) { return "Order: " + orderId; } } class PaymentService { String processPayment(String paymentId) { return "Payment: " + paymentId; } } class Client { public static void main(String[] args) { UserService userService = new UserService(); OrderService orderService = new OrderService(); PaymentService paymentService = new PaymentService(); String userDetails = userService.getUserDetails("u1"); String orderDetails = orderService.getOrderDetails("o1"); String paymentDetails = paymentService.processPayment("p1"); System.out.println(userDetails + ", " + orderDetails + ", " + paymentDetails); } }

The client's actual goal is "give me the full order details" -- one task -- not "know how to correctly call three separate services." Something should sit between the client and the three services, holding references to all of them and doing the coordination once, in one place. That something should expose one simple method that internally makes all three calls and combines the results, so the client calls that one method and never touches the individual services directly.

That's the Facade pattern: provide one simple, high-level method that internally coordinates a more complicated subsystem correctly, once -- callers interact with the simple facade, never with the individual subsystems directly.

APIGateway owns all three service fields, and its constructor is what actually takes over their construction -- the client will never call new UserService() itself again, anywhere. Its one coordinating method, getFullOrderDetails(), calls all three services in sequence and combines their results into a single return value. That's the only method most callers ever need to know about.

💻 Code example

class APIGateway { private final UserService userService; private final OrderService orderService; private final PaymentService paymentService; public APIGateway() { // the client will never call "new UserService()" again, anywhere this.userService = new UserService(); this.orderService = new OrderService(); this.paymentService = new PaymentService(); } public String getFullOrderDetails(String userId, String orderId, String paymentId) { String userDetails = userService.getUserDetails(userId); String orderDetails = orderService.getOrderDetails(orderId); String paymentDetails = paymentService.processPayment(paymentId); return userDetails + ", " + orderDetails + ", " + paymentDetails; // combined once, here } }

The client's entire job now shrinks to constructing one APIGateway and calling one method on it. Compare this to the naive version: the client no longer constructs UserService, OrderService, or PaymentService at all, and no longer needs to know they exist, what order to call them in, or how to combine their results.

If a fourth microservice needs to be consulted for "full order details" tomorrow, exactly one method -- APIGateway.getFullOrderDetails() -- needs updating, and every existing call site keeps working completely unchanged. Note that Facade doesn't actually hide the individual subsystems or prevent an advanced caller from reaching them directly when there's a genuine reason to -- userService, orderService, and paymentService still exist and could still be used directly. Facade just gives everyone who doesn't need that fine-grained control a simple, correct default path.

💻 Code example

class Client { public static void main(String[] args) { APIGateway gateway = new APIGateway(); String result = gateway.getFullOrderDetails("u1", "o1", "p1"); // one call, correct coordination guaranteed System.out.println(result); } }

▲ Edge case — a facade is a natural place for cross-cutting concerns

Because every call to any of the three services now funnels through APIGateway, this is exactly the right place to add logging, rate limiting, authentication checks, or caching, once, centrally, rather than duplicated inside each individual service. This is a genuine, common reason real API gateways exist as actual infrastructure, not just as a design pattern.

▲ Edge case — a facade can quietly turn into a god object

If every task the application ever needs gets added as another method on APIGateway, it can grow into a single, sprawling class responsible for coordinating everything in the system -- the same overload risk a Mediator faces if it accumulates too many unrelated responsibilities. Splitting into several smaller, more focused facades, one per genuine use case or client type, is the usual fix once a single facade starts accumulating unrelated responsibilities.

▲ Common mistake — expecting Facade to fix an incompatible interface

Facade's job is simplifying access to something already functionally correct but complicated to use directly. It's a genuinely different goal from Adapter, which exists to make an incompatible interface compatible, or Decorator, which exists to add new behavior. Reaching for Facade to solve an interface-mismatch problem, or for Adapter to solve a too-many-steps problem, is a common mix-up worth avoiding.

◆ Where this shows up

API gateways in a microservice architecture are the real-world version of this chapter's exact example -- a single entry point that coordinates calls to many backend services, so a client only ever needs to know about one address and one contract.

A Spring Boot @Service class is very often a facade in practice, even if nobody calls it that explicitly -- coordinating a repository call, a validation step, and an event-publishing call behind one simple placeOrder() method that a controller calls, hiding exactly the kind of multi-step coordination this chapter builds. Collections.unmodifiableList() and similar wrapper methods in the Java standard library serve a related simplifying role, presenting one straightforward view over more complex underlying behavior. Anywhere a system has several moving parts that almost always need to be used together in the same sequence, a facade is the pattern that turns that sequence into one dependable entry point.

Q: Does Facade prevent callers from accessing the underlying subsystems directly? A: No -- it just provides a simple default path for callers who don't need fine-grained control; the subsystems remain directly accessible for those who genuinely need it.

Q: How is Facade's goal different from Adapter's? A: Adapter makes an incompatible interface compatible; Facade simplifies access to something that's already functionally correct but complicated to use directly -- genuinely different problems.

Q: What specifically changed for the client between the naive version and the facade version? A: It stopped constructing or calling UserService, OrderService, and PaymentService directly -- it now only knows about APIGateway and its one getFullOrderDetails() method.

Q: Why is a facade a natural place to add logging or rate limiting? A: Because every call to the underlying services already funnels through it -- adding a cross-cutting concern there covers every caller at once, rather than duplicating it inside each individual service.

Q: What's the risk of a facade accumulating too many responsibilities over time? A: It can grow into a god object coordinating unrelated tasks across the whole system -- the same risk an overloaded Mediator faces -- usually fixed by splitting into several smaller, more focused facades.

Want a visual for this concept?

Generate a diagram tailored to “Facade Pattern” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Flyweight Pattern← Back to all Low-Level Design & Design Patterns chapters