LLD - Facade Design Pattern

Manish PushkarManish Pushkar
5 min read

Facade Design Pattern

The Facade Design Pattern is a structural pattern that provides a simplified, unified interface to a set of complex subsystems.

Blueprint of Facade Design pattern

  • Facade → Simplified interface to the subsystem

  • Subsystem classes → Actual code implementation, unaware of Facade

  • Client → Can access either Facade or Subsystem classes

Example 1: Making Payments

Let’s say a client who wants to process a payment might need to go through these steps:

Encrypt data → Call payments API → Handle retries & logging

This makes every client do these steps = Duplication + Complexity

Before Facade:

encrypt(data);
doPayment(…)
log(…)
retry(…)

Solution with Facade: ✅

  • Encapsulate this orchestration

  • Expose simple method - paymentFacade.makePayment(...) that handles all the steps

Example 2: Starting a Car

Without Facade:
Client is handling low level tasks

public class Driver {
    public static void main(String[] args) {
        Engine engine = new Engine();
        Lights lights = new Lights();
        AC ac = new AC();

        engine.start();
        lights.turnOn();
        ac.turnOn();
    }
}

With Facade: ✅
- Client is simplified
- Subsystems are encapsulated

// Subsystems
class Engine {
    void start() { System.out.println("Engine started"); }
}

class Lights {
    void turnOn() { System.out.println("Lights on"); }
}

class AC {
    void turnOn() { System.out.println("AC running"); }
}

// Facade
public class CarFacade {
    private Engine engine = new Engine();
    private Lights lights = new Lights();
    private AC ac = new AC();

    public void startCar() {
        engine.start();
        lights.turnOn();
        ac.turnOn();
    }
}

// Client
public class Driver {
    public static void main(String[] args) {
        CarFacade car = new CarFacade();
        car.startCar();
    }
}

Case Study - Hotel Booking System

In a typical hotel booking flow, multiple steps are involved:

  1. Check room availability

  2. Verify user identity

  3. Process the payment

  4. Book the room

  5. Send booking confirmation

Solution - Using Facade Pattern

  • Create sub system classes that perform each task

  • Introduce a Hotel Booking Facade class to orchestrate the process

  • Expose a single method bookHotel(…) to the client

Github - Facade Design Pattern Code

Subsystems:

// AvailabilityService.java
public class AvailabilityService {
    public boolean isRoomAvailable(String roomType) {
        System.out.println("Checking availability for room: " + roomType);
        return true; // Dummy logic
    }
}

// PaymentService.java
public class PaymentService {
    public boolean processPayment(String paymentDetails) {
        System.out.println("Processing payment with details: " + paymentDetails);
        return true; // Dummy logic
    }
}

// KYCService.java
public class KYCService {
    public boolean verifyUser(String userId) {
        System.out.println("Verifying user KYC for ID: " + userId);
        return true; // Dummy logic
    }
}

// BookingService.java
public class BookingService {
    public String bookRoom(String userId, String roomType) {
        System.out.println("Booking room for user " + userId + " with type: " + roomType);
        return "BOOKING12345"; // Dummy booking ID
    }
}

// NotificationService.java
public class NotificationService {
    public void sendConfirmation(String userId, String bookingId) {
        System.out.println("Sending confirmation to user " + userId + " for booking: " + bookingId);
    }
}

Facade:

// HotelBookingFacade.java
public class HotelBookingFacade {

    private AvailabilityService availabilityService = new AvailabilityService();
    private PaymentService paymentService = new PaymentService();
    private KYCService kycService = new KYCService();
    private BookingService bookingService = new BookingService();
    private NotificationService notificationService = new NotificationService();

    public void bookHotel(String userId, String roomType, String paymentDetails) {
        System.out.println("Starting hotel booking process...");

        if (!availabilityService.isRoomAvailable(roomType)) {
            System.out.println("Room not available!");
            return;
        }

        if (!kycService.verifyUser(userId)) {
            System.out.println("User verification failed!");
            return;
        }

        if (!paymentService.processPayment(paymentDetails)) {
            System.out.println("Payment failed!");
            return;
        }

        String bookingId = bookingService.bookRoom(userId, roomType);
        notificationService.sendConfirmation(userId, bookingId);

        System.out.println("Booking successful! Booking ID: " + bookingId);
    }
}

