February 17, 2021

Facade Pattern

or why this pattern might save your bacon
edit

We live in exciting times (for JavaScript developers). There’s almost a library for everything you can imagine on npm and you can add packages to your projects so quickly. It’s really quite a bit of fun when you take a second to think about it.

However, this ability to quickly grab and use packages hides some technical debt you might be creating when you do so. There’s a reason these packages are called “dependencies”, and if you’re not careful, you can wind up dependent upon something that is terribly difficult to manage or remove later.

To that end, I want to introduce you to a pattern of programming called the “facade pattern”. I get the term from the “Gang of Four” (aka GoF) book, Design Patterns. It’s a classic piece of computer science literature on the topic of object oriented programming. I don’t talk much about OOP, but there are plenty of patterns and goodies to borrow from it. Use the link above if you’d like to check it out.

Essentially, the facade pattern is this: we add a layer of abstraction between the consumer of some code and the implementation of that code. In regards to packages like I’m describing above, it’s creating a wrapping function or class that uses the package (or several). There are at least 2 reasons to do this:

  • Greater control of the exposed API

Often a facade pattern is used to simplify a more complex class, object, or function’s implementation from the consumer of the facade.

  • To give yourself the flexibility to later swap packages and other implementation details without affecting the code that uses the facade

With the facade pattern, we’re able to hide details “under the hood”, changing them when necessary without affecting the API exposed by the facade and consumed by the rest of your application. I want to explore these two reasons in this post.

When to Use a Facade Pattern

There are some libraries so essential to your work that wrapping them in a facade would provide no benefit. If your project is built in React, it doesn’t make sense to wrap the React library in a facade because you’ll probably want access to its entire API and you won’t be swapping out any implementation details under the hood of your facade. React would be such an essential part of your project that if you change it, you want to have to change everything else, too.

On the other hand, libraries for fetching data, date formatting, and more can make good candidates for the facade pattern. Let’s look at fetching data as an example.

For a long time, and perhaps in many of your apps still, the axios library was a popular way to fetch data. You might have used it directly in some code somewhere like this:

import React from 'react'
import axios from 'axios'

function DetailPage({ id }) {
  const [data, setData] = React.useState(null)

  React.useEffect(() => {
    axios
      .get(`your/api/url/pages/${id}`)
      .then(res => res.json())
      .then(json => {
        setData(json)
      })
  }, [id])

  return data !== null && <div>{JSON.stringify(data, null, 2)}</div>
}

Now, imagine you have dozens, maybe hundreds or more components that are similar. They’re fetching and posting data around your app, all using axios directly.

One day, your team decides that you want to drop the axios dependency and switch to using fetch instead. What’s your immediate thought? I bet it’s something like, “Oh shit! Now I have to change every single place we use axios!”

Not a fun thought to have.

You might get lucky. You might be able to write a codemod that can manage the problem, but what if you never put yourself in that situation in the first place? If we use a facade pattern, we can hide axios under the hood, only consuming it in a single place. Something like:

// API.js
import axios from 'axios'

export default class API {
  get(url) {
    return axios.get(url)
  }

  post(url, options) {
    return axios.post(url, options)
  }

  // other methods, you get the idea...
}

// DetailPage.js
import React from 'react'
import API from './API'

function DetailPage({ id }) {
  const [data, setData] = React.useState(null)

  React.useEffect(() => {
    API.get(`your/api/url/pages/${id}`)
      .then(res => res.json())
      .then(json => {
        setData(json)
      })
  }, [id])

  return data !== null && <div>{JSON.stringify(data, null, 2)}</div>
}

Now if we want to replace axios, we simply update the implementation details of our API facade without having any affect on our components. Let’s replace it with native fetch:

// API.js
export default class API {
  get(url) {
    return fetch(url)
  }

  post(url, options) {
    return fetch(url, {
      method: 'POST',
      ...options,
    })
  }
}

Not only was it really painless to swap the implementation, it allowed us to manage some details and complexity that the consumers of our facade don’t need. In this case, we bake in the method value of POST on the post method, so that our users don’t have to change the instances of API.post() in the application.

Now, because of our facade, if another great library for fetching data comes along that has features we want over native fetch, we can update the implementation of API with out needing to change any of our components that use it.

Additional Thoughts

Admittedly, I haven’t had a chance to really try this pattern out on a massive codebase. I want to be upfront with you about that. I have been thinking about it a lot, though, because of some of the challenges I have seen at work. It’s really easy to stuck for years using some outdated library because it has become too essential to the application. I think using this pattern on key parts of your project could save you from a massive overhaul in a few years.

For example, right now your project might use the moment library, but you’d like to try date-fns instead. A facade would make that possible. You probably only use about 25% of the library anyways, so only expose what you need from it. With a facade already in place, it will certainly make it easy to switch to Temporal when it lands.

There are some tradeoffs to consider, too. You are adding a layer of abstraction that will likely require documentation. How else will you and your team know how to use the facade? Also, creating code means you’re responsible for its maintenance and improvements. It’s not as simple as just using another method or function from the library. You have to put in the work to update the facade if there are features of the underlying library you want to use. But it still might be a tradeoff worth making, hiding complexity that can be swapped or improved in the future. You’ll have to decide for yourself.


Liked the post?
Give the author a dopamine boost with a few "beard strokes". Click the beard up to 50 times to show your appreciation.
Need help with your software problems?

My team and I are ready to help you. Hire Agathist to build your next great project or to improve one of your existing ones.

Get in touch
Kyle Shevlin's face, which is mostly a beard with eyes

Kyle Shevlin is the founder & lead software engineer of Agathist, a software development firm with a mission to build good software with good people.

Agathist
Good software by good people.
Visit https://agath.ist to learn more
Sign up for my newsletter
Let's chat some more about TypeScript, React, and frontend web development. Unsubscribe at any time.
Logo for Data Structures and Algorithms
Data Structures and Algorithms
Check out my courses!
If you enjoy my posts, you might enjoy my courses, too. Click the button to view the course or go to Courses for more information.