Hello everyone, and welcome to .NET Core Central. In this blog post, I am going to walk through the facade pattern.
The facade design pattern is also one of the Gang Of Four design patterns. The facade design pattern is a structural design pattern.
The main intent of this pattern is to provide a unified interface instead of exposing a set of interfaces from a subsystem. When we get into the details it will be a little bit more clear what this sentence means.
But is important to understand that, in the above statement, the interface literally does not mean just the C# interface concept. An interface in this case is any set of interfaces to a particular subsystem. Which can be a service, it can be an interface, and so on and so forth.
The use case for Facade Pattern
The facade pattern defines a high-level interface that makes the underlying services easier to use. Now let us take an example and it will be clear what I mean by this statement.
Note:
The facade pattern is probably one of the easiest and simplest design patterns out there. And in a lot of cases, we use facade patterns without even knowing about it.
So let us take an example, let us say we have an e-commerce system. And the e-commerce system deals with buying some items.
And for buying items what all do we need?
- Firstly, we will need an inventory system that is going to be responsible for showing the inventory.
- Secondly, we will have a payment system that will be responsible for the payment processing.
- And then finally we might have a notification system that is responsible for sending notifications.
Interfaces and class details
I am going to use .NET 6.0 framework and C# language for this blog post.
So if we have to do that in the code, what we can do is create three new interfaces.
Inventory Service
The first interface will be IInventoryService
. For simplicity let’s consider it is a full-fledged inventory interface, which will provide all the functionality of an inventory. But in a real-life scenario, you’ll be probably breaking it out into inventory provider versus inventory search versus inventory creator, and so on and so forth.
For the time being, let us say we just have an inventory service. And the inventory service is going to have just one method. The method name will be GetAsync
and it will have a return type of string array
. And this method will be responsible for returning all inventory.
public interface IInventoryService
{
Task<string[]> GetAsync();
}
Next, as a part of the inventory service module, let us create the InventoryService
class. The InventoryService
class will implement the IInventoryService
interface. And for the implementation of the GetAsync
method, we will just return a hardcoded array of two string values.
internal class InventoryService : IInventoryService
{
public Task<string[]> GetAsync()
{
return Task.FromResult(new string[] {"Book", "Pen"});
}
}
Note:
The implementation here for this blog post is very simple for the purpose of demonstration.
Payment Service
Next, we will have another service called the payment service. Hence for that, we will create another interface IPaymentService
. And this interface will have one method PayAsync
, which will not return anything (just a Task
). And the PayAsync
method will take two parameters, double amount
and string item
.
public interface IPaymentService
{
Task PayAsync(double amount, string item);
}
Next, as a part of the payment service module, let us create the PaymentService
class. The PaymentService
class will implement the IPaymentService
interface. And for the implementation of the PayAsync
method, we will just write a message to the console using the Console.WriteLine
statement.
internal class PaymentService : IPaymentService
{
public Task PayAsync(double amount, string item)
{
Console.WriteLine($"Paying amount: {amount} for item: {item}");
return Task.CompletedTask;
}
}
Notification Service
Finally, we will have another interface which is INotificationService
. This interface is responsible for sending notifications. And this interface will have a single method SendAsync
, which will not return anything (just a Task
). The SendAsync
method will take a single parameter string message
.
public interface INotificationService
{
Task SendAsync(string message);
}
Next, as a part of the notification service module, let us create the NotificationService
class. The NotificationService
class will implement the INotificationService
interface. And for the implementation of the SendAsync
method, we will just write a message to the console using the Console.WriteLine
statement.
internal class NotificationService : INotificationService
{
public Task SendAsync(string message)
{
Console.WriteLine($"Sending notification message: {message}");
return Task.CompletedTask;
}
}
Note:
All of these methods from the interfaces are async methods because in a real-life scenario, they will be working with external services like sending emails, saving payments, and getting inventory. And these activities will be through calling an external service or data store, which will be done asynchronously.
But for the purpose of the blog, I am just going to do Console.WriteLine.
Controller Classes
After the business classes are ready, we will go ahead and create three different controllers representing the three business classes.
Payment controller
The first controller we will create is for payment purposes. So we will name this as PaymentController
. For creating the controller, we will use the API Controller with read/write actions
template.
Inside the controller, we will create a constructor. And in the constructor will inject the IPaymentService
interface as a parameter.
Next, I will provide an implementation for the Post
method of the controller. And I will delete all the methods that come with the template.
I will update the post method to take two parameters from the request body. The first one is the string item
and the second one is the double payment
. And inside the Post
method, I will just call the PayAsync
method of the IPaymentService
interface passing the amount and item.
using Microsoft.AspNetCore.Mvc;
namespace FacadePattern.Demo.Controllers;
[Route("api/[controller]")]
[ApiController]
public class PaymentController : ControllerBase
{
private readonly IPaymentService paymentService;
public PaymentController(IPaymentService paymentService)
{
this.paymentService = paymentService;
}
// POST api/<PaymentController>
[HttpPost]
public async Task Post([FromBody] string item, [FromBody] double amount)
{
await paymentService.PayAsync(amount, item);
}
}
Notification Controller
Similar to the PaymentController
, we will add another controller. And this controller also is going to be a controller for sending notifications. So we will name this controller as NotificationController
.
And for the NotificationController
as well, we will just have the Post
method. And next, we will create a constructor for the controller. And we will inject the INotificationService
interface as a parameter to the constructor.
And finally, we will update the parameter name of the Post
method from value
to message
. Inside of the Post
method, we will call the SendAsync
of the INotificationService
passing the incoming message
from the Post
method.
using Microsoft.AspNetCore.Mvc;
namespace FacadePattern.Demo.Controllers;
[Route("api/[controller]")]
[ApiController]
public class NotificationController : ControllerBase
{
private readonly INotificationService notificationService;
public NotificationController(INotificationService notificationService)
{
this.notificationService = notificationService;
}
// POST api/<NotificationController>
[HttpPost]
public async Task Post([FromBody] string message)
{
await notificationService.SendAsync(message);
}
}
InventoryController
The third and last controller is going to be the InventoryController
. And in the InventoryController
we are just going to have the Get
method, and we will delete the rest of the methods.
We will create a constructor for the InventoryController
. And we will inject the IInventoryService
interface as a parameter to the constructor.
Inside the Get
method, we will call the GetAsync
method of the IInventoryService
and return the string array.
using Microsoft.AspNetCore.Mvc;
namespace FacadePattern.Demo.Controllers;
[Route("api/[controller]")]
[ApiController]
public class InventoryController : ControllerBase
{
// GET: api/<InventoryController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
The case for Facade Pattern
So at this point in time, we have three controllers ready. And with the help of these three controllers, we can get all inventory, make payments, and send notifications.
Now consider that these services are accessed from your web application or a mobile client. As a part of the workflow, the client is first going to make a call to get all inventories using the InventryController
.
And then it will make a call to the PaymentController
to send payment. Finally, it will call the NotificationController
to send notifications.
The inventory call might be done twice, the first time when all the inventory is loaded. And a second time when someone buys something it will make the inventory call again to verify if the inventory is still there and then make a purchase through the PaymentController
.
This scenario causes two issues, the first issue is now the client has to make multiple calls. Secondly, there are too many granular-level services exposed to the client.
So how do we solve this? This is exactly the situation where the facade pattern should be used.
Purchase facade
At this point, we can create a new controller and we can call it as PurchaseFacadeController
. And the PurchaseFacadeController
is now going to act as a facade for all the other controllers. Hence, instead of calling individual controllers, the client can call the PurchaseFacadeController
to get all the functionality of a purchase.
In the PurchaseFacadeController
first, I will create a constructor. And in the constructor, I will inject IInventoryService
, IPaymentService
, and the INotificationService
as parameters.
Next, I will only keep the Post
method inside the controller. And I will update the Post
method to take the string item
and double amount
from the FormBody
.
Since the Post
method is going to be the facade for the rest of the services, hence,
- Firstly, we are going to call the
GetAsync
method from theIInventoryService
and get all inventories. - Secondly, it will check if the item sent in the request is available in the inventory. And if it does, it is going to call the
PayAsync
method of theIPaymentService
to pay for the item. - And finally, call the
SendAsync
method of theINotificationService
to send a notification.
using Microsoft.AspNetCore.Mvc;
namespace FacadePattern.Demo.Controllers;
[Route("api/[controller]")]
[ApiController]
public class PurchaseFacadeController : ControllerBase
{
private readonly IInventoryService inventoryService;
private readonly IPaymentService paymentService;
private readonly INotificationService notificationService;
public PurchaseFacadeController(IInventoryService inventoryService,
IPaymentService paymentService,
INotificationService notificationService)
{
this.inventoryService = inventoryService;
this.paymentService = paymentService;
this.notificationService = notificationService;
}
// POST api/<PurchaseFacadeController>
[HttpPost]
public async Task<IActionResult> Post([FromBody] string item, [FromBody] double amount)
{
var inventories = await inventoryService.GetAsync();
if (inventories.Any(i => i == item)) return BadRequest();
await paymentService.PayAsync(amount, item);
await notificationService.SendAsync($"Item: {item} purchased with amount: {amount}");
return Ok();
}
}
What we did here is that we created a facade, and now instead of calling three different controllers or three different Web APIs, the client can just call this facade and do the purchase as a part of a single call. And this is where the facade pattern comes in very handy.
Microservice scenario for Facade Pattern
The next question you might have is when working in a microservices environment, the inventory, notification, and payment are going to be different microservices. In that case, how will the facade pattern work?
There are two ways we can achieve this in the case of microservices:
- The first way to achieve this is the
PurchaseFacadeController
itself can be its own microservice. And it is the one that is going to expose thisPost
method and internally it will be making HTTPGet
andPost
calls to all the other three microservices and acts as a facade. - And the second way of doing this is to use an API gateway. And in the API gateway implementation, you can call these microservices from the API gateway through orchestration. And in this case, the API gateway will be the facade for the three microservices.
Conclusion
As I mentioned at the beginning of this blog post, you might be using this pattern very frequently. This pattern comes very naturally when you are building services using the SOLID design principles.
And if you are using microservices, there are a lot of scenarios where you will end up having a facade service through an API gateway to orchestrate your microservices. Or having another microservice to act as a facade.
A Youtube video for this implementation is available here.