Blogs
Facade Design Pattern

Facade Design Pattern

The Facade design pattern provides a unified interface to a set of interfaces in a subsystem. This pattern defines a higher-level interface that makes the subsystem easier to use.

I’ve dealt with major codebases where there’s large number of subsystems, and when I understood this pattern - it was easy to identify when this pattern was being used.

Participants

  • Facade (MortgageApplication): knows which subsystem classes are responsible for a request. delegates client requests to appropriate subsystem objects.
  • Subsystem classes (Bank, Credit, Loan): implement subsystem functionality. handle work assigned by the Facade object. They have no knowledge of the facade and keep no reference to it.

Intuitive Implementation:

C#
1
/// <summary>
2
/// Facade Design Pattern
3
/// </summary>
4
public class Program
5
{
6
static void Main()
7
{
8
Facade facade = new Facade();
9
facade.MethodA();
10
facade.MethodB();
11
12
// Wait for user
13
Console.ReadKey();
14
}
15
}
16
17
/// <summary>
18
/// The 'Subsystem ClassA' class
19
/// </summary>
20
public class SubSystemOne
21
{
22
public void MethodOne()
23
{
24
Console.WriteLine(" SubSystemOne Method");
25
}
26
}
27
28
/// <summary>
29
/// The 'Subsystem ClassB' class
30
/// </summary>
31
public class SubSystemTwo
32
{
33
public void MethodTwo()
34
{
35
Console.WriteLine(" SubSystemTwo Method");
36
}
37
}
38
39
/// <summary>
40
/// The 'Subsystem ClassC' class
41
/// </summary>
42
public class SubSystemThree
43
{
44
public void MethodThree()
45
{
46
Console.WriteLine(" SubSystemThree Method");
47
}
48
}
49
50
/// <summary>
51
/// The 'Subsystem ClassD' class
52
/// </summary>
53
public class SubSystemFour
54
{
55
public void MethodFour()
56
{
57
Console.WriteLine(" SubSystemFour Method");
58
}
59
}
60
61
/// <summary>
62
/// The 'Facade' class
63
/// </summary>
64
public class Facade
65
{
66
private readonly SubSystemOne one;
67
private readonly SubSystemTwo two;
68
private readonly SubSystemThree three;
69
private readonly SubSystemFour four;
70
71
public Facade()
72
{
73
one = new SubSystemOne();
74
two = new SubSystemTwo();
75
three = new SubSystemThree();
76
four = new SubSystemFour();
77
}
78
79
public void MethodA()
80
{
81
Console.WriteLine("\nMethodA() ---- ");
82
one.MethodOne();
83
two.MethodTwo();
84
four.MethodFour();
85
}
86
87
public void MethodB()
88
{
89
Console.WriteLine("\nMethodB() ---- ");
90
two.MethodTwo();
91
three.MethodThree();
92
}
93
}
Output
1
MethodA() ----
2
SubSystemOne Method
3
SubSystemTwo Method
4
SubSystemFour Method
5
6
MethodB() ----
7
SubSystemTwo Method
8
SubSystemThree Method

Practical Example

Implement a MortgageApplication object which provides a simplified interface to a large subsystem of classes measuring the creditworthiness of an applicant.

Create a customer:

C#
1
public class Customer
2
{
3
public string Name { get; set; }
4
}

Create your subsystems:

C#
1
/// <summary>
2
/// The 'Subsystem ClassA' class
3
/// </summary>
4
public class Bank
5
{
6
public bool HasSufficientSavings(Customer c, int amount)
7
{
8
Console.WriteLine("Check bank for " + c.Name);
9
return true;
10
}
11
}
12
13
/// <summary>
14
/// The 'Subsystem ClassB' class
15
/// </summary>
16
public class Credit
17
{
18
public bool HasGoodCredit(Customer c)
19
{
20
Console.WriteLine("Check credit for " + c.Name);
21
return true;
22
}
23
}
24
25
/// <summary>
26
/// The 'Subsystem ClassC' class
27
/// </summary>
28
public class Loan
29
{
30
public bool HasNoBadLoans(Customer c)
31
{
32
Console.WriteLine("Check loans for " + c.Name);
33
return true;
34
}
35
}

Create the Facade class:

C#
1
/// <summary>
2
/// The 'Facade' class
3
/// </summary>
4
public class Mortgage
5
{
6
private readonly Bank bank = new Bank();
7
private readonly Loan loan = new Loan();
8
private readonly Credit credit = new Credit();
9
10
public bool IsEligible(Customer cust, int amount)
11
{
12
Console.WriteLine("{0} applies for {1:C} loan\n",
13
cust.Name, amount);
14
15
bool eligible = true;
16
17
// Check creditworthyness of applicant
18
if (!bank.HasSufficientSavings(cust, amount))
19
{
20
eligible = false;
21
}
22
else if (!loan.HasNoBadLoans(cust))
23
{
24
eligible = false;
25
}
26
else if (!credit.HasGoodCredit(cust))
27
{
28
eligible = false;
29
}
30
return eligible;
31
}
32
}

Run it:

C#
1
static void Main()
2
{
3
// Facade
4
Mortgage mortgage = new Mortgage();
5
6
// Evaluate mortgage eligibility for customer
7
Customer customer = new() { Name = "Giovany" };
8
bool eligible = mortgage.IsEligible(customer, 125000);
9
10
Console.WriteLine("\n" + customer.Name +
11
" has been " + (eligible ? "Approved" : "Rejected"));
12
13
// Wait for user
14
Console.ReadKey();
15
}
Output
1
Giovany applies for $125,000.00 loan
2
3
Check bank for Giovany
4
Check loans for Giovany
5
Check credit for Giovany
6
7
Giovany has been Approved

