Facade Pattern
Facade hides complexity of a multi-class subsystem behind a small, purpose-built API.
Introduction
Facade hides complexity of a multi-class subsystem behind a small, purpose-built API. Adapter changes one interface to match expectations; Facade collapses many interfaces into one. Checkout becomes CheckoutFacade.placeOrder(cart) — inventory, pricing, tax, payment, fulfillment, notification orchestrated in one place.
The story
A team's place-order controller grew to 240 lines orchestrating six subsystems. New features regressed because every endpoint reimplemented parts of the flow. Extracting CheckoutFacade turned controllers into 8-line shells and gave QA one class to integration-test.
The business problem
Scattered orchestration duplicates logic and hides the entry point:
- Controller bloat: API layers orchestrate six+ subsystems per request.
- Duplicated cross-cutting: audit, metrics, retries copied at every call site.
- Onboarding cost: newcomers must learn ten classes to do one user action.
- Regression risk: subtle ordering differences between endpoints.
The problem teams faced
Facade fits when:
- Consumers orchestrate many subsystem calls in a specific order.
- Cross-cutting steps (audit, metrics, error handling) duplicate across callers.
- Subsystem internals leak into UI/API layers.
- Multiple clients replicate the same orchestration with subtle differences.
Understanding the topic
Intent: Provide a unified, higher-level interface to a set of interfaces in a subsystem.
- Facade — single class with domain-named operations (placeOrder, refund).
- Subsystem classes — each does one job; facade composes them.
- Client — uses Facade only for common path; subsystems stay accessible for power users.
Internal architecture
Checkout Facade structure:
class CheckoutFacade {constructor(private inventory: InventoryService,private pricing: PricingService,private tax: TaxService,private payments: PaymentGateway,private fulfillment: FulfillmentService,private notifier: NotificationService,) {}async placeOrder(cart: Cart): Promise<OrderReceipt> {await this.inventory.reserve(cart)const priced = this.pricing.total(cart)const taxed = this.tax.apply(priced)const receipt = await this.payments.charge(taxed)await this.fulfillment.schedule(cart)await this.notifier.confirm(receipt)return receipt}}
Visual explanation
Three diagrams cover Facade orchestration, vs god class, and DDD mapping:
Informative example
Before / after — 240-line controller to Facade:
// ❌ Controller orchestrates everything — duplicated across endpointsapp.post("/orders", async (req, res) => {const cart = req.bodyawait inventoryService.reserve(cart)const priced = pricingService.total(cart)const taxed = taxService.apply(priced)// ... 200 more lines, metrics, audit scattered})// ✅ Thin controller + Facadeapp.post("/orders", async (req, res) => {const cart = validateCart(req.body)const receipt = await checkoutFacade.placeOrder(cart)res.json(receipt)})// Facade owns orchestration + cross-cutting onceclass CheckoutFacade {async placeOrder(cart: Cart) {return this.tracer.trace("placeOrder", async () => {// orchestrate subsystems in correct order})}}
Execution workflow
Find orchestration
Request paths touching multiple subsystems in order.
Real-world use
DDD application services / use cases are Facades. JdbcTemplate hides JDBC; EntityManager hides JPA; jQuery $.ajax hid XHR; AWS SDK clients hide signing/retry/serialization.
Production case study
Telecom billing platform:
- Scenario: UI made 27 microservice calls per "view bill" page.
- Challenge: Each frontend reinvented orchestration with subtle differences.
- Decision: BillingFacade service owns orchestration; UI calls one endpoint.
- Outcome: p95 page load 2.1s → 480ms; Android and partner portal shipped without reinventing logic.
Trade-offs
- Pro: single entry point; centralized cross-cutting concerns.
- Pro: subsystems independently replaceable behind stable API.
- Con: can grow into god-class without per-use-case split.
- Con: risk of transaction script hiding real domain modelling.
Decision framework
- Use when multiple consumers replicate same orchestration.
- Use when publishing stable API while subsystems churn internally.
- Avoid when one subsystem and one consumer — direct call is fine.
- Avoid hiding richness clients legitimately need — provide Facade + raw access.
Best practices
- One Facade per use case — CheckoutFacade, RefundFacade, not MegaFacade.
- Facade delegates; domain services own business rules.
- Centralize spans, audit events, and retry policy in Facade methods.
- Integration-test the Facade — it's the contract clients depend on.
Anti-patterns to avoid
- God Facade with business logic and 2000 lines.
- Facade that seals subsystems — forces workarounds.
- Facade per technical layer instead of per user intent.
Common mistakes
- Extracting Facade without tests — orchestration bugs ship silently.
- Putting domain rules in Facade — belongs in domain layer.
- One Facade for entire platform — split by bounded context.
Debugging tips
- Single span around Facade method — see full orchestration in traces.
- Compare Facade call order across endpoints when behaviour diverges.
Optimization strategies
- Parallelize independent subsystem calls inside Facade where safe.
- Cache read-heavy Facade aggregations with TTL.
Common misconceptions
- Facade ≠ Mediator. Facade routes to subsystems; Mediator coordinates peers that talk back.
- Most DDD application services are Facades — recognize the pattern.
- Facade ≠ API Gateway at network scale — same idea, different boundary.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1IntermediateQuestionFacade vs god class?+
Answer
Follow-up
2AdvancedQuestionHide subsystem classes or front them?+
Answer
Follow-up
3IntermediateQuestionFacade helps cross-cutting how?+
Answer
Follow-up
4AdvancedQuestionDDD name for Facade?+
Answer
Follow-up
Summary
You can extract Facades from fat controllers, split by use case, and centralize cross-cutting concerns. Tell the 240-line controller → CheckoutFacade story — if you can do that, you own Facade.