February 17, 2021
0 strokes bestowed

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.

Sharing this article on Twitter is a great way to help me out and I really appreciate the support.
+0
Liked the post? Click the beard a few times.
Tags
Newer Post: How to Use React Context Effectively
Older Post: useEncapsulation

Let's talk some more about JavaScript, React, and software engineering.

I write a newsletter to share my thoughts and the projects I'm working on. I would love for you to join the conversation. You can unsubscribe at any time.

Introduction to State Machines and XState Logo
Introduction to State Machines and XState

Check out my courses!

Liked the post? You might like my video courses, too. Click the button to view this course or go to Courses for more information.
View on egghead.io
Kyle Shevlin's face, which is mostly a beard with eyes
Kyle Shevlin is a software engineer who specializes in JavaScript, React and front end web development.