Practical Example

Say we have a Web API where we might have multiple services/controllers.

Service/InventoryService.cs
1
public interface IInventoryService
2
{
3
string[] Get();
4
}
5
6
internal class InventoryService : IInventoryService
7
{
8
public string[] Get()
9
{
10
return new[] { "Book", "Pen" };
11
}
12
}
Service/INotificationService.cs
1
public interface INotificationService
2
{
3
void Send(string message);
4
}
5
6
internal class NotificaitonService : INotificationService
7
{
8
public void Send(string message)
9
{
10
Console.WriteLine($"Sending notification message: {message}");
11
}
12
}
Service/IPaymentService.cs
1
public interface IPaymentService
2
{
3
void Pay(double amount, string item);
4
}
5
6
internal class PaymentService : IPaymentService
7
{
8
public void Pay(double amount, string item)
9
{
10
Console.WriteLine($"Paying amount: {amount} for item: {item}");
11
}
12
}
Program.cs
1
using Practice.Service;
2
3
var builder = WebApplication.CreateBuilder(args);
4
5
// Add services to the container.
6
builder.Services.AddSingleton<INotificationService, NotificaitonService>();
7
builder.Services.AddSingleton<IPaymentService, PaymentService>();
8
builder.Services.AddSingleton<IInventoryService, InventoryService>();
9
10
builder.Services.AddControllers();
11
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
12
builder.Services.AddEndpointsApiExplorer();
13
builder.Services.AddSwaggerGen();
14
15
var app = builder.Build();
16
17
// Configure the HTTP request pipeline.
18
if (app.Environment.IsDevelopment())
19
{
20
app.UseSwagger();
21
app.UseSwaggerUI();
22
}
23
24
app.UseHttpsRedirection();
25
26
app.UseAuthorization();
27
28
app.MapControllers();
29
30
app.Run();
Dtos.cs
1
public record SubmitPaymentDto(string Item, double Amount);
2
3
public record SubmitNotificationDto(string Value);

Now, let’s define our subsystems:

Controllers/InventoryController.cs
1
[ApiController]
2
[Route("api/[controller]")]
3
public class InventoryController : ControllerBase
4
{
5
private readonly IInventoryService m_inventoryService;
6
7
public InventoryController(IInventoryService m_inventoryService)
8
{
9
this.m_inventoryService = m_inventoryService;
10
}
11
12
[HttpGet]
13
public IEnumerable<string> Get()
14
{
15
return m_inventoryService.Get();
16
}
17
}
Controllers/NotificationController.cs
1
[ApiController]
2
[Route("api/[controller]")]
3
public class NotificationController : ControllerBase
4
{
5
private readonly INotificationService m_notificationService;
6
7
public NotificationController(INotificationService m_notificationService)
8
{
9
this.m_notificationService = m_notificationService;
10
}
11
12
[HttpPost]
13
public void Post([FromBody] SubmitNotificationDto notification)
14
{
15
m_notificationService.Send(notification.Value);
16
}
17
}
Controllers/PaymentController.cs
1
[ApiController]
2
[Route("api/[controller]")]
3
public class PaymentController : ControllerBase
4
{
5
private readonly IPaymentService m_paymentService;
6
7
public PaymentController(IPaymentService m_paymentService)
8
{
9
this.m_paymentService = m_paymentService;
10
}
11
12
[HttpPost]
13
public void Post([FromBody] SubmitPaymentDto payment)
14
{
15
m_paymentService.Pay(payment.Amount, payment.Item);
16
}
17
}

Where the design pattern takes place is when the client needs to make request to each service via each single controller which is not ideal. Therefore, we would use the Facade Design Pattern to solve this issue by creating PurchaseFacadeController. Note that you may decouple this monolothic architecture into seperate microservice (i.e Notification Microservice, etc).

Controllers/PurchaseFacadeController.cs
1
[ApiController]
2
[Route("api/purchase")]
3
public class PurchaseFacadeController : ControllerBase
4
{
5
// Subsystems
6
private readonly INotificationService m_notificationService;
7
private readonly IPaymentService m_paymentService;
8
private readonly IInventoryService m_inventoryService;
9
10
private readonly ILogger<PurchaseFacadeController> m_logger;
11
12
public PurchaseFacadeController(
13
INotificationService m_notificationService,
14
IPaymentService paymentService,
15
IInventoryService inventoryService,
16
ILogger<PurchaseFacadeController> logger)
17
{
18
this.m_notificationService = m_notificationService;
19
m_paymentService = paymentService;
20
m_inventoryService = inventoryService;
21
m_logger = logger;
22
}
23
24
[HttpPost]
25
public IActionResult Post([FromBody] SubmitPaymentDto payment)
26
{
27
var inventory = m_inventoryService.Get();
28
29
if (!inventory.Any(item => item == payment.Item)) return BadRequest();
30
31
m_paymentService.Pay(payment.Amount, payment.Item);
32
33
m_notificationService.Send($"Item: {payment.Item} purchase with amount {payment.Amount}");
34
35
m_logger.LogInformation($"Submitted a payment of {0} with amount {1}", payment.Item, payment.Amount);
36
37
return Ok();
38
}
39
}

I hope this helps!

x, gio