DEV Community

Tomasz Buszewski
Tomasz Buszewski

Posted on

Facade pattern in JavaScript

When building an application, we often face problems with external APIs. One has simple methods, other has them very complicated. Unifying them under one common interface is one of uses of the facade pattern.

Let's imagine we're building an application that displays information about movies, TV shows, music and books. For each of these we have a different vendor. They are implemented using various methods, have various requirements etc. We have to remember or keep noted how to query each type.

Or do we?

Facade pattern solves such problems. This is a common interface that have the same methods no matter what it used underneath.

I have prepared four different implementations of a resource serving:

class FetchMusic {
  get resources() {
    return [
      { id: 1, title: "The Fragile" },
      { id: 2, title: "Alladin Sane" },
      { id: 3, title: "OK Computer" }
    ];
  }

  fetch(id) {
    return this.resources.find(item => item.id === id);
  }
}

class GetMovie {
  constructor(id) {
    return this.resources.find(item => item.id === id);
  }

  get resources() {
    return [
      { id: 1, title: "Apocalypse Now" },
      { id: 2, title: "Die Hard" },
      { id: 3, title: "Big Lebowski" }
    ];
  }
}

const getTvShow = function(id) {
  const resources = [
    { id: 1, title: "Twin Peaks" },
    { id: 2, title: "Luther" },
    { id: 3, title: "The Simpsons" }
  ];

  return resources.find(item => item.id === 1);
};

const booksResource = [
  { id: 1, title: "Ulysses" },
  { id: 2, title: "Ham on Rye" },
  { id: 3, title: "Quicksilver" }
];
Enter fullscreen mode Exit fullscreen mode

They are named using different patterns, they are implemented better, worse, require more or less work. Because I didn't want to overcomplicate, I used simple examples with common response format. But nevertheless, this illustrates the problem well.

Design of our facade

To create a facade, first thing we need to know every aspect of every vendor. If one would require additional authorization, more parameters etc., this has to be implemented. This is an extra, and can be discarded when used with vendor that doesn't need it.

The building block of a facade is common interface. No matter which resource you want to query, you should only use one method. Of course, underneath it, there may lay more, but the public access should be limited and easy to use.

First, we're ought to decide the shape of the public API. For this example, a single getter should be enough. The only distinction here is the media type – book, movie etc. So the type will be our foundation.

Next, the common things among resources. Every one is queryable by ID. So, our getter should accept one parameter, an ID.

Building our facade

