Design Patterns Tutorial 0/70 lessons ~6 min read Lesson 25

    Facade Pattern

    Facade hides complexity of a multi-class subsystem behind a small, purpose-built API.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    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:

    ts
    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:

    Facade orchestration
    Client / Controller
    Thin shell
    CheckoutFacade
    placeOrder()
    Subsystem calls
    Inv · Tax · Pay
    Cross-cutting
    Audit · metrics
    One domain-named operation replaces scattered orchestration.
    Facade vs god class
    Delegates only?
    Facade OK
    Implements logic?
    God class risk
    One use case?
    Split facades
    Thin orchestration
    Domain owns rules
    Facade orchestrates; domain services own business rules.
    One facade per use case
    placeOrder
    CheckoutFacade
    refundOrder
    RefundFacade
    cancelOrder
    CancelFacade
    Not one mega-facade
    SRP applies
    Split by user intent — not one class for entire subsystem.

    Informative example

    Before / after — 240-line controller to Facade:

    ts
    // ❌ Controller orchestrates everything — duplicated across endpoints
    app.post("/orders", async (req, res) => {
    const cart = req.body
    await inventoryService.reserve(cart)
    const priced = pricingService.total(cart)
    const taxed = taxService.apply(priced)
    // ... 200 more lines, metrics, audit scattered
    })
    // ✅ Thin controller + Facade
    app.post("/orders", async (req, res) => {
    const cart = validateCart(req.body)
    const receipt = await checkoutFacade.placeOrder(cart)
    res.json(receipt)
    })
    // Facade owns orchestration + cross-cutting once
    class CheckoutFacade {
    async placeOrder(cart: Cart) {
    return this.tracer.trace("placeOrder", async () => {
    // orchestrate subsystems in correct order
    })
    }
    }

    Execution workflow

    1Extract Facade workflow
    1 / 5

    Find orchestration

    Request paths touching multiple subsystems in order.

    Git blame on fat controllers.

    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.

    4 questions
    1IntermediateQuestionFacade vs god class?+

    Answer

    Facade exposes high-level ops and delegates. God class implements business logic itself. Split by use case before Facade becomes new monolith.

    Follow-up

    Heuristic to spot slide?
    2AdvancedQuestionHide subsystem classes or front them?+

    Answer

    Front them. Power users need direct access for admin/batch. Facade simplifies common path, doesn't seal alternatives.

    Follow-up

    Prevent back-door overuse?
    3IntermediateQuestionFacade helps cross-cutting how?+

    Answer

    Concentrate metrics/audit in Facade method — one span around placeOrder beats fragmented subsystem spans.

    Follow-up

    Subsystem already emits spans?
    4AdvancedQuestionDDD name for Facade?+

    Answer

    Application service or use case. Orchestrates domain + infra for one user intent; transaction boundary.

    Follow-up

    What lives in domain layer?

    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.

    Ready to mark this lesson complete?Track your journey across the entire course.