Facade Pattern — FixIt Pro Series #10
Scheduling, assigning, notifying, and billing, FixIt Pro's dispatch process involves five subsystems. Learn how the Facade pattern hides that complexity behind a single clean interface, in C# and TypeScript.
Series: Design Patterns with FixIt Pro · Episode 10 / 22 · Structural Pattern
Previous: #09 — Decorator Pattern
The Scenario
When a homeowner submits a job request in FixIt Pro, a lot happens under the hood:
- Scheduler — finds an available time slot and books it
- HandymanAssigner — finds a qualified handyman and assigns them
- NotificationService — alerts the homeowner, handyman, and supervisor
- JobCardRegistry — registers the new job card in the system
- BillingService — generates a quote and sends it to the client
Every one of these is a separate subsystem with its own interface. A developer building a "Submit Job" feature shouldn't need to know the internals of all five. They shouldn't need to call them in the right order, handle each one's error surface, or know which depends on which.
They should call one method: DispatchJob(...).
That's the Facade.
What Is the Facade Pattern?
Provide a unified interface to a set of interfaces in a subsystem. The Facade defines a higher-level interface that makes the subsystem easier to use.
The Facade doesn't replace the subsystems — they still exist and can be used directly if needed. It just provides a simplified entry point for the most common use cases. Complexity doesn't disappear; it moves to where it belongs.
The two participants
| Role | FixIt Pro equivalent |
|---|---|
| Facade | JobDispatchFacade — the unified entry point |
| Subsystems | Scheduler, HandymanAssigner, NotificationService, JobCardRegistry, BillingService |
C# Implementation
// ── Subsystem 1: Scheduler ─────────────────────────────────
public class Scheduler
{
public string FindAvailableSlot(string category)
{
Console.WriteLine($" 📅 Scheduler: Finding slot for {category} job...");
return "Monday 2 June 2025 @ 09:00";
}
public void BookSlot(string slot, string jobId)
{
Console.WriteLine($" 📅 Scheduler: Slot '{slot}' booked for Job #{jobId}.");
}
}
// ── Subsystem 2: HandymanAssigner ──────────────────────────
public class HandymanAssigner
{
public string FindQualifiedHandyman(string category)
{
Console.WriteLine($" 👷 HandymanAssigner: Finding {category} handyman...");
return "James Nghipandua";
}
public void Assign(string handyman, string jobId)
{
Console.WriteLine($" 👷 HandymanAssigner: {handyman} assigned to Job #{jobId}.");
}
}
// ── Subsystem 3: NotificationService ──────────────────────
public class NotificationService
{
public void NotifyHomeowner(string jobId, string slot)
{
Console.WriteLine($" 📧 NotificationService: Homeowner notified — Job #{jobId} scheduled for {slot}.");
}
public void NotifyHandyman(string jobId, string handyman)
{
Console.WriteLine($" 📱 NotificationService: {handyman} notified of Job #{jobId}.");
}
public void NotifySupervisor(string jobId)
{
Console.WriteLine($" 🔔 NotificationService: Supervisor notified of new Job #{jobId}.");
}
}
// ── Subsystem 4: JobCardRegistry ───────────────────────────
public class JobCardRegistry
{
private readonly List<(string JobId, string Title, string Status)> _jobs = new();
public void Register(string jobId, string title)
{
_jobs.Add((jobId, title, "Active"));
Console.WriteLine($" 🗂️ JobCardRegistry: Job #{jobId} '{title}' registered.");
}
public int Count => _jobs.Count;
}
// ── Subsystem 5: BillingService ────────────────────────────
public class BillingService
{
public decimal GenerateQuote(string category, bool isUrgent)
{
var baseRate = category switch
{
"Plumbing" => 850m,
"Electrical" => 1200m,
"Carpentry" => 600m,
_ => 750m
};
var total = isUrgent ? baseRate * 1.25m : baseRate;
Console.WriteLine($" 💰 BillingService: Quote generated — N${total:N2}");
return total;
}
public void SendQuote(string jobId, string clientEmail, decimal amount)
{
Console.WriteLine($" 💰 BillingService: Quote of N${amount:N2} sent to {clientEmail} for Job #{jobId}.");
}
}
// ── Facade ─────────────────────────────────────────────────
public class JobDispatchFacade
{
// Facade owns and coordinates the subsystems
private readonly Scheduler _scheduler = new();
private readonly HandymanAssigner _assigner = new();
private readonly NotificationService _notifier = new();
private readonly JobCardRegistry _registry = new();
private readonly BillingService _billing = new();
// Single entry point — the client calls this and nothing else
public void DispatchJob(
string jobId,
string title,
string category,
string clientEmail,
bool isUrgent = false)
{
Console.WriteLine($"\n{'='.ToString().PadRight(50, '=')}");
Console.WriteLine($"🚀 Dispatching Job #{jobId}: {title}");
Console.WriteLine('='.ToString().PadRight(50, '='));
// Step 1 — Schedule
var slot = _scheduler.FindAvailableSlot(category);
_scheduler.BookSlot(slot, jobId);
// Step 2 — Assign handyman
var handyman = _assigner.FindQualifiedHandyman(category);
_assigner.Assign(handyman, jobId);
// Step 3 — Notify all parties
_notifier.NotifyHomeowner(jobId, slot);
_notifier.NotifyHandyman(jobId, handyman);
_notifier.NotifySupervisor(jobId);
// Step 4 — Register
_registry.Register(jobId, title);
// Step 5 — Billing
var quote = _billing.GenerateQuote(category, isUrgent);
_billing.SendQuote(jobId, clientEmail, quote);
Console.WriteLine($"✅ Job #{jobId} dispatched successfully.\n");
}
}
// ── Client Code ────────────────────────────────────────────
// The client knows nothing about Scheduler, HandymanAssigner,
// NotificationService, JobCardRegistry, or BillingService.
// It just calls DispatchJob.
class Program
{
static void Main()
{
var facade = new JobDispatchFacade();
facade.DispatchJob(
jobId: "JC-301",
title: "Replace burst pipe under sink",
category: "Plumbing",
clientEmail: "[email protected]",
isUrgent: false
);
facade.DispatchJob(
jobId: "JC-302",
title: "Faulty circuit breaker — urgent",
category: "Electrical",
clientEmail: "[email protected]",
isUrgent: true
);
}
}
Output:
==================================================
🚀 Dispatching Job #JC-301: Replace burst pipe under sink
==================================================
📅 Scheduler: Finding slot for Plumbing job...
📅 Scheduler: Slot 'Monday 2 June 2025 @ 09:00' booked for Job #JC-301.
👷 HandymanAssigner: Finding Plumbing handyman...
👷 HandymanAssigner: James Nghipandua assigned to Job #JC-301.
📧 NotificationService: Homeowner notified — Job #JC-301 scheduled for Monday 2 June 2025 @ 09:00.
📱 NotificationService: James Nghipandua notified of Job #JC-301.
🔔 NotificationService: Supervisor notified of new Job #JC-301.
🗂️ JobCardRegistry: Job #JC-301 'Replace burst pipe under sink' registered.
💰 BillingService: Quote generated — N$850.00
💰 BillingService: Quote of N$850.00 sent to [email protected] for Job #JC-301.
✅ Job #JC-301 dispatched successfully.
TypeScript Implementation
// ── Subsystem/Scheduler.ts ─────────────────────────────────
export class Scheduler {
findAvailableSlot(category: string): string {
console.log(` 📅 Scheduler: Finding slot for ${category} job...`);
return "Monday 2 June 2025 @ 09:00";
}
bookSlot(slot: string, jobId: string): void {
console.log(` 📅 Scheduler: Slot '${slot}' booked for Job #${jobId}.`);
}
}
// ── Subsystem/HandymanAssigner.ts ──────────────────────────
export class HandymanAssigner {
findQualifiedHandyman(category: string): string {
console.log(` 👷 HandymanAssigner: Finding ${category} handyman...`);
return "James Nghipandua";
}
assign(handyman: string, jobId: string): void {
console.log(` 👷 HandymanAssigner: ${handyman} assigned to Job #${jobId}.`);
}
}
// ── Subsystem/NotificationService.ts ──────────────────────
export class NotificationService {
notifyHomeowner(jobId: string, slot: string): void {
console.log(` 📧 NotificationService: Homeowner notified — Job #${jobId} scheduled for ${slot}.`);
}
notifyHandyman(jobId: string, handyman: string): void {
console.log(` 📱 NotificationService: ${handyman} notified of Job #${jobId}.`);
}
notifySupervisor(jobId: string): void {
console.log(` 🔔 NotificationService: Supervisor notified of new Job #${jobId}.`);
}
}
// ── Subsystem/JobCardRegistry.ts ──────────────────────────
export class JobCardRegistry {
private jobs: { jobId: string; title: string; status: string }[] = [];
register(jobId: string, title: string): void {
this.jobs.push({ jobId, title, status: "Active" });
console.log(` 🗂️ JobCardRegistry: Job #${jobId} '${title}' registered.`);
}
get count(): number { return this.jobs.length; }
}
// ── Subsystem/BillingService.ts ────────────────────────────
export class BillingService {
generateQuote(category: string, isUrgent: boolean): number {
const rates: Record<string, number> = {
Plumbing: 850,
Electrical: 1200,
Carpentry: 600,
};
const base = rates[category] ?? 750;
const total = isUrgent ? base * 1.25 : base;
console.log(` 💰 BillingService: Quote generated — N$${total.toFixed(2)}`);
return total;
}
sendQuote(jobId: string, clientEmail: string, amount: number): void {
console.log(` 💰 BillingService: Quote of N$${amount.toFixed(2)} sent to ${clientEmail} for Job #${jobId}.`);
}
}
// ── Facade/JobDispatchFacade.ts ────────────────────────────
import { Scheduler } from "../Subsystem/Scheduler";
import { HandymanAssigner } from "../Subsystem/HandymanAssigner";
import { NotificationService } from "../Subsystem/NotificationService";
import { JobCardRegistry } from "../Subsystem/JobCardRegistry";
import { BillingService } from "../Subsystem/BillingService";
export class JobDispatchFacade {
private scheduler = new Scheduler();
private assigner = new HandymanAssigner();
private notifier = new NotificationService();
private registry = new JobCardRegistry();
private billing = new BillingService();
dispatchJob(params: {
jobId: string;
title: string;
category: string;
clientEmail: string;
isUrgent?: boolean;
}): void {
const { jobId, title, category, clientEmail, isUrgent = false } = params;
console.log(`\n${"=".repeat(50)}`);
console.log(`🚀 Dispatching Job #${jobId}: ${title}`);
console.log("=".repeat(50));
// Step 1 — Schedule
const slot = this.scheduler.findAvailableSlot(category);
this.scheduler.bookSlot(slot, jobId);
// Step 2 — Assign handyman
const handyman = this.assigner.findQualifiedHandyman(category);
this.assigner.assign(handyman, jobId);
// Step 3 — Notify all parties
this.notifier.notifyHomeowner(jobId, slot);
this.notifier.notifyHandyman(jobId, handyman);
this.notifier.notifySupervisor(jobId);
// Step 4 — Register
this.registry.register(jobId, title);
// Step 5 — Billing
const quote = this.billing.generateQuote(category, isUrgent);
this.billing.sendQuote(jobId, clientEmail, quote);
console.log(`✅ Job #${jobId} dispatched successfully.\n`);
}
}
// ── App.ts ─────────────────────────────────────────────────
import { JobDispatchFacade } from "./Facade/JobDispatchFacade";
const facade = new JobDispatchFacade();
facade.dispatchJob({
jobId: "JC-301",
title: "Replace burst pipe under sink",
category: "Plumbing",
clientEmail: "[email protected]",
isUrgent: false,
});
facade.dispatchJob({
jobId: "JC-302",
title: "Faulty circuit breaker — urgent",
category: "Electrical",
clientEmail: "[email protected]",
isUrgent: true,
});
C# vs TypeScript — Key Differences
| Aspect | C# | TypeScript |
|---|---|---|
| Named parameters | DispatchJob(jobId: "JC-301", title: ...) |
Object destructuring { jobId, title, ... } |
| Switch expression | category switch { "Plumbing" => 850m, ... } |
Record<string, number> lookup object |
| Default parameter | bool isUrgent = false |
isUrgent?: boolean with ?? false |
| String repeat | '='.ToString().PadRight(50, '=') |
"=".repeat(50) |
| Subsystem ownership | Private fields, instantiated in constructor body | Private fields with inline initialisation |
The TypeScript version uses an options object for dispatchJob — idiomatic for functions with many parameters. The C# version uses named arguments for the same clarity at the call site.
Facade vs Other Patterns It Resembles
| Facade | Adapter | Mediator | |
|---|---|---|---|
| Purpose | Simplify a complex subsystem | Make incompatible interfaces compatible | Centralise complex communication between objects |
| Direction | One-way — client to subsystem | One-way — client to adaptee | Multi-directional — objects communicate through it |
| Knows about | Many subsystems | One adaptee | Many colleagues |
| FixIt Pro | JobDispatchFacade → 5 subsystems |
SmsNotifierAdapter → LegacySmsGateway |
Coming in Episode 16 |
The Mediator (Episode 16) looks similar but is different in intent — the Facade simplifies access to subsystems from the outside; the Mediator controls how objects communicate with each other on the inside.
When to Use the Facade
Use it when:
- You want to provide a simple interface to a complex subsystem
- You want to layer your system — the Facade defines the entry point for each layer
- There are too many dependencies between clients and implementation classes
Avoid it when:
- The subsystem is already simple — adding a Facade is unnecessary overhead
- You need full access to subsystem internals from all callers — the Facade would be leaky anyway
- The "simplification" just moves the complexity one level up without actually reducing it
Real-World Takeaway
The Facade is the most quietly ubiquitous pattern in software. Every SDK you've ever used is a Facade. The AWS SDK wraps hundreds of HTTP calls behind methods like s3.putObject(). The Stripe API hides payment processing complexity behind stripe.charges.create(). In .NET, File.ReadAllText() is a Facade over FileStream, StreamReader, encoding, and buffer management. In ASP.NET Core, WebApplication.CreateBuilder() is a Facade over DI container setup, configuration, logging, and middleware pipeline construction.
In FixIt Pro, facade.DispatchJob(...) is one line for the caller. Underneath, five subsystems coordinate in a specific order. The caller doesn't know. The caller doesn't need to know. That's the point.
Repo Structure for This Episode
github.com/antonlungameni/fixit-pro-design-patterns
fixit-pro-design-patterns/
├── csharp/Structural/10-Facade/
│ ├── Subsystem/Scheduler.cs
│ ├── Subsystem/HandymanAssigner.cs
│ ├── Subsystem/NotificationService.cs
│ ├── Subsystem/JobCardRegistry.cs
│ ├── Subsystem/BillingService.cs
│ ├── Facade/JobDispatchFacade.cs
│ └── Program.cs
└── typescript/Structural/10-Facade/
├── Subsystem/Scheduler.ts
├── Subsystem/HandymanAssigner.ts
├── Subsystem/NotificationService.ts
├── Subsystem/JobCardRegistry.ts
├── Subsystem/BillingService.ts
├── Facade/JobDispatchFacade.ts
└── App.ts
Previous: #09 — Decorator Pattern
Next up: #11 — Flyweight Pattern
Sharing common job metadata — tool types, material specs, category descriptions — across thousands of job cards in memory without duplicating data.