Intermediate

C-Sharp Design Patterns Facade

This course teaches the Facade design pattern: when to use it, and how to do it correctly in C#.

Prerequisites: Basic knowledge of C# or other object-oriented language


Table of Contents

  1. Course Overview
  2. Introduction to Design Patterns
  3. The two situations of application of the Facade
  4. Situation 1 — The Big Ball of Mud
  1. Situation 2 — Multiple service classes (Worker Classes)
  1. Demonstration — Generic example (GenericFacade)
  1. Architecture and class structure
  2. Good practices and summary
  3. Complete code file reference

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

  1. Identify the problem to be resolved and understand the root cause
  2. Introduce the Facade pattern and show how it improves the code
  3. Explore the two variants of the problem that the Facade addresses
  4. 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:

LocationDescriptionSymptom
Big Ball of MudA single, massive class that attempts to do everythingDifficult to find the right methods among dozens of useless ones
Worker ClassesMultiple service classes that the calling code must orchestrateRepeating 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:

  1. We instantiate BigClass directly — the program is coupled to this massive class
  2. We call bigClass.SetValueI(3) — what does “I” mean? It’s unclear
  3. We call bigClass.GetValueB() to display the result — but GetValueB() actually returns the string "Ball of Mud"! This is not what we wanted
  4. You have to explore the internals of BigClass to find GetValueA() which returns the true numerical value
  5. The DecrememntI() typo (instead of DecrementI()) 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 BigClass in a private readonly field
  • Constructor initializes internal state (call to SetValueI(0)) — program doesn’t have to worry about it
  • IncreaseBy(int): clear method that increases the value
  • DecreaseBy(int): clear method that decreases the value (uses AddToI with a negative number)
  • GetCurrentValue(): returns current value via GetValueA() — the right choice, opaque to the caller

Visible structure of the Facade:

  • BigClassFacade() — constructor
  • IncreaseBy(int) — expressive public method
  • DecreaseBy(int) — expressive public method
  • GetCurrentValue() — 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 BigClass directly — it only uses BigClassFacade
  • Calls are expressive and self-documenting: IncreaseBy(50), DecreaseBy(20)
  • No knowledge of _i, SetValueI, AddToI is 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:

  1. Decoupling: caller only needs the interface, not the concrete class
  2. Scalability: if the underlying services change, we can create a new implementation of the Facade without modifying the calling code
  3. 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 Fahrenheit
  • Celsius: temperature in degrees Celsius
  • City: city object (with Name property)
  • State: state/province object (with Name property)

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:

  1. 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).

  2. Services in private fields: the three services are stored as readonly fields — they are part of the internal implementation details of the Facade.

  3. GetTempInCity(string zipCode): This method orchestrates the three services exactly as the original code did in Main(), but now this logic is encapsulated in the Facade.

  4. Returning a DTO: instead of returning several separate values, the Facade returns a single WeatherFacadeResults object.

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 new for 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:

  1. When you have to work with a very large class of which you only use part of the functionalities
  2. When you need to orchestrate multiple service classes in your calling code, and this orchestration repeats
  3. When you want to reduce coupling between calling code and underlying classes
  4. 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

AppearanceFront (without Facade)After (with Facade)
PairingStrong coupling with concrete classesPairing via interface only
ReadabilityMany variables and callsA single expressive call
TestabilityDifficult to mockEasy to mock via interface
ScalabilityChange a service = change the callerChange a service = change the Facade only
Complexity managementThe caller manages everythingThe Facade manages everything
Knowledge requiredMust know all servicesOnly knows the Facade

Key Takeaways

  1. Use the Facade to provide a single interface to low-level classes (services, workers)
  2. Always create an interface for the Facade — this is a fundamental best practice
  3. The Facade allows you to hide giant classes (Big Ball of Mud) by reducing the visible interface
  4. The Facade becomes testable, even if the underlying classes are not easily testable
  5. The pattern is flexible: it applies to a giant class as well as to a set of multiple services
  6. 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

FileDescription
Program.csMain program that uses BigClass directly
Services/BigClass.csThe giant class with many unclear methods

BallOfMud project — “After” files

FileDescription
Program.csSimplified program that only uses BigClassFacade
Services/BigClass.csThe giant class (unchanged)
Services/BigClassFacade.cs[NEW] The Facade that hides BigClass

Facade project (Weather) — “Before” files

FileDescription
Program.csProgram that instantiates and orchestrates 3 services manually
Services/GeoLookupService.csGeolocation service by postal code
Services/WeatherService.csWeather service (temperature in Fahrenheit)
Services/ConverterService.csFahrenheit ↔ Celsius conversion service
Entities/City.csEntity representing a city
Entities/State.csEntity representing a state

Facade project (Weather) — “After” files

FileDescription
Program.csSimplified 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.csGeolocation service (enriched with additional methods)
Services/WeatherService.csWeather service (unchanged)
Services/ConverterService.csConversion service (enhanced by ConvertCelsiusToFahrenheit)

GenericFacade project — “Before” files

FileDescription
Program.csProgram that instantiates and calls 3 generic services
Services/ServiceA.csGeneric service A (returns int)
Services/ServiceB.csGeneric Service B (returns string)
Services/ServiceC.csGeneric C service (returns double)

GenericFacade project — “After” files

FileDescription
Program.csSimplified 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.csService A (unchanged)
Services/ServiceB.csService B (slightly modified)
Services/ServiceC.csService 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

Interested in this course?

Contact us to book it or get a custom training plan for your team.