Prerequisites: Basic knowledge of C# or other object-oriented language
Table of Contents
- Course Overview
- Introduction to Design Patterns
- The two situations of application of the Facade
- Situation 1 — The Big Ball of Mud
- 4.1 The problem: the Big Ball of Mud
- 4.2 The solution: the BigClassFacade
- 4.3 Demonstration — Before the Facade (BallOfMud)
- 4.4 Demonstration — After the Facade (BigClassFacade)
- 5.1 The problem: orchestration of multiple services
- 5.2 The solution: a Facade with interface
- 5.3 Demonstration — Before the Facade (Facade/Weather)
- 5.4 Demonstration — After the WeatherFacade
- 6.1 Before the Facade
- 6.2 After the Facade
1. Course Overview
This course teaches the Facade design pattern: when to use it, and how to do it correctly in C#. It is part of a series of courses on design patterns by David Starr, which you can find on Twitter under the handle @elegantcoder.
Main topics covered
- The situation of the big ball of mud (giant and disorganized class)
- The situation of multiple worker classes (multiple services to orchestrate)
- The flexibility of the Facade pattern to address these two situations
- Putting Facade into practice in real C# code
Prerequisites
Be familiar with the basics of C# or any other object-oriented language. By the end of this course, you will be comfortable exploring other courses on C# patterns.
2. Introduction to Design Patterns
A design pattern is essentially a known solution to a recurring problem. These are not solutions invented for the occasion, but proven answers, shared and named in the developer community. They facilitate communication between developers: by naming a pattern, everyone immediately understands the intention of the code.
David Starr has been using design patterns for many years and teaching them at various levels. He considers them valuable not only for improving code quality, but also for formalizing discussions between developers when it comes to finding a solution to a problem.
Course objectives
- Identify the problem to be resolved and understand the root cause
- Introduce the Facade pattern and show how it improves the code
- Explore the two variants of the problem that the Facade addresses
- See Facade in practice through several real code demonstrations
3. The two application situations of the Facade
The Facade pattern is particularly useful in two distinct situations:
| Location | Description | Symptom |
|---|---|---|
| Big Ball of Mud | A single, massive class that attempts to do everything | Difficult to find the right methods among dozens of useless ones |
| Worker Classes | Multiple service classes that the calling code must orchestrate | Repeating new code and calls in controllers |
In both cases, the solution is the same: interpose a Facade which simplifies the interface and hides the underlying complexity.
4. Situation 1 — The Big Ball of Mud
4.1 The problem: the Big Ball of Mud
The Big Ball of Mud is an anti-pattern made famous by Martin Fowler. It’s a class that tries to solve all the world’s problems: it’s bloated, too big, and completely misses the spirit of object-oriented programming.
Concrete problems:
- The program only needs 4 methods from the dozens available
- You have to dig deep into the class to figure out which ones to use
- Method names are unclear (
GetValueA,GetValueB,DoSomething) - It’s not clear what each method does without reading its implementation
Conceptual illustration:
Programme ────────────────► BigClass (100+ méthodes)
SetValueI(), GetValueA(), GetValueB(),
IncrementI(), DecrememntI(), DoSomething(),
AddToI(), UnrelatedMethod(), AddedThisMethodLater()...
The program must navigate all this complexity to simply increment and decrement a value.
4.2 The solution: the BigClassFacade
We place a Facade between the program and the giant class. The Facade:
- Holds an internal reference to the giant class
- Orchestra calls on behalf of the program
- Only exposes the methods actually needed by the program
- Uses expressive method names (
IncreaseBy,DecreaseBy,GetCurrentValue) - The program no longer knows anything about the giant class
Programme ──► BigClassFacade ──────► BigClass
IncreaseBy() (détails cachés)
DecreaseBy()
GetCurrentValue()
The program only speaks to the Facade, without knowing anything about BigClass.
4.3 Demonstration — Before the Facade (BallOfMud)
Program.cs — Initial version (problematic)
using System;
using BallOfMud.Services;
namespace BallOfMud
{
class Program
{
static void Main(string[] args)
{
BigClass bigClass = new BigClass();
bigClass.SetValueI(3);
bigClass.IncrementI();
bigClass.IncrementI();
bigClass.IncrementI();
bigClass.DecrememntI();
Console.WriteLine($"Final Number : {bigClass.GetValueB()}");
}
}
}
What’s wrong with this code:
- We instantiate
BigClassdirectly — the program is coupled to this massive class - We call
bigClass.SetValueI(3)— what does “I” mean? It’s unclear - We call
bigClass.GetValueB()to display the result — butGetValueB()actually returns the string"Ball of Mud"! This is not what we wanted - You have to explore the internals of
BigClassto findGetValueA()which returns the true numerical value - The
DecrememntI()typo (instead ofDecrementI()) is another sign of the messiness of this class
Output with GetValueB(): Final Number: Ball of Mud ← incorrect result
Correct output with GetValueA(): Final Number: 5 ← (3 + 3 increments - 1 decrement = 5)
Services/BigClass.cs — The giant class
namespace BallOfMud.Services
{
public class BigClass
{
private int _i;
public int GetValueA()
{
// some work
return _i;
}
public string GetValueB()
{
return "Ball of Mud";
}
public void SetValueI(int i)
{
_i = i;
}
public void IncrementI()
{
_i++;
}
public void DoSomething()
{
_i--;
}
public int AddToI(int addMe)
{
_i += addMe;
return _i;
}
public void UnrelatedMethod()
{
// do something unrelated
}
public void AddedThisMethodLater()
{
// calls a db for a number
int theNumber = 12;
_i += theNumber;
}
public void DecrememntI()
{
_i--;
}
}
}
Issues in this class:
- Mix of related (
_i) and unrelated (UnrelatedMethod) methods - Methods added over time (
AddedThisMethodLater) which obfuscate the interface - Non-expressive nouns (
GetValueA,GetValueB,DoSomething) - Typo in method name (
DecrememntI) - Caller must know call order and internal details
4.4 Demonstration — After the Facade (BigClassFacade)
Services/BigClassFacade.cs — Le Facade
namespace BallOfMud.Services
{
public class BigClassFacade
{
private readonly BigClass _bigClass;
public BigClassFacade()
{
_bigClass = new BigClass();
_bigClass.SetValueI(0);
}
public void IncreaseBy(int numberToAdd)
{
_bigClass.AddToI(numberToAdd);
}
public void DecreaseBy(int numberToSubtract)
{
_bigClass.AddToI(-numberToSubtract);
}
public int GetCurrentValue()
{
return _bigClass.GetValueA();
}
}
}
What the Facade does:
- It encapsulates
BigClassin a privatereadonlyfield - Constructor initializes internal state (call to
SetValueI(0)) — program doesn’t have to worry about it IncreaseBy(int): clear method that increases the valueDecreaseBy(int): clear method that decreases the value (usesAddToIwith a negative number)GetCurrentValue(): returns current value viaGetValueA()— the right choice, opaque to the caller
Visible structure of the Facade:
BigClassFacade()— constructorIncreaseBy(int)— expressive public methodDecreaseBy(int)— expressive public methodGetCurrentValue()— expressive public method_bigClass— private field (implementation detail invisible)
Program.cs — Version after Facade
using System;
using BallOfMud.Services;
namespace BallOfMud
{
class Program
{
static void Main(string[] args)
{
BigClassFacade bigClass = new BigClassFacade();
bigClass.IncreaseBy(50);
bigClass.DecreaseBy(20);
Console.WriteLine($"Final Number : {bigClass.GetCurrentValue()}");
}
}
}
Output: Final Number: 30
What changed:
- The program no longer instantiates
BigClassdirectly — it only usesBigClassFacade - Calls are expressive and self-documenting:
IncreaseBy(50),DecreaseBy(20) - No knowledge of
_i,SetValueI,AddToIis required - No more risk of calling the wrong method or getting the order of calls wrong
- The result is immediately understandable: 50 - 20 = 30
5. Situation 2 — Multiple service classes (Worker Classes)
5.1 The problem: orchestration of multiple services
This is the second variant of the same problem, and the most common in practice. It is mainly encountered in controllers (MVC pattern) where several services are injected via the constructor.
The scenario: A program needs to call several service classes to obtain a result. Each department does part of the work, and it’s up to the caller to orchestrate them all.
Problems:
- The program must instantiate each service (
new GeoLookupService(),new WeatherService(), etc.) - It must call the methods in the correct order — sometimes the order is meaningful, especially if the services share a common subsystem (database, shared state)
- It must handle many intermediate variables (
city,state,fahrenheit,celsius) - This code repeats everywhere in the application
Typical code (before Facade):
// 3 services à instancier
GeoLookupService geoLookupService = new GeoLookupService();
WeatherService weatherService = new WeatherService();
ConverterService metricConverter = new ConverterService();
// Appels en séquence avec variables intermédiaires
City city = geoLookupService.GetCityForZipCode(zipCode);
State state = geoLookupService.GetStateForZipCode(zipCode);
int fahrenheit = weatherService.GetTempFahrenheit(city, state);
int celsius = metricConverter.ConvertFahrenheitToCelcius(fahrenheit);
// Affichage des résultats
Console.WriteLine("...", fahrenheit, celsius, city.Name, state.Name);
This code is repetitive, verbose, and difficult to unit test.
5.2 The solution: a Facade with interface
We introduce a Facade which hides all calls to services. The recommended good practice is to accompany the Facade with an interface, for two reasons:
- Decoupling: caller only needs the interface, not the concrete class
- Scalability: if the underlying services change, we can create a new implementation of the Facade without modifying the calling code
- Testability: you can easily mock the Facade in unit tests
Programme ──► IWeatherFacade ──► WeatherFacade ──► GeoLookupService
GetTempInCity() ──► WeatherService
──► ConverterService
After the Facade: the program only makes one call → weatherFacade.GetTempInCity(zipCode).
5.3 Demonstration — Before the Facade (Facade/Weather)
Program.cs — Initial release
using System;
using Facade.Entities;
using Facade.Services;
namespace Facade
{
public static class Program
{
static void Main(string[] args)
{
const string zipCode = "98074";
// appel au service 1
GeoLookupService geoLookupService = new GeoLookupService();
City city = geoLookupService.GetCityForZipCode(zipCode);
State state = geoLookupService.GetStateForZipCode(zipCode);
// appel au service 2
WeatherService weatherService = new WeatherService();
int fahrenheit = weatherService.GetTempFahrenheit(city, state);
// appel au service 3
ConverterService metricConverter = new ConverterService();
int celcius = metricConverter.ConvertFahrenheitToCelcious(fahrenheit);
// agrégation des résultats
Console.WriteLine("The current temperature is {0} F / {1} C in {2}, {3}",
fahrenheit,
celcius,
city.Name,
state.Name);
}
}
}
Services/GeoLookupService.cs
using Facade.Entities;
namespace Facade.Services
{
public class GeoLookupService
{
public City GetCityForZipCode(string zipCode)
{
return new City();
}
public State GetStateForZipCode(string zipCode)
{
return new State();
}
}
}
Services/WeatherService.cs
using Facade.Entities;
namespace Facade.Services
{
public class WeatherService
{
public int GetTempFahrenheit(City city, State state)
{
// call to service or db would go here
return 53;
}
}
}
Services/ConverterService.cs
using System;
using System.Net.Mail;
namespace Facade.Services
{
public class ConverterService
{
public int ConvertFahrenheitToCelcious(int fahrenheit)
{
// int celsius = (fahrenheit * 9) / (5 + 32);
double celsius = (5.0 / 9.0) * (fahrenheit - 32);
return (int) celsius;
}
}
}
Entities/City.cs
namespace Facade.Entities
{
public class City
{
public City GetCityForZipCode(string zipCode)
{
// service or db lookup would go here
return new City();
}
public string Name => "Redmond";
}
}
Entities/State.cs
namespace Facade.Entities
{
public class State
{
public State GetStateForZipCode(string zipCode)
{
// service or db lookup would go here
return new State();
}
public string Name => "Washington";
}
}
5.4 Demonstration — After the Facade (WeatherFacade)
Step 1: Create the IWeatherFacade interface
David Starr explains that he normally starts by refactoring an interface from a concrete type once he needs to generalize it. In this course, it starts with the interface to illustrate top-down development (from architecture to implementation).
The interface contains only one method — that’s all the program needs.
using Facade.Entities;
namespace Facade
{
public interface IWeatherFacade
{
WeatherFacadeResults GetTempInCity(string zipCode);
}
}
Step 2: Create the WeatherFacadeResults DTO
WeatherFacadeResults is a simple Data Transfer Object (DTO) — it only contains properties for passing data between the Facade and the calling program.
namespace Facade.Entities
{
public class WeatherFacadeResults
{
public int Fahrenheit { get; set; }
public int Celsius { get; set; }
public City City { get; set; }
public State State { get; set; }
}
}
Properties:
Fahrenheit: temperature in degrees FahrenheitCelsius: temperature in degrees CelsiusCity: city object (withNameproperty)State: state/province object (withNameproperty)
Step 3: Implement WeatherFacade
using Facade.Entities;
using Facade.Services;
namespace Facade
{
public class WeatherFacade : IWeatherFacade
{
private readonly ConverterService _converterService;
private readonly GeoLookupService _geoLookUpService;
private readonly WeatherService _weatherService;
// Constructeur sans paramètre — instancie les services par défaut
public WeatherFacade() :
this(new ConverterService(), new GeoLookupService(), new WeatherService())
{
}
// Constructeur avec injection de dépendances (pour les tests)
public WeatherFacade(ConverterService converterService,
GeoLookupService geoLookUpService,
WeatherService weatherService)
{
_converterService = converterService;
_geoLookUpService = geoLookUpService;
_weatherService = weatherService;
}
public WeatherFacadeResults GetTempInCity(string zipCode)
{
City city = _geoLookUpService.GetCityForZipCode(zipCode);
State state = _geoLookUpService.GetStateForZipCode(zipCode);
int tempF = _weatherService.GetTempFahrenheit(city, state);
int tempC = _converterService.ConvertFahrenheitToCelsius(tempF);
var results = new WeatherFacadeResults
{
City = city,
State = state,
Fahrenheit = tempF,
Celsius = tempC
};
return results;
}
}
}
Key points of this implementation:
-
Two constructors: the first is practical (creates the services itself), the second allows dependency injection. David Starr mentions that some people call this pattern “Poor Man’s Inversion of Control” (simplified IoC).
-
Services in private fields: the three services are stored as
readonlyfields — they are part of the internal implementation details of the Facade. -
GetTempInCity(string zipCode): This method orchestrates the three services exactly as the original code did inMain(), but now this logic is encapsulated in the Facade. -
Returning a DTO: instead of returning several separate values, the Facade returns a single
WeatherFacadeResultsobject.
Step 4: The improved ConverterService service
using System;
using System.Net.Mail;
namespace Facade.Services
{
public class ConverterService
{
public int ConvertFahrenheitToCelsius(int fahrenheit)
{
double celsius = (5.0 / 9.0) * (fahrenheit - 32);
return (int) celsius;
}
public int ConvertCelsiusToFahrenheit(int celsius)
{
double fahrenheit = celsius * (1.8 + 32);
return (int) fahrenheit;
}
}
}
Services/GeoLookupService.cs — Enhanced version
using Facade.Entities;
namespace Facade.Services
{
public class GeoLookupService
{
public City GetCityForZipCode(string zipCode)
{
// a lookup would occur here
return new City();
}
public State GetStateForZipCode(string zipCode)
{
// a lookup would occur here
return new State();
}
public City GetCityForCoordinates(double longitude, double latitude)
{
// a lookup would occur here
return new City();
}
public City GetStateByCapital(string capital)
{
// a lookup would occur here
return new City();
}
}
}
Program.cs — Version after Facade
using System;
using Facade.Entities;
using Facade.Services;
namespace Facade
{
public static class Program
{
static void Main(string[] args)
{
const string zipCode = "98074";
IWeatherFacade weatherFacade = new WeatherFacade();
WeatherFacadeResults results = weatherFacade.GetTempInCity(zipCode);
Console.WriteLine("The current temperature is {0}F/{1}C in {2}, {3}",
results.Fahrenheit,
results.Celsius,
results.City.Name,
results.State.Name);
}
}
}
Output: The current temperature is 53F/11C in Redmond, Washington
What changed:
- No more manual instantiation of the 3 services
- More management of intermediate variables (
city,state,fahrenheit) - The program is declared via the interface
IWeatherFacade— complete decoupling - Single call:
weatherFacade.GetTempInCity(zipCode) - The result is a structured object
WeatherFacadeResults
6. Demonstration — Generic example (GenericFacade)
This third project illustrates the Facade pattern in a more generic way, with three abstract services (ServiceA, ServiceB, ServiceC), to show the fundamental structure of the pattern independent of the business domain.
6.1 Before the Facade
Program.cs — Initial release
using System;
using GenericFacade.Services;
namespace GenericFacade
{
class Program
{
static void Main(string[] args)
{
ServiceA serviceA = new ServiceA();
int sAResult = serviceA.Method2();
ServiceB serviceB = new ServiceB();
string sBResult = serviceB.Method2();
ServiceC serviceC = new ServiceC();
double sCResult = serviceC.Method1();
Console.WriteLine(sAResult + " - " + sCResult + " - " + sBResult);
}
}
}
Problems:
- Three
newfor three different services - Each service has its own methods — the caller must know which method to call on which service
- The calling code is strongly coupled to the three concrete classes
Services/ServiceA.cs
namespace GenericFacade.Services
{
public class ServiceA
{
public void Method1()
{
// do some work
}
public int Method2()
{
// do some work
return 0;
}
}
}
Services/ServiceB.cs
namespace GenericFacade.Services
{
public class ServiceB
{
public void Method1()
{
// do some work
}
public string Method2()
{
// do some work
return "ServiceB string";
}
}
}
Services/ServiceC.cs
namespace GenericFacade.Services
{
public class ServiceC
{
public double Method1()
{
// do some work
return 1.01;
}
public string Method2()
{
// do some work
return "ServiceC string";
}
}
}
6.2 After the Facade
Services/IServiceFacade.cs — The interface
using System;
namespace GenericFacade.Services
{
public interface IServiceFacade
{
Tuple<int, double, string> CallFacade();
}
}
Note: The interface uses a Tuple<int, double, string> as a return type to aggregate the results of the three services into a single object. In production, we would prefer a dedicated DTO (like WeatherFacadeResults) for more readability.
Services/ServiceFacade.cs — Implementation
using System;
namespace GenericFacade.Services
{
public class ServiceFacade : IServiceFacade
{
readonly ServiceA _serviceA = new ServiceA();
readonly ServiceB _serviceB = new ServiceB();
readonly ServiceC _serviceC = new ServiceC();
public Tuple<int, double, string> CallFacade()
{
int SAResult = _serviceA.Method2();
string SBResult = _serviceB.Method2();
double SCResult = _serviceC.Method1();
return new Tuple<int, double, string>(SAResult, SCResult, SBResult);
}
}
}
Note: In this simplified version, services are directly instantiated as fields. For production code, it is better to use dependency injection via the constructor (like in WeatherFacade).
Program.cs — Version after Facade
using System;
using GenericFacade.Services;
namespace GenericFacade
{
class Program
{
static void Main(string[] args)
{
IServiceFacade facade = new ServiceFacade();
Tuple<int, double, string> result = facade.CallFacade();
Console.WriteLine(result.Item1 + " - " + result.Item2 + " - " + result.Item3);
}
}
}
Result: The program only interacts with IServiceFacade — a single facade.CallFacade() call overrides multiple instantiations and calls.
7. Architecture and class structure
Conceptual diagram of the Facade pattern (Big Ball of Mud case)
┌─────────────────────────────────────────────────────────────┐
│ Programme │
│ │
│ BigClassFacade facade = new BigClassFacade(); │
│ facade.IncreaseBy(50); │
│ facade.DecreaseBy(20); │
│ Console.WriteLine(facade.GetCurrentValue()); │
└──────────────────────┬──────────────────────────────────────┘
│ utilise uniquement
▼
┌──────────────────────────────────────────────────────────────┐
│ BigClassFacade (Facade) │
│ │
│ - _bigClass : BigClass (champ privé) │
│ + BigClassFacade() │
│ + IncreaseBy(int) : void │
│ + DecreaseBy(int) : void │
│ + GetCurrentValue() : int │
└──────────────────────┬───────────────────────────────────────┘
│ délègue à
▼
┌──────────────────────────────────────────────────────────────┐
│ BigClass (sous-système) │
│ │
│ - _i : int │
│ + GetValueA() : int + GetValueB() : string │
│ + SetValueI(int) : void + IncrementI() : void │
│ + DecrememntI() : void + DoSomething() : void │
│ + AddToI(int) : int + UnrelatedMethod() : void │
│ + AddedThisMethodLater() : void │
└──────────────────────────────────────────────────────────────┘
Conceptual diagram of the Facade pattern (Case Worker Classes)
┌─────────────────────────────────────────────────────────────┐
│ Programme │
│ │
│ IWeatherFacade facade = new WeatherFacade(); │
│ WeatherFacadeResults r = facade.GetTempInCity("98074"); │
│ Console.WriteLine(...); │
└──────────────────────┬──────────────────────────────────────┘
│ utilise l'interface
▼
┌────────────────────────────────────────────────────────────┐
│ <<interface>> IWeatherFacade │
│ │
│ + GetTempInCity(string zipCode) : WeatherFacadeResults │
└──────────────────────┬─────────────────────────────────────┘
│ implémente
▼
┌───────────────────────────────────────────────────────────────┐
│ WeatherFacade (Facade) │
│ │
│ - _converterService : ConverterService │
│ - _geoLookUpService : GeoLookupService │
│ - _weatherService : WeatherService │
│ + WeatherFacade() │
│ + WeatherFacade(ConverterService, GeoLookupService, │
│ WeatherService) │
│ + GetTempInCity(string) : WeatherFacadeResults │
└───────────┬───────────────────────┬───────────────────────────┘
│ │ │
▼ ▼ ▼
GeoLookupService WeatherService ConverterService
GetCityForZip() GetTempF() ConvertFtoC()
GetStateForZip() ConvertCtoF()
Structure of the DTO WeatherFacadeResults
WeatherFacadeResults
├── Fahrenheit : int
├── Celsius : int
├── City : City (Name : "Redmond")
└── State : State (Name : "Washington")
Organization of projects in the solution
FacadePattern.sln
├── BallOfMud/
│ ├── Program.cs
│ └── Services/
│ ├── BigClass.cs
│ └── BigClassFacade.cs ← ajouté dans la version "after"
│
├── Facade/
│ ├── Program.cs
│ ├── IWeatherFacade.cs ← ajouté dans la version "after"
│ ├── WeatherFacade.cs ← ajouté dans la version "after"
│ ├── Entities/
│ │ ├── City.cs
│ │ ├── State.cs
│ │ └── WeatherFacadeResults.cs ← ajouté dans la version "after"
│ └── Services/
│ ├── GeoLookupService.cs
│ ├── WeatherService.cs
│ └── ConverterService.cs
│
└── GenericFacade/
├── Program.cs
└── Services/
├── ServiceA.cs
├── ServiceB.cs
├── ServiceC.cs
├── IServiceFacade.cs ← ajouté dans la version "after"
└── ServiceFacade.cs ← ajouté dans la version "after"
8. Best practices and summary
Summary of the Facade pattern
The Facade pattern belongs to the category of structural patterns (Structural Patterns). It provides a simplified interface to a set of more complex classes or subsystems.
When to use the Facade:
- When you have to work with a very large class of which you only use part of the functionalities
- When you need to orchestrate multiple service classes in your calling code, and this orchestration repeats
- When you want to reduce coupling between calling code and underlying classes
- When you want to simplify testing — the Facade can be easily mocked via its interface
When not to use the Facade:
- When the underlying complexity is not real or when the abstraction overhead is not justified
- When only one simple class is involved and it already exposes a clear interface
Best practices
1. Always accompany the Facade with an interface
// ✅ Bonne pratique
public interface IWeatherFacade
{
WeatherFacadeResults GetTempInCity(string zipCode);
}
public class WeatherFacade : IWeatherFacade { ... }
// Dans le code appelant :
IWeatherFacade facade = new WeatherFacade(); // découplé
The interface allows:
- To change the implementation without modifying the calling code
- Write unit tests with mocks
- To clearly express the Facade contract
2. Use dependency injection via constructor
// ✅ Bonne pratique — supporte l'injection de dépendances
public WeatherFacade(ConverterService converterService,
GeoLookupService geoLookUpService,
WeatherService weatherService)
{
_converterService = converterService;
_geoLookUpService = geoLookUpService;
_weatherService = weatherService;
}
// Constructeur de commodité (optionnel)
public WeatherFacade() :
this(new ConverterService(), new GeoLookupService(), new WeatherService())
{
}
David Starr notes that some call this pattern “Poor Man’s Inversion of Control” — it is a simplified form of IoC that does not require a DI container.
3. Use expressive method names
// ❌ Peu expressif (la BigClass originale)
bigClass.AddToI(-20);
bigClass.GetValueA();
// ✅ Expressif (le Facade)
bigClass.DecreaseBy(20);
bigClass.GetCurrentValue();
The Facade is an opportunity to rename methods to reflect business intent rather than implementation details.
4. The Facade becomes testable
Even if the original BigClass is difficult to test (too many methods, obscure internal state), the Facade is simple to test:
// Test possible grâce au Facade et à l'interface
IWeatherFacade mockFacade = new MockWeatherFacade();
var result = mockFacade.GetTempInCity("98074");
Assert.Equal("Redmond", result.City.Name);
5. Hide the giant classes behind the Facade
Reminder: the BigClass had many methods. The BigClassFacade only exposes 3:
IncreaseBy(int)DecreaseBy(int)GetCurrentValue()
All the complexity is hidden behind this reduced interface.
Comparison table: Before vs After the Facade
| Appearance | Front (without Facade) | After (with Facade) |
|---|---|---|
| Pairing | Strong coupling with concrete classes | Pairing via interface only |
| Readability | Many variables and calls | A single expressive call |
| Testability | Difficult to mock | Easy to mock via interface |
| Scalability | Change a service = change the caller | Change a service = change the Facade only |
| Complexity management | The caller manages everything | The Facade manages everything |
| Knowledge required | Must know all services | Only knows the Facade |
Key Takeaways
- Use the Facade to provide a single interface to low-level classes (services, workers)
- Always create an interface for the Facade — this is a fundamental best practice
- The Facade allows you to hide giant classes (Big Ball of Mud) by reducing the visible interface
- The Facade becomes testable, even if the underlying classes are not easily testable
- The pattern is flexible: it applies to a giant class as well as to a set of multiple services
- Dual constructor (without and with parameters) is a common technique to enable both convenience and dependency injection
9. Complete code file reference
BallOfMud Project — “Before” Files
| File | Description |
|---|---|
Program.cs | Main program that uses BigClass directly |
Services/BigClass.cs | The giant class with many unclear methods |
BallOfMud project — “After” files
| File | Description |
|---|---|
Program.cs | Simplified program that only uses BigClassFacade |
Services/BigClass.cs | The giant class (unchanged) |
Services/BigClassFacade.cs | [NEW] The Facade that hides BigClass |
Facade project (Weather) — “Before” files
| File | Description |
|---|---|
Program.cs | Program that instantiates and orchestrates 3 services manually |
Services/GeoLookupService.cs | Geolocation service by postal code |
Services/WeatherService.cs | Weather service (temperature in Fahrenheit) |
Services/ConverterService.cs | Fahrenheit ↔ Celsius conversion service |
Entities/City.cs | Entity representing a city |
Entities/State.cs | Entity representing a state |
Facade project (Weather) — “After” files
| File | Description |
|---|---|
Program.cs | Simplified program with a single call to Facade |
IWeatherFacade.cs | [NEW] Weather Facade interface |
WeatherFacade.cs | [NEW] Concrete implementation of the Facade |
Entities/WeatherFacadeResults.cs | [NEW] Results DTO |
Services/GeoLookupService.cs | Geolocation service (enriched with additional methods) |
Services/WeatherService.cs | Weather service (unchanged) |
Services/ConverterService.cs | Conversion service (enhanced by ConvertCelsiusToFahrenheit) |
GenericFacade project — “Before” files
| File | Description |
|---|---|
Program.cs | Program that instantiates and calls 3 generic services |
Services/ServiceA.cs | Generic service A (returns int) |
Services/ServiceB.cs | Generic Service B (returns string) |
Services/ServiceC.cs | Generic C service (returns double) |
GenericFacade project — “After” files
| File | Description |
|---|---|
Program.cs | Simplified program with a single call facade.CallFacade() |
Services/IServiceFacade.cs | [NEW] Generic Facade Interface |
Services/ServiceFacade.cs | [NEW] Implementation of the Generic Facade |
Services/ServiceA.cs | Service A (unchanged) |
Services/ServiceB.cs | Service B (slightly modified) |
Services/ServiceC.cs | Service C (unchanged) |
Search Terms
c-sharp · design · patterns · facade · testing · architecture · c# · .net · development · services · program.cs · demonstration · version · interface · ball · ballofmud · classes · genericfacade · initial · mud · pattern · weather · bigclassfacade · case