Design PatternsBeginner 11 min Lesson 7 of 13

Facade — One Simple Front Door

Give a complicated set of classes one simple entry point, so common tasks take one call instead of ten.

Design Patterns · Lesson 7 of 13
0/13 done(0%)

What is it? #

A Facade provides one simple interface in front of a complicated subsystem.

The subsystem stays exactly as it is. The facade just offers the common operations as single, well-named calls, so callers do not need to know the sequence or the internal classes.

It is the lightest structural pattern and one of the most immediately useful. Any time a routine task requires six objects in a specific order, a facade turns it into one method.

It does not restrict access. Callers who need the detailed control can still use the subsystem directly.

Think of it like this #

A hotel front desk. Behind it are housekeeping, the kitchen, maintenance and the booking system. You ask for a late checkout and the desk coordinates whoever needs to know.

You could phone each department yourself, and occasionally a specialist request needs that. For everything ordinary, one conversation is enough.

Simple example #

Placing an order touches inventory, payments, shipping, invoicing and notifications, in that order, with rollback if payment fails. Every caller repeating that sequence is both tedious and dangerous.

Code #

PYTHON
# --- The subsystem: several focused classes, unchanged ---
class Inventory:
    def reserve(self, sku, qty): print(f"reserved {qty} x {sku}"); return True
    def release(self, sku, qty): print(f"released {qty} x {sku}")


class Payments:
    def charge(self, customer, amount): print(f"charged {amount}"); return "txn_1"
    def refund(self, txn): print(f"refunded {txn}")


class Shipping:
    def schedule(self, order_id, address): print(f"shipment for {order_id}"); return "shp_1"


class Invoices:
    def create(self, order_id, amount): print(f"invoice for {order_id}"); return "inv_1"


class Notifications:
    def order_confirmed(self, email, order_id): print(f"emailed {email}")


# --- The facade: one call for the common case ---
class OrderPlacement:
    def __init__(self, inventory, payments, shipping, invoices, notifications):
        self.inventory = inventory
        self.payments = payments
        self.shipping = shipping
        self.invoices = invoices
        self.notifications = notifications

    def place(self, order) -> dict:
        self.inventory.reserve(order["sku"], order["qty"])
        try:
            txn = self.payments.charge(order["customer"], order["total"])
        except Exception:
            self.inventory.release(order["sku"], order["qty"])   # undo on failure
            raise

        shipment = self.shipping.schedule(order["id"], order["address"])
        invoice = self.invoices.create(order["id"], order["total"])
        self.notifications.order_confirmed(order["email"], order["id"])
        return {"transaction": txn, "shipment": shipment, "invoice": invoice}


placement = OrderPlacement(Inventory(), Payments(), Shipping(), Invoices(), Notifications())
result = placement.place({
    "id": "A-1", "sku": "P-9", "qty": 2, "total": 1998.0,
    "customer": "cust_7", "email": "[email protected]", "address": "Pune",
})
print(result)

# The subsystem is still available directly when something unusual is needed:
# Inventory().reserve("P-9", 50)
TEXT
When to use it
  - a common workflow requires several objects in a specific order
  - callers keep duplicating that sequence
  - you want one place that owns the ordering and the rollback

When NOT to use it
  - the subsystem is already one or two calls
  - the facade grows into a god class holding all the logic
  - you use it to hide a design problem rather than to simplify usage

How it works #

The subsystem classes stay small and focused. Inventory knows about stock, payments know about money. None of them knows about the ordering workflow.

OrderPlacement owns the sequence. It knows that stock must be reserved before charging, and that a failed charge must release the reservation.

That rollback is the strongest argument for the facade. When the sequence is duplicated across five callers, one of them will forget the release and stock will leak. Centralising it means the rule exists once.

The facade holds no business rules of its own. It does not calculate prices or validate addresses; it coordinates the components that do.

Its dependencies arrive through the constructor, so tests can pass stubs and assert that the right things were called in the right order.

The last comment matters: the subsystem remains accessible. A facade is a convenience, not a wall. An admin tool doing something unusual can still call Inventory directly.

Real-world use #

Most "service" classes in application code are facades. UserRegistrationService, CheckoutService, ReportService — each coordinates several lower-level components for one workflow.

Library authors use them constantly. Python's requests.get() is a facade over connection pooling, header handling, redirects and encoding.

Infrastructure code benefits too. A deployment facade might coordinate building an image, pushing it, updating configuration and running a health check.

The failure mode to watch for is the facade absorbing the logic it was meant to coordinate. When the facade has 600 lines and the subsystem classes are anaemic, the responsibilities have drifted into the wrong place.

Common mistakes #

  • Letting the facade accumulate business logic instead of delegating.
  • Adding one facade method per subsystem method, which only adds a layer.
  • Making the subsystem private so legitimate advanced use becomes impossible.
  • Forgetting rollback in a multi-step workflow, leaving partial state behind.
  • Creating a facade over something that is already simple.

Practice #

Write a UserOnboarding facade coordinating account creation, password hashing, welcome email and audit logging, with rollback if the email fails. Then write a test using stub components that asserts the account is removed when the email step raises.

Quick quiz

  1. 1. What does a Facade provide?

  2. 2. Why is centralising a multi-step workflow valuable?

  3. 3. Should the subsystem remain directly accessible?

  4. 4. What is the main failure mode of a facade?

  5. 5. Which is an example of a facade in a library?

Summary

  • A facade gives one simple call for a common multi-step workflow.
  • The subsystem stays unchanged and directly usable.
  • Centralising ordering and rollback prevents duplicated mistakes.
  • Facades coordinate; they should not hold the business rules.
  • Most application service classes are facades.