Welcome to today’s post.
In today’s post I will be discussing another software design pattern which is the Façade design pattern.
What is the façade design pattern?
The façade design pattern is a structural design pattern which allows us to encapsulate multiple backend services into one interface that can be more easily accessible and useable by developers to provide an interface for end users. None of the backend services need to be aware of the other services, only the façade needs to be aware of the backend services.
Examples of Facade Pattern Usage
It does not concern us how we integrate the backend services into a common interface. The net objective is the same to combine multiple backend services into one service that simplifies access to the backend services.
Examples of facades are below:
- A travel booking system which has backend booking, product, customer, security, and payment modules. The façade is a web site that allows customers to book holidays in a user-friendly way by allowing registration, product searches, checking product availability, booking products, and making payments.
- A banking system which has backend accounting, customer, and security modules. The façade is an ATM machine that allows signing in, menu selection, account selection, and actions (get balance, withdraw, transfer).
- A library loan system which as backend catalogue, loan, membership, and security modules. The façade is an online library booking system which allows members to sign in, search for a book from the catalogue, then borrow a book.
In each case, the above facades are interfaces or gateways for customers to access the backend services in a user-friendly manner. There is no direct access from a client to any backend services. All access is via an intermediate interface that encapsulates complex backend services. The additional benefit is providing new developers a public interface that can be understood and used to develop new features for the client interface with a lower learning curve. From an abstract view the façade pattern resembles the following class diagram:
In our library loan booking system façade, we have the following functions:
- Sign In
- Search for a book from the catalogue.
- Borrow a book.
- Return a book.
- Leave a review for a loaned book.
If we include too many services within the containing class, then we could violate the Single Responsibility Principle of SOLID Principles, so careful design decisions will need to be employed when including classes as members of the container class.
In the above set of functions, I did not include the sign-in function as part of the set of services within the façade. We could include the sign-in function within an identity service which passes a token to a web API method or controller that validates the token before calling methods within the BookService class.
We could also have included a logging service within the set of services within the façade. Like an identity service, a logging service is a non-functional aspect of the application and façade container class. User authentication provided by an identity service can be restricted from the API or controller level, whereas logging can be injected into the façade class. In terms of business logic, it does not add to the number of separate business services within the façade.
In the next section I will show how to use the Façade design pattern within a service class.
Applying the Facade Design Pattern
To be able to use these services we can implement the dependencies for the façade as follows.
We create a class that is extended by an interface that contains method signatures that correspond to the exposed functions of the included services.
The interface is shown below:
namespace BookLoan.Services
{
public interface IBookService
{
Task<BookViewModel> SearchBooks(string title)
Task<LoanRequestResponse> BorrowBook(LoanViewModel request)
Task<ReviewRequestResponse> ReviewBook(int LoanId, string comment)
}
}
In the member section of the class, we declare private interfaces for each business class that we would like to include in the façade. The way we do this is shown below:
public class BookingService: IBookingService
{
private readonly IBookService _bookService;
private readonly ILoanService _loanService;
private readonly IReviewService _reviewService;
…
}
The reason why we declare the interfaces as private is so that access to each service is through member methods within the façade, and not directly through direct member or property accessors.
The above has established the services we will use in our façade class. This is very similar to the concept of composition in object-oriented programming, except we are declaring injectable interfaces instead of instances as class members.
To set instances of the interfaces we use class constructor parameters, which are resolved to instances through dependency injection as shown:
public BookingService(
IBookService bookService,
ILoanService: loanService
IReviewService reviewService)
{
_bookService = bookService;
_loanService = loanService;
_reviewService = reviewService;
}
Below is the implementation of the member methods that access each of the composed services in the façade:
namespace BookLoan.Services
{
public class BookingService: IBookingService
{
private readonly IBookService _bookService;
private readonly ILoanService _loanService;
private readonly IReviewService _reviewService;
public BookingService(
IBookService bookService,
ILoanService: loanService
IReviewService reviewService)
{
_bookService = bookService;
_loanService = loanService;
_reviewService = reviewService;
}
public async Task<BookViewModel> SearchBooks(string title)
{
var books = await _bookService.SearchBooks(title);
...
return books;
}
public async Task<LoanRequestResponse> BorrowBook(LoanViewModel request)
{
var result = await _loanService.BorrowBook(request.BookId, request.UserId);
...
return result;
}
public async Task<ReviewRequestResponse> ReviewBook(int LoanId, string comment)
{
var loanResponse = await _reviewService.ReviewBook(LoanId, comment);
...
return loanResponse;
}
}
}
In the next section we will look at the benefits and pitfalls of using the façade pattern.
Benefits and Pitfalls of using the Facade Pattern
As we can see with the façade pattern and the sample façade above, the pattern encourages encapsulation of dependencies however this can end up violating the SOLID single responsibility principle with many dependent classes overloading the features of the façade. There must be care taken not to overload too many business service dependencies into the same façade.
Should we include multiple dependent services, validations, and logging then the façade would certainly be overloaded in functionality. If there are many tasks encapsulated in the façade, the interface segregation principle could be violated. This does not mean that that design pattern does not have a useful purpose in any developers’ repertoire, however it requires disciplined use to stay as close to achieving the SOLID principles.
Ensuring that any additional libraries including cross cutting (non-functional) concerns like logging, exception, security and so forth are properly abstracted into their own classes before being injected into the façade can avoid breaking the principle. Embedding code into a façade direct from custom libraries would violate the clean code separation of concerns principle and make the code less testable. Keeping in mind these principles of SOLID and sticking to clean code principles of DRY, SoC, refactoring and so on will give a successful implementation.
That is all for today’s post.
I hope you found this post useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.