Client:

// HotelBookingApp.java
public class HotelBookingApp {
    public static void main(String[] args) {
        HotelBookingFacade bookingFacade = new HotelBookingFacade();

        String userId = "USER-001";
        String roomType = "Deluxe";
        String paymentDetails = "CreditCard: 1234-5678-9101-5678";

        bookingFacade.bookHotel(userId, roomType, paymentDetails);
    }
}

Output:

Starting hotel booking process...
Checking availability for room: Deluxe
Verifying user KYC for ID: USER-001
Processing payment with details: CreditCard: 1234-5678-9101-5678
Booking room for user USER-001 with type: Deluxe
Sending confirmation to user USER-001 for booking: BOOKING12345
Booking successful! Booking ID: BOOKING12345

Multiple Facade

Lets say you're booking international travel:

  • You use a TravelFacade that:

    • Books your flight using FlightBookingFacade

    • Books your hotel using HotelBookingFacade

    • Schedules a visa appointment using VisaFacade

Each sub-facade encapsulates its own complexity, and the top-level TravelFacade simply coordinates them.

public class TravelFacade {

    private FlightBookingFacade flightBookingFacade = new FlightBookingFacade();
    private HotelBookingFacade hotelBookingFacade = new HotelBookingFacade();
    private VisaProcessingFacade visaFacade = new VisaProcessingFacade();

    public void bookInternationalTrip(String userId, String destination, String paymentDetails) {
        System.out.println("Booking international trip to " + destination + " for " + userId);

        visaFacade.applyVisa(userId, destination);
        flightBookingFacade.bookFlight(userId, destination, paymentDetails);
        hotelBookingFacade.bookHotel(userId, "Deluxe", paymentDetails);

        System.out.println("International trip booked successfully!");
    }
}

Example Use Case: Apache Commons IO - File Utilities

Problem Without Facade:
Reading, copying, writing, or monitoring files requires dealing with low-level Java I/O (Streams, Readers, Buffers), which is verbose and error-prone.

How Facade Helps:
FileUtils and IOUtils from Apache Commons IO provides simple one-liner methods like copyFile(...), readFileToString(...), etc., hiding all the stream/reader/exception complexity.

Facade:
Static utility classes in Apache Commons IO that wrap low-level stream operations.

Impact:
Reduces boilerplate, improves readability, and encourages safe file handling practices.

Benefits

  • Cleaner client code

  • Hides complexity

  • Easier testing and mocking

  • Promotes maintainability

Drawbacks

  • Risk of becoming a god object if not designed well

  • Over-abstraction can obscure useful methods

  • Doesn’t eliminate complexity — just hides it

Facade vs Mediator?

FacadeMediator
Client → Facade → SubsystemsColleagues → Mediator → Other Colleagues
Simplifies client usageCoordinates communication between components
Doesn't manage subsystem interaction between componentsCentral controller that manages communication

Facade vs Adapter?

FacadeAdapter
Simplifies usage by providing a unified, high-level interfaceConverts one interface to another so incompatible types can work together
Hide complexity of subsystemsMake two incompatible interfaces compatible
When you have a complex system with multiple componentsWhen you need to work with an external class or legacy API with a different interface

Facade vs Proxy?

FacadeProxy
Simplify and unify access to a complex subsystemControl access to an object (add extra behavior, delay, or restrict access)
Hide complexity and reduce couplingAdd control (e.g., caching, logging, access control, lazy loading)
Client knows only the Facade, not the internal componentsClient often doesn’t realize it's talking to a proxy instead of the real object
0
Subscribe to my newsletter

Read articles from Manish Pushkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Manish Pushkar
Manish Pushkar

Software Engineer - Backend