Facade Design Pattern

Imagine a car engine!
When you want to control the engine as a driver, you get into the divers seat. But, when you want to work on it as a mechanic, you open its bonnet.
Even when we are talking about the same engine, it has exposed two separate facades for two different types of users.

Its a structural design pattern that provides context specific interfaces to simplify and make the integration of a complex system easy for the clients.

A complex system may consists of multiple sub-systems. But, we need not expose everything, expecting the clients to figure out what they need. Instead we can identify what the client needs from our system to build an interface, so that it provides a simplified view of our system. We call such a context specific interface as a facade.

A facade may provide many key advantages while exposing a complex systems as listed below.

Benefits of using Facades
  1. Provides simplified views of the our system, matching to their usage
    • Since different user groups have different views, we may create multiple facades to meet their needs. For instance an admin and a normal user can have their own facades.
    • In case there are too many features to accomodate, we may break our facades using logical groups.
  2. Provides the flexibility to redesign by decoupling the sub-systems from the clients
  3. Enable us to simplify our usage by creating higher order services
    • Because of the facade, we can combine multiple dependent calls to the sub-systems to build higher order services.
    • Without a facade, we may force the clients to build these services on their own.
      • In case of remote services, it would also affect the performance due to higher number of network calls.

In the section below we look at these points with an example to figure out how it really works.

How does the pattern work?

Lets say we have a core banking system with so many re-usable service modules. The web app, mobile app, desktop or the ATM, all the clients use these services provided by different modules.

Now lets say we want to expose these services for our ATM client. Out of so many features provided by these modules, our ATM needs only a specific set. Hence, as shown in the figure, we can either let the client access the services directly or use it through a simplified interface.

First of all, with the option-1, the client needs to unnecessarily understand all our internal modules. For example, for getting the approved list of payees for fund transfer purpose, the client has to figure out if it is there in customer profile service or fund transfer service. Both are a close possibility.

Secondly, once the clients figure it out and use it, we can no longer redesign without affecting the clients.

But, a facade can solve both of the above problems for us. With a context specific interface, we reduce the complex system to something which is just enough to meet our ATM client needs. And, since the clients refer only through this interface, we are free to change our internal design as we need.

Source Code for the Demo

Below is the demo code for showing the design and significance of a facade that we have discussed above.

1. The first set has the facade and the related services, showing how does a facade hide the internal design with it’s concise interface.

The inline comments highlights the key points along the code.

package spectutz.dp.struct.facade;
import java.util.List;
import spectutz.dp.struct.facade.services.CustomerProfileService;
import spectutz.dp.struct.facade.services.FundTransferService;
import spectutz.dp.struct.facade.services.SavingsAccountService;
import spectutz.dp.struct.facade.vo.Account;

//We have used minimal code and interfaces to keep the size small and focus on the concept
public class ATMUserServiceFacade implements IATMUserServiceFacade {
	CustomerProfileService customerProfileService = new CustomerProfileService();
	FundTransferService fundTransferService = new FundTransferService();
	SavingsAccountService savingsAccountService = new SavingsAccountService();
	
	//A facade is mostly an indirection that provides 
	//a usage specific simplified view of the underlying complex system. 
	
	@Override
	public boolean withdrawCash(String accountId, float amount) {
		return savingsAccountService.withdrawCash(accountId, amount);
	}
	@Override
	public boolean depositeFunds(String accountId, float amount) {
		return savingsAccountService.depositeFunds(accountId, amount);
	}
	@Override
	public float checkBalance(String accountId) {
		return savingsAccountService.checkBalance(accountId);
	}
	@Override
	public List<Account> getApprovedPayeeList(String accountId) {
		return customerProfileService.getApprovedPayeeList(accountId);
	}
	@Override
	public boolean transferFunds(String fromAccId, String toAccId, float amount) {
		return fundTransferService.transferFunds(fromAccId, toAccId, amount);
	}

}
package spectutz.dp.struct.facade;

import java.util.List;

import spectutz.dp.struct.facade.vo.Account;

//A simple demo facade from ATM user's point of view
public interface IATMUserServiceFacade {
	public boolean withdrawCash(String accountId, float amount);
	public boolean depositeFunds(String accountId, float amount);
	public float checkBalance(String accountId);
	public List<Account> getApprovedPayeeList(String accountId);
	public boolean transferFunds(String fromAccId, String toAccId, float amount);
}
package spectutz.dp.struct.facade.services;
import java.util.ArrayList;
import java.util.List;
import spectutz.dp.struct.facade.vo.Account;

