Facade โ Structural design pattern with UML diagrams, Java implementation, and real-world examples.
Structural
Facade Pattern
Provide a unified, simplified interface to a complex subsystem. A foundational structural pattern — the front desk of every well-designed library, SDK, and service layer.
You are building an e-commerce checkout. To place a single order you must: (1) validate the user’s session with AuthService, (2) check stock via InventoryService, (3) calculate tax and shipping with PricingEngine, (4) charge the card through PaymentGateway, (5) create the order record in OrderRepository, and (6) fire a confirmation via NotificationService. Six subsystem classes, each with its own API, its own error handling, its own configuration. Every controller, every test, every new feature that touches checkout must orchestrate all six — in the right order, with the right error recovery.
Naive approach — client orchestrates every subsystem directly
// โ Controller couples to 6 subsystem classespublicOrderResultplaceOrder(OrderRequest req) {
Session s = authService.validate(req.getToken()); // 1
inventoryService.reserve(req.getItems()); // 2Price price = pricingEngine.calculate(req.getItems(),
s.getAddress()); // 3ChargeResult charge = paymentGateway.charge(
s.getPaymentMethod(), price.getTotal()); // 4Order order = orderRepo.create(s.getUserId(),
req.getItems(), charge.getTxnId()); // 5
notificationService.sendConfirmation(
s.getEmail(), order); // 6return newOrderResult(order);
}
// โ Every controller / test / new feature repeats these 6 steps// โ Change the order? Add fraud check? Edit EVERY caller.
What goes wrong:
Massive coupling — every client (REST controller, CLI handler, scheduled job) depends on 6 internal classes. Change one subsystem API and every caller breaks.
Duplicated orchestration — the 6-step sequence is copy-pasted across controllers. A bug fix in step 4 must be applied everywhere.
Exposed complexity — the client must know which services exist, what order to call them, and how to handle each service’s errors. Internal structure leaks outward.
Hard to test — unit-testing the controller means mocking 6 collaborators. Add a 7th service? Every test file changes.
Open/Closed violation — adding a fraud-check step means editing every caller, not just one place.
Without Facade — every client wires the subsystem directly
This is the problem Facade solves — introduce a single CheckoutFacade class that owns the 6-step orchestration. Every client calls facade.placeOrder(request). The clients know one class instead of six. Add a fraud check? Edit the facade. Change the order of steps? Edit the facade. All callers benefit automatically.
02
Section Two ยท The Pattern
What Is Facade?
Facade is a structural pattern that provides a simplified, unified interface to a set of interfaces in a subsystem. It defines a higher-level interface that makes the subsystem easier to use. The key insight: the facade doesn’t replace the subsystem — it wraps it. Clients can still access the subsystem directly for advanced use cases, but most callers only ever talk to the facade.
GoF Intent: “Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.” — Gamma, Helm, Johnson, Vlissides (1994)
Analogy — hotel concierge: You arrive at a hotel in a foreign city. You need a restaurant reservation, airport transfer, theatre tickets, and a wake-up call. You could call each vendor yourself — figure out the restaurant’s phone number, negotiate with the taxi dispatcher in a language you don’t speak, navigate the theatre’s booking website. Or you call the concierge. One person, one phone number, one language. The concierge knows which restaurant to call, which taxi company is reliable, and how to get last-minute tickets. You say “dinner for two at 8, airport pickup at 6 AM,” and it’s done. The concierge is the facade — one simple interface to a complex subsystem of vendors. The vendors still exist; the concierge just saves you from knowing their details.
Key insight: Facade is about simplification through aggregation. It doesn’t add behaviour, doesn’t transform interfaces, doesn’t add layers of indirection for extensibility. It takes N subsystem classes and exposes them as M high-level methods (where M « N). The facade knows which subsystem objects to delegate to and in what order. The client knows only the facade.
The Facade class holds references to subsystem objects and exposes simplified methods
▶
Clients depend on one class instead of many. Coupling drops from N to 1.
Each facade method orchestrates a multi-step workflow across subsystem classes
▶
The orchestration logic lives in one place. Change the workflow โ edit the facade โ all callers benefit.
The subsystem classes remain accessible — the facade doesn’t hide them
▶
Power users bypass the facade when they need fine-grained control. The facade is a convenience, not a cage.
The facade carries no business logic of its own — it only delegates and coordinates
▶
It stays thin. If the facade grows complex, it’s a sign you need to refactor the subsystem, not the facade.
With Facade — clients talk to one class
Facade vs. Adapter vs. Mediator:
All three “sit between” clients and subsystems, but for different reasons.
Adapter converts one interface to another (1:1 translation).
Facade simplifies many interfaces into one (N:1 simplification).
Mediator manages complex interactions between subsystem objects so they don’t reference each other (N:N coordination).
Facade is the simplest of the three — it’s just convenience delegation with no protocol or interface translation.
03
Section Three ยท Anatomy
Participants & Structure
Participant
Role
In the Analogy
Facade
A class that exposes a few high-level methods. Internally, each method delegates to the appropriate subsystem objects in the right order. Knows which subsystem classes to call, when, and how to translate results. Carries no business logic of its own.
The hotel concierge — one person who coordinates restaurants, taxis, theatres on your behalf.
Subsystem Classes
The existing classes that do the real work: AuthService, InventoryService, PricingEngine, PaymentGateway, OrderRepository, NotificationService. They are unaware of the facade — they have no reference to it. They can be used directly by power users who need fine-grained control.
The vendors — restaurants, taxi companies, theatres. They exist independently and don’t know the concierge exists.
Client
Any code that needs the subsystem’s functionality. Instead of coupling to N subsystem classes, it depends only on the facade. Calls one method (facade.placeOrder(req)) and gets the complete result.
The hotel guest — makes one request to the concierge instead of calling six vendors.
Facade — UML Class Diagram
The facade is not a God Object:
A well-designed facade has a small number of high-level methods that cover the most common use cases.
It doesn’t expose every method of every subsystem class — that would just be a wrapper, not a simplification.
If your facade has 40 methods, you don’t have a facade; you have a forwarding layer.
Keep it to 3–8 methods that represent use cases, not subsystem APIs.
04
Section Four ยท How It Works
The Pattern In Motion
Scenario: A REST controller needs to place an order. Instead of wiring 6 subsystem services, it calls one method on the facade. The facade orchestrates the entire workflow internally.
Step 1 — Client calls facade.placeOrder(request)
▶
The client passes a simple DTO with items, token, and address. It knows nothing about the internal services. One method, one dependency.
Step 2 — Facade delegates to AuthService.validate(token)
▶
The facade retrieves the authenticated session (userId, email, payment method, address). If the token is invalid, the facade throws early — no further services are called.
Step 3 — Facade delegates to InventoryService.reserve(items)
▶
The facade reserves stock for all requested items. If any item is out of stock, the facade throws before charging the card — order of operations matters, and the facade owns that order.
Step 4 — Facade delegates to PricingEngine.calculate(items, address)
▶
Tax, shipping, and discounts are computed. The facade passes the session’s address (from Step 2) to pricing (Step 4) — it wires the data flow between services that don’t know about each other.
Step 5 — Facade delegates to PaymentGateway.charge(method, total)
▶
The card is charged. If payment fails, the facade releases the inventory reservation (compensating action). This error-recovery logic lives in the facade, not in every caller.
Step 6 — Facade delegates to OrderRepository.create() and NotificationService.send()
▶
The order is persisted, the confirmation email is fired. The facade returns an OrderResult to the client. Six services orchestrated; the client saw one method call.
Notice how the facade calls auth before inventory, inventory before pricing, pricing before payment.
This ordering is a policy decision.
If you later need to add a fraud check between steps 3 and 4, you edit one method in one class.
Every client — REST controller, CLI, batch job — gets the fraud check for free.
This is the core value of Facade: centralised workflow coordination.
05
Section Five ยท Java Stdlib
You Already Use This
Facade is the most common structural pattern in Java — any class that wraps a complex subsystem behind a simple API is a facade.
IN JAVA
Example 1javax.faces.context.FacesContext — the canonical Java EE facade. Behind it sit the ExternalContext, Application, ViewRoot, ResponseWriter, NavigationHandler, and dozens more. Instead of touching 10+ objects, a JSF managed bean calls FacesContext.getCurrentInstance() and accesses everything through one class. Classic N:1 simplification.
Example 2java.net.URL / java.net.HttpURLConnection — making an HTTP request involves DNS resolution, socket creation, TLS handshake, header formatting, chunked transfer encoding, redirect following, and response parsing. URL.openConnection() gives you a single object that hides all of this. You call getInputStream() and read bytes — the entire networking subsystem is behind the facade.
Example 3java.util.concurrent.Executors — creating a thread pool requires configuring core pool size, max pool size, keep-alive time, work queue type, thread factory, and rejection policy. Executors.newFixedThreadPool(4) hides all of that behind a one-liner. It’s a facade (via factory methods) over ThreadPoolExecutor’s 7-parameter constructor.
Example 4org.springframework.jdbc.core.JdbcTemplate — raw JDBC requires: get connection from DataSource, create PreparedStatement, set parameters, execute, iterate ResultSet, map rows, close ResultSet, close Statement, close Connection, handle SQLExceptions at every step. JdbcTemplate.query(sql, mapper) does it all in one call. The most-used facade in the Spring ecosystem.
Stdlib usage — Executors as a Facade over ThreadPoolExecutor
// โ Without facade โ 7-parameter constructorExecutorService pool = newThreadPoolExecutor(
4, // corePoolSize4, // maximumPoolSize0L, TimeUnit.MILLISECONDS, // keepAliveTimenewLinkedBlockingQueue<>(), // workQueueExecutors.defaultThreadFactory(),
newThreadPoolExecutor.AbortPolicy()
);
// โ With facade โ one call, same resultExecutorService pool = Executors.newFixedThreadPool(4);
Stdlib usage — JdbcTemplate as a Facade over raw JDBC
// โ Without facade โ 15 lines of boilerplateConnection conn = dataSource.getConnection();
try {
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setLong(1, userId);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
// map each row manually
}
rs.close(); ps.close();
} catch (SQLException e) { /* handle */ }
finally { conn.close(); }
// โ With facade โ one callUser user = jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
newBeanPropertyRowMapper<>(User.class),
userId
);
Spring is built on facades:
JdbcTemplate (JDBC), RestTemplate / WebClient (HTTP), JmsTemplate (messaging), MongoTemplate (MongoDB), RedisTemplate (Redis) — Spring’s entire “Template” family is facades over complex subsystems.
Each hides connection management, error handling, and resource cleanup behind a handful of high-level methods.
If you’ve used any Spring Template, you’ve used Facade.
06
Section Six ยท Implementation
Build It Once
Domain: Home Theatre System. A HomeTheatreFacade that orchestrates an amplifier, DVD player, projector, screen, and lights to provide one-call watchMovie() and endMovie() methods.
The facade receives all subsystem objects through its constructor.
This makes it easy to test (inject mocks), easy to configure (Spring/DI wires it), and keeps the facade free of “new” calls.
The facade coordinates; it doesn’t create.
07
Section Seven ยท Watch Out
Common Mistakes
Mistake #1 — The God Facade (too many methods): A facade should expose 3–8 high-level use-case methods (placeOrder, cancelOrder, getOrderStatus). If your facade has 40 methods that mirror every subsystem method, you don’t have a facade — you have a pass-through wrapper that adds no simplification. Fix: If the facade is growing, split it into multiple focused facades: CheckoutFacade, ReportingFacade, AdminFacade.
✗ Wrong — facade that mirrors every subsystem method
// โ This is not a facade โ it's a forwarding layerclassOrderFacade {
voidvalidateToken(String t) { auth.validate(t); } // just forwardingvoidreserveItems(List items) { inventory.reserve(items); } // just forwardingvoidcalculatePrice(...) { pricing.calculate(...); } // just forwardingvoidchargeCard(...) { payment.charge(...); } // just forwarding// ... 20 more pass-through methods// Client still must call them in the right order!
}
✔ Correct — facade that provides use-case methods
// โ Facade hides the workflow โ one method per use caseclassCheckoutFacade {
OrderResultplaceOrder(OrderRequest req) {
// orchestrates auth โ inventory โ pricing โ payment โ persist โ notify// client calls ONE method; facade owns the sequence
}
voidcancelOrder(String orderId) { /* reverse workflow */ }
OrderStatusgetStatus(String orderId) { /* query workflow */ }
}
Mistake #2 — Putting business logic in the facade: The facade should coordinate, not compute. If your facade is calculating tax rates, validating credit card numbers, or applying discount rules, that logic belongs in the subsystem classes (PricingEngine, PaymentGateway). A facade with business logic becomes a monolith that’s hard to test and impossible to reuse.
✔ Correct — facade delegates computation to subsystem
// โ Facade delegates to PricingEngine โ no business logic hereclassCheckoutFacade {
OrderResultplaceOrder(OrderRequest req) {
Session s = auth.validate(req.getToken());
Price price = pricing.calculate(req.getItems(), s.getAddress()); // โ delegate!
payment.charge(s.getPaymentMethod(), price.getTotal());
// ...
}
}
Mistake #3 — Making the facade the only way in:
A facade should be a convenience, not a prison.
Don’t make subsystem classes package-private or otherwise inaccessible just because you have a facade.
Power users, batch jobs, and admin tools may need direct access to InventoryService or PaymentGateway for operations the facade doesn’t cover.
The facade reduces complexity for the common case; it doesn’t eliminate the subsystem API.
Mistake #4 — Confusing Facade with Adapter:
Adapter converts one incompatible interface to another (1:1 translation).
Facade simplifies many interfaces into one (N:1 simplification).
If you’re wrapping a single class to change its method signatures, that’s Adapter.
If you’re wrapping 6 classes to provide a single placeOrder(), that’s Facade.
The test: does the wrapper reduce the number of classes the client depends on? If yes โ Facade.
If it just renames methods โ Adapter.
08
Section Eight ยท Decision Guide
When To Use Facade
Use Facade When
You have a complex subsystem with many classes and the client only needs a few high-level operations — order placement, report generation, user onboarding
You want to decouple clients from a subsystem’s internal structure — so internal refactoring doesn’t ripple outward
You need a single entry point for a multi-step workflow where the order of operations matters (auth โ validate โ charge โ persist โ notify)
You’re building a library or SDK and want a simple “getting started” API that covers 80% of use cases, while still exposing lower-level classes for power users
Multiple clients (REST controller, CLI, scheduler, tests) all need the same orchestration — centralise it in one facade instead of duplicating it
Avoid Facade When
The subsystem is already simple — wrapping one or two classes behind another class adds indirection with no simplification
You need to convert an interface (1:1 method translation) — that’s Adapter, not Facade
You need subsystem objects to communicate with each other through a central hub — that’s Mediator
You need to add behaviour (logging, caching, security) to an existing interface — that’s Decorator or Proxy
Clients always need fine-grained control — if every caller bypasses the facade, it serves no purpose
Facade vs. Related Patterns
Pattern
Purpose
Wraps?
When to pick it
Facade ← this
Simplify N classes into 1
Many objects (N:1)
Complex subsystem, common workflows
Adapter
Convert interface A โ B
One object (1:1)
Incompatible interface you can’t modify
Mediator
Coordinate N objects
Knows all peers (N:N)
Objects need to interact but shouldn’t reference each other
Proxy
Control access to an object
One object (same interface)
Lazy loading, access control, caching
Decorator
Add behaviour to same interface
One object (same interface)
Extend functionality without subclassing
Decision Flowchart
09
Section Nine ยท Practice
Problems To Solve
Facade problems test whether you can identify the subsystem boundary, design a minimal facade API, keep business logic out of the facade, and handle error/compensating actions in the workflow.
Difficulty
Problem
Key Insight
Easy
Computer Startup Facade A computer has a CPU (freeze(), jump(addr), execute()), Memory (load(position, data)), and HardDrive (read(sector, size)). To boot, you must: freeze CPU, load boot sector from HD into memory, jump CPU to boot address, execute. Build a ComputerFacade with a single start() method that orchestrates the boot sequence.
The simplest Facade exercise: 3 subsystem classes, 1 facade method, fixed sequence. Tests whether you can centralise a multi-step startup into one call and keep the facade free of logic (just delegation + ordering).
Easy
File Converter Facade Converting a file involves: FileReader.read(path) โ byte[], Decoder.decode(bytes, srcFormat) โ Document, Encoder.encode(doc, targetFormat) โ byte[], FileWriter.write(path, bytes). Build a FileConverterFacade with convert(srcPath, srcFmt, destPath, destFmt) that chains the 4 subsystems.
Tests data piping through a facade — the output of one subsystem becomes the input to the next. The facade wires I/O between classes that don’t know about each other. Also tests proper resource cleanup (close reader/writer) in the facade.
Medium
Travel Booking Facade A travel system has: FlightService (search(), book(), cancel()), HotelService (search(), reserve(), cancel()), CarRentalService (findCars(), rent(), cancel()), and PaymentService (charge(), refund()). Build a TravelFacade with bookTrip() that books all three + charges once. If any booking fails mid-way, the facade must roll back previous bookings (compensating actions).
Tests error handling and compensating transactions in a facade. If the hotel books but the car fails, the facade must cancel the flight and hotel before throwing. This is the real-world challenge of facades — coordinating rollback across multiple services. Demonstrates that the facade owns not just the happy path but the failure path too.
Medium
Report Generation Facade Generating a monthly report involves: DataWarehouse.query(sql), AggregationEngine.aggregate(data, groupBy), ChartRenderer.render(aggregated) โ Image, PdfBuilder.create(title, tables, charts) โ byte[], EmailService.send(to, subject, attachment). Build a ReportFacade with generateAndSend(reportType, recipient). The facade should select the right SQL query and groupBy based on reportType (monthly, quarterly, annual) without embedding business logic — use a ReportConfig lookup instead.
Tests a facade that needs configuration-driven behaviour without putting business logic in the facade itself. The facade looks up the right config and passes it to subsystem objects. Also tests chaining 5 subsystems where each step produces input for the next.
Hard
Cloud Deployment Facade Deploying an application involves: ContainerRegistry (build & push Docker image), KubernetesClient (create/update Deployment, Service, Ingress), DnsManager (update DNS records), CertManager (provision TLS certificate), MonitoringService (create dashboards & alerts), SlackNotifier (announce deployment). Build a DeploymentFacade with deploy(appName, version, env) and rollback(appName, env). The facade must: handle partial failures with rollback, support dry-run mode (log actions without executing), and allow optional subsystems (monitoring and Slack may not be configured).
Tests an advanced facade with: compensating rollback on partial failure, dry-run/preview mode, optional subsystems (null-safe or feature-flagged), and environment-specific configuration (staging vs. production). This mirrors real-world infrastructure facades where not all subsystems are always available and failures mid-deployment require careful unwinding.
Interview Tip:
When asked about Facade, the interviewer wants to see: (1) a clear subsystem boundary — name the classes the facade wraps; (2) a small API with 2–5 use-case methods, not a pass-through wrapper; (3) no business logic in the facade — it delegates and coordinates; (4) awareness that JdbcTemplate, Executors, and FacesContext are real-world Java facades.
Stand-out answers mention error handling / compensating actions in the facade, and distinguish Facade (N:1 simplification) from Adapter (1:1 translation), Mediator (N:N coordination), and Proxy (same interface, access control).