(I've decided to use a class for this, but this is not a requirement. Module consisting of object literal or even a collection of functions would probably suffice. Nevertheless, I like this notation.)

class CultureFasade {
  constructor(type) {
    this.type = type;
  }
}
Enter fullscreen mode Exit fullscreen mode

For starters, we define the type in the constructor. This means, that each of the facade instance will return different one. I know this might seem redundant, but it's more convenient that using a single instance of function and pass more arguments every time.

Okay, so the next thing is to define our public and private methods. For noting the "private" ones, I used the famous _ instead of the #, because CodePen doesn't support it yet.

As we said earlier, the only public method should be our getter.

class CultureFacade {
  constructor(type) {
    this.type = type;
  }

  get(id) {
    return id;
  }
}
Enter fullscreen mode Exit fullscreen mode

The base implementation (a skeleton) is there. Now, let's move to the actual meat of our class – private getters.

First off, we need to identify how each resource is queried:

  • Music requires a new instance and then passing and ID within the method get;
  • Movie's each instance returns the data, requires ID during initialization;
  • TV Show is just a single function that accepts an ID and returns the data;
  • Books are just a resource, we need to query it ourselves.

I know this step seemed tedious and unnecessary, but note that now we don't really have to figure out anything. The conceptual phase is very important during the design and build process.

Okay, music, go.

class CultureFacade {
  ...

  _findMusic(id) {
    const db = new FetchMusic();
    return db.fetch(id);
  }
}
Enter fullscreen mode Exit fullscreen mode

We've created a simple method that does exactly what we've described earlier. The remaining three will be just a formality.

class CultureFacade {
  ...

  _findMusic(id) {
    const db = new FetchMusic();
    return db.fetch(id);
  }

  _findMovie(id) {
    return new GetMovie(id);
  }

  _findTVShow(id) {
    return getTvShow(id);
  }

  _findBook(id) {
    return booksResource.find(item => item.id === id);
  }
}
Enter fullscreen mode Exit fullscreen mode

There, now we have all the methods to query our databases.

Getting the public API

One of the most important things I've learned when working as a programmer is to never rely on your vendors. You never know what might happen. They might get attacked, shut down, your company may stop paying for the service etc.

Knowing this, our getter should be also using a kind of facade. It should try to fetch the data, not assuming that it'll succeed.

So, let's write such method.

class CultureFacade {
  ...

  get _error() {
    return { status: 404, error: `No item with this id found` };
  }

  _tryToReturn(func, id) {
    const result = func.call(this, id);

    return new Promise((ok, err) => !!result
      ? ok(result)
      : err(this._error));
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's stop here for a minute. As you can see, this method is also private. Why? Public doesn't benefit from it. It require the knowledge of other private methods. Next, it requires two parameters – func and id. While the latter is quite obvious, the former is not. Okay, so this will accept a function (or rather our class' method) to run. As you can see, the execution is being assigned to result variable. Next, we're checking whether it succeed and we're returning a Promise. Why such baroque construct? Promises are very easy to debug and execute, with the async/await or even plain then/catch syntax.

Oh, and the error. Nothing big, just a getter returning a message. This can be more elaborate, has more information etc. I didn't implement anything fancy, since this doesn't really require it, and our vendors doesn't have any errors to base upon either.

Okay, so what we have now? The private methods for querying vendors. Our inner facade to try to query. And our public getter skeleton. Let's expand it into a living being.

Since we're relying on predefined types, we'll use the ever-so-powerful switch statement.

class CultureFacade {
  constructor(type) {
    this.type = type;
  }

  get(id) {
    switch (this.type) {
      case "music": {
        return this._tryToReturn(this._findMusic, id);
      }

      case "movie": {
        return this._tryToReturn(this._findMovie, id);
      }

      case "tv": {
        return this._tryToReturn(this._findTVShow, id);
      }

      case "book": {
        return this._tryToReturn(this._findBook, id);
      }

      default: {
        throw new Error("No type set!");
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

A note about defining string types

Our types are written by hand. This isn't the best practice. It should be defined aside, so no typo will cause the error. Why not, let's do it.

const TYPE_MUSIC = "music";
const TYPE_MOVIE = "movie";
const TYPE_TV = "tv";
const TYPE_BOOK = "book";

class CultureFacade {
  constructor(type) {
    this.type = type;
  }

  get(id) {
    switch (this.type) {
      case TYPE_MUSIC: {
        return this._tryToReturn(this._findMusic, id);
      }

      case TYPE_MOVIE: {
        return this._tryToReturn(this._findMovie, id);
      }

      case TYPE_TV: {
        return this._tryToReturn(this._findTVShow, id);
      }

      case TYPE_BOOK: {
        return this._tryToReturn(this._findBook, id);
      }

      default: {
        throw new Error("No type set!");
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

These types should be exported and then used application-wide.

Usage

So, it seems that we're done here. Let's take it for a spin!

const music = new CultureFacade(TYPE_MUSIC);
music.get(3)
    .then(data => console.log(data))
    .catch(e => console.error(e));
Enter fullscreen mode Exit fullscreen mode

Very simple implementation using then/catch. It simply logs out the album we were looking for, Radiohead's OK Computer in this case. Great listen, by the way.

Okay, but let's try to get an error as well. None of our vendors can really tell anything when they don't have the requested resource. But we can!

const movies = new CultureFacade(TYPE_MOVIE);
movie.get(5)
    .then(data => console.log(data))
    .catch(e => console.log(e));
Enter fullscreen mode Exit fullscreen mode

And what do we have here? Oh, the console throws an error, saying "No item with this id found". Actually, it's a JSON-compliant object! Yeah!

The facade pattern, as you can see, can be very powerful when used properly. It can be really beneficial when you have multiple similar sources, similar operations etc., and want to unify the usage.

All the code is available on CodePen.

Top comments (15)

Collapse
 
saeedzandian profile image
Saeed • Edited

amazing! thanks for great article!
just out of curiosity, would it be nicer if we use symbol instead of string literals for different types?
and then instead of switch using strategy pattern using symbol as object key to select intended function

Collapse
 
tomekbuszewski profile image
Tomasz Buszewski

You can use Symbol, example I've brought is purely illustrational. Or you can try TypeScript and use enums. Choose whatever you're most comfortable with, but remember to stick with it :)

Collapse
 
fabriziobertoglio1987 profile image
fabriziobertoglio1987 • Edited

in ruby would be .send(:"_findMusic", id), in JS _findMusic.call(this, id)?? or something similar??... thanks

Thread Thread
 
tomekbuszewski profile image
Tomasz Buszewski

Hi fabriziobertoglio1987, can you rephrase your question? I don't seem to understand it fully :)

Thread Thread
 
fabriziobertoglio1987 profile image
fabriziobertoglio1987

I was referring to use either Symbol or other code like .call or other Object method to delete your switch statement and replace it with a loop

developer.mozilla.org/en-US/docs/W...

developer.mozilla.org/en-US/docs/W...

Thread Thread
 
tomekbuszewski profile image
Tomasz Buszewski

Why would you obscure such a simple statement with a loop? It would be shorter, true, but more complex to the reader.

Thread Thread
 
fabriziobertoglio1987 profile image
fabriziobertoglio1987 • Edited

It is a common practive in Ruby. Right now would be done for exercise and experimentation, but it would make a lot of sense when you don't have only 1 switch statement and your code grows in complexity.

I don't like switch or if statements

keithcirkel.co.uk/metaprogramming-...

Thread Thread
 
tomekbuszewski profile image
Tomasz Buszewski

I don't know about Ruby, but in fact some languages don't even have switch statements, for example, Python.

Creating a code that'll "know" which function to run without you telling it to with a direct statement is nice, but always try to think about some junior devs that might join your team. I had this phase of writing super complex code that only senior developers understood. But I quickly backed away, as I wasn't able to go on vacation, since I was the one person in the current team that understood how it all worked.

Thread Thread
 
fabriziobertoglio1987 profile image
fabriziobertoglio1987 • Edited

(But I quickly backed away, as I wasn't able to go on vacation, since I was the one person in the current team that understood how it all worked) :-D

Yes, I guess everything has its downside.

I think using metaprogramming sometimes is literally an alternative to using design patterns. For example the Factory Design Pattern allows you dinamically create objects, but you could solve the same problem with inheritance and metaprogramming

alligator.io/js/factory-pattern/

but the code would me much harder to read and does not make sense in Frontend Development.

The good thing is that Ecmascript now is really advanced and you are free to do anything with it. It is very good for Frontend, but also backend development.

Collapse
 
metcoder profile image
Carlos Fuentes

Good topic, great explanation. Thanks!

Collapse
 
wh0ismi profile image
wh0ismi

What theme is this?

Collapse
 
tomekbuszewski profile image
Tomasz Buszewski

Hey wh0ismi, what do you mean? If you are referring to the code theme used on dev.to, I am not sure, but looks like Monokai.

Collapse
 
silverium profile image
Soldeplata Saketos

TypeScript and enums could help as well

Collapse
 
hanneslim profile image
hanneslim

When building the facade wouldn't it be better to use the rxjs observables? Like in this example: nerd-corner.com/how-to-build-a-pus...

Collapse
 
jamalabd profile image
Jamal Abdul-Majid

Nice! like a beautiful reducer.