Facade provides a simpler interface over a subsystem that is noisy, fragmented, or too detailed for most callers. That makes it a natural fit for application services and orchestration boundaries.


Example Problem

Checkout requires multiple subsystems:

  • inventory reservation
  • payment authorization
  • shipping initiation
  • invoice generation

Most callers should not coordinate these pieces manually.


UML

sequenceDiagram
    participant C as Client
    participant F as CheckoutFacade
    participant I as InventoryService
    participant P as PaymentService
    participant S as ShippingService
    participant V as InvoiceService
    C->>F: checkout(command)
    F->>I: reserveItems()
    F->>P: authorizePayment()
    F->>S: createShipment()
    F->>V: generateInvoice()
    F-->>C: CheckoutResult

Implementation Walkthrough

public final class CheckoutFacade {
    private final InventoryService inventoryService;
    private final PaymentService paymentService;
    private final ShippingService shippingService;
    private final InvoiceService invoiceService;

    public CheckoutFacade(InventoryService inventoryService,
                          PaymentService paymentService,
                          ShippingService shippingService,
                          InvoiceService invoiceService) {
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
        this.shippingService = shippingService;
        this.invoiceService = invoiceService;
    }

    public CheckoutResult checkout(CheckoutCommand command) {
        inventoryService.reserve(command.getItems());
        String paymentRef = paymentService.authorize(command.getOrderId(), command.getAmount());
        String shipmentId = shippingService.createShipment(command.getOrderId(), command.getAddress());
        String invoiceId = invoiceService.generate(command.getOrderId(), command.getAmount());
        return new CheckoutResult(command.getOrderId(), paymentRef, shipmentId, invoiceId);
    }
}

Usage stays simple:

CheckoutResult result = checkoutFacade.checkout(command);

The facade is useful because the client now interacts with checkout as one application-level action instead of four subsystem calls. That reduces duplication in callers and gives you one place to define sequencing, failure semantics, and request-level observability.


Why Facade Helps

It reduces coupling at the caller side. Clients interact with one high-level use case instead of four lower-level services.

It also creates a natural place for:

  • orchestration rules
  • transaction boundaries
  • compensating actions
  • request-level logging

In real systems, this is also the right layer to make compensation rules explicit. If payment succeeds but shipment creation fails, the facade is the natural place to define whether the system should roll back, retry, or mark the workflow for asynchronous recovery.


Common Mistake

Facade should simplify access, not become the home for every business rule in the system. If the class keeps growing, split by use case instead of creating one mega-facade.