public class CustomerProfileService {
	public List<Account> getApprovedPayeeList(String accountId){
		List<Account> approvedPayees = new ArrayList<Account>();
		approvedPayees.add(new Account("SA4684","Dave Muskan"));
		approvedPayees.add(new Account("SA4684","Tommy Pehelwan"));
		
		System.out.println("Returning approved payee list.");
		return approvedPayees;
	}
}
package spectutz.dp.struct.facade.services;

public class FundTransferService {
	 
	public boolean transferFunds(String fromAccId, String toAccId,float amount) {
		SavingsAccountService savingsAccountService = new SavingsAccountService();
		
		if(savingsAccountService.withdrawCash(fromAccId, amount)) {
			System.out.println("Initiate fund transfer to account id :"+fromAccId);
			return true;
		}else {
			System.out.println("Insufficient balance.");
			return false;
		}		
	}
}
package spectutz.dp.struct.facade.services;

public class SavingsAccountService {
	public boolean withdrawCash(String accountId, float amount) {
		if (checkBalance(accountId) > amount) {
			System.out.println("Amount debited  - " + amount);
			return true; // approved
		} else {
			System.out.println("Not enough balance !");
			return false;// failed
		}
	};

	public float checkBalance(String accountId) {
		return 50000;// dummy amount
	};

	public boolean depositeFunds(String accountId, float amount) {
		System.out.println("Amount desposited :" + amount);
		return true;// successful
	}

}
package spectutz.dp.struct.facade.vo;

public class Account {
	private String id;
	private String name;
	//Other details
	public Account(String id,String name) {
		this.id=id;
		this.name=name;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

2. This one only has the client code with it’s output, majorly showing the call flow that simplifies the system for the client.

package spectutz.dp.struct.facade;

public class ATMApplicationClientDemo {

	IATMUserServiceFacade atmService = new ATMUserServiceFacade();

	public void withdrawCash(String accountId, float amount) {
		System.out.println("Inside ATMApplicationClientDemo.withdrawCash()");

		boolean isApproved = atmService.withdrawCash(accountId, amount);
		if (isApproved) {
			System.out.println("Disburse the cash.");
		} else {
			System.out.println("Show the error message.");
		}
	}

	public static void main(String[] args) {
		ATMApplicationClientDemo demo = new ATMApplicationClientDemo();

		System.out.println("Call flow : ATM Client --> Facade --> Actual Service\n");
		demo.withdrawCash("SA53762", 2000);
	}
}

//Output showing call flow
/*
Call flow : ATM Client --> Facade --> Actual Service

Inside ATMApplicationClientDemo.withdrawCash()
Inside ATMUserServiceFacade.withdrawCash()
Inside SavingsAccountService.withdrawCash()
Amount debited  - 2000.0
Disburse the cash. 
*/
Comparing with other patterns

1. Facade Vs Adapter :
An adapter looks similar to a facade in terms of design, but they are quite different in their intents.

  • A client builds an adapter for addressing the compatibility concerns. Whereas a system builds a facade for providing simpler views to its clients.
  • An adapter manages the changes in the underlying system. In contrast, a facade provides the flexibility to re-design the underlying system.

2. Facade Vs Mediator:
A facade may also look similar to behavioral pattern called, the mediator. But, again the way they decouple multiple components from the clients is quite different.

  • First of all, a facade simplifies a group of systems whereas a mediator simplifies a group of interactions.
  • Second, facade is mostly an indirection to a feature whereas a mediator holds the business logic or a feature.
    • Thus we can think of a facade hiding a group of mediators under it from its clients.
    • For example, the facade talks to FundTransferService which is a mediator that:
      • might be talking to CustomerProfileService for verifying the limits
      • the SavingsAccountService for withdrawal and
      • a third party transfer service for initiating the transfer.

Conclusion

As we have noticed, a facade provides a simplified view irrespective of the underlying design. Its a great way to server multiple user groups from a common set of reusable assets. We use it to serve only that is meaningful and hiding the rest.

In case of microservice environments, integration tools like Apache Camel, API gateways can help us build such facades on top of independent reusable services.