Skip to main content

Façade Pattern in TypeScript

4 min read

Older Article

This article was published 9 years ago. Some information may be outdated or no longer applicable.

Design patterns help us write better software. They’re reusable solutions to problems that keep showing up, and they’ve been around long enough to prove their worth.

GoF

Back in 1994, Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides published a book covering 23 design patterns: Design Patterns: Elements of Reusable Object-Oriented Software. You might know it (or its authors) as the Gang of Four (GoF).

The patterns they laid out still hold up. Every one of them applies to modern applications.

Design Pattern types

Design patterns fall into these categories based on what they aim to achieve:

  • Creational: deal with object creation
  • Structural: deal with relationships between objects (or other entities)
  • Behavioural: deal with communication patterns between objects
  • Concurrency: deal with multi-threaded paradigms

Facade Pattern

The facade pattern is a structural design pattern. It hides the complexity of subsystems from the client and hands them a simple interface instead.

When to use the facade pattern?

The facade pattern fits situations where multiple subsystems (multiple APIs) exist and you want a single, unified interface to pull information from all of them. Think about a real-life example: applying for a mortgage. The bank needs to check fraud systems, credit score systems, other banks’ records, and more. The right implementation is an interface where someone enters the applicant’s details, presses an ‘eligible’ button, and that single action queries all the subsystems behind the scenes.

TypeScript implementation

The facade pattern could be implemented in any language, including vanilla JavaScript. But I thought it’d be interesting to see how a TypeScript version would look.

Should you want the JavaScript version, just transpile the TypeScript code to JavaScript, either to ES2015 or to ES5.

Facade example

Imagine we’re building a simple Football Manager type application. For players to sign someone to their team, a few checks need to happen. We’ll make two: verify the team has enough funds to buy the player, and confirm the player has no active contract. These two checks come from two different (imaginary) subsystems.

// subsystem1.ts
export class Contract {
  private contractTerminationDateexport class Contract {
  private contractTerminationDate: Date;
  constructor(terminationDate: Date) {
    this.contractTerminationDate = terminationDate;
  }
  checkActiveContract(date: Date): boolean {
    if (date < this.contractTerminationDate) {
      return true;
    } else {
      return false;
    }
  }
}


// subsystem2.ts
export class TeamFunds {
  private totalFunds: number;
  constructor(total: number) {
    this.totalFunds = total;
  }
  checkFunds(transferFee: number): boolean {
    if (transferFee < this.totalFunds) {
      return true;
    } else {
      return false;
    }
  }
}

Two classes. One checks whether a player has an active contract, the other checks if a team has enough money to buy them.

Now we create the facade to wrap both subsystems behind a single interface:

// facade.ts
import { Contract } from './subsystem1';
import { TeamFunds } from './subsystem2';

export class Facade {
  private contract: Contract = new Contract(
    new Date(new Date().setDate(new Date().getDate() + 10))
  );
  private funds: TeamFunds = new TeamFunds(200000);

  private playerContract: Date;
  private playerPrice: number;

  constructor(playerPrice: number) {
    this.playerContract = new Date();
    this.playerPrice = playerPrice;
  }

  buyPlayer(): void {
    if (this.contract.checkActiveContract(this.playerContract)) {
      console.log('Player has active contract');
    } else {
      console.log('Player has no active contract');
      if (!this.funds.checkFunds(this.playerPrice)) {
        console.log('Player is too expensive to buy.');
      } else {
        console.log('Player can be bought.');
      }
    }
  }
}

We create a new contract for a player (setting the expiry date to today + 10 days). The facade class exposes a single buyPlayer() method. That’s the only method the client needs to call. It checks both subsystems and returns a message.

Here’s how you’d use it:

// app.ts
import { Facade } from './facade';

const facade: Facade = new Facade(200001);
facade.buyPlayer();

The implementation is clean. You don’t need to think about the subsystems at all.