“I was at a party when this guy came in and said “I’m a builder!”. I thought, ‘he knows how to make an entrance.’ But it turns out he was just putting up a facade.” – An unknown jokester
In my sophomore year of college, I took a class titled “Software Design.” What an ambitious name! Teaching a topic as huge as “Software Design” to a new programmer in a single semester is a nearly impossible feat. Despite the torrent of information from that semester, I held on to some fundamental concepts. Among these was the notion of a “Design Pattern.” The way my professor explained it was something like this:
“There are a lot of programmers who know more than you. They’ve figured out how to solve some common design problems. We call these prepackaged solutions ‘Design Patterns.’”
One of the textbooks for this class was titled Design Patterns: Elements of Reusable Object-Oriented Software. This book, also known as The Gang of Four Book, reminds me that many of the design problems I face have existing solutions. And many of these solutions are tried and tested (the book was published in 1994).
Today I’d like to pick out just one of the patterns mentioned in The Gang of Four Book: the facade pattern.
What is the Facade Pattern?
The clearest definition of the Facade pattern I could find is in The Gang of Four Book:
“[The Facade pattern provides] a unified interface to a set of interfaces in a subsystem.
Facade defines a higher-level interface that makes the subsystem easier to use.”
The book provides an example of a compiler subsystem. The facade over the compiler is a class with one method: “compile.” Within the compiler is a complex set of classes that do things such as scanning, parsing, and reading files. You might not need all those classes if you worked in this codebase. It’s more likely that you are interested in compiling some code, and so the facade is a useful abstraction on top of all of those other classes.
Here’s an example in a distributed computing environment: Suppose your web application needs to communicate with various data sources through web API’s. And let’s suppose you often need different pieces of data from different sources simultaneously.
You might create one module for each web API you need to work with. But then, getting the data involves the complexity of using multiple modules. What if we created a single module that defined an interface that stitched all the data sources together? This would be a facade. The parent module creates “a unified interface to a set of interfaces in a subsystem.” This unified interface should be easier to use and reduces the subsystem’s complexity.
Given this definition, you might want to reach for the Facade pattern when you have a complex, potentially hard-to-use set of modules that can be stitched together into a single idea.
Common Misconceptions About The Facade Pattern
The Facade pattern is relatively straightforward compared to other patterns (I’m looking at you, Flyweight), but misconceptions still arise.
The first misconception about Facade is that it stops the user of the facade from reaching into the subsystem and taking advantage of its more nuanced and complex features. But it doesn’t have to be interpreted this way. In fact, the source material explicitly gives this caveat:
“[The Facade pattern] doesn’t prevent applications from using the subsystem classes if needed. Thus you can choose between ease of use and generality.”
Another misconception is that a facade might provide an interface over a single other interface. In this case, “facade” is an incorrect term. A facade provides a single interface to a set of interfaces—a one-to-many relationship. A one-to-one change of interfaces is closer to the Adapter pattern.
Understanding these misconceptions can help eliminate many pitfalls you might encounter when using a facade, which we’ll review in the next section.
Pitfalls of The Facade Pattern And How to Avoid Them
When using the Facade pattern, as with any abstraction, there are numerous ways that the abstraction can turn sour and make life harder for you and your team. Hiding the information about how a subsystem works behind a facade comes with tradeoffs. The interface is simpler, but now if something goes wrong behind the scenes, more work is required.
Ideally, we want to create abstractions that don’t hide information their users need, have good cohesion, and provide a useful decoupling from complexity. We won’t always hit the mark on these points. With that in mind, here are some potential pitfalls to avoid when considering the Facade pattern.
The Mega Facade
A mega facade is a facade that covers either too large of a subsystem or multiple subsystems. In this scenario, the facade takes on too much responsibility and has too many reasons to change. In other terms, it’s a “god object.” It becomes a breeding ground for bugs and a constant source of frustration for the team, who must update it every time a change happens within one of the subsystems.
You can avoid this pitfall by ensuring your facade is highly cohesive, creating an abstraction around a single idea or entity in the business domain.
The Naive Facade
A naive facade is a facade that degrades or eliminates some useful property of the subsystem which it covers. Typically, this is done unknowingly by the creator of the facade, thus the term “naive.”
For instance, suppose you are using a highly available yet sometimes inconsistent database. Your team notices that accessing the database is a complex process requiring multiple modules for authentication, networking, etc. You create a facade by stitching all these steps together into a single, easy-to-use interface.
For a while, things are working well until you start to get reports of bugs in your system due to the inconsistent nature of the database. Not wanting to fix the root of the problem by swapping databases, you utilize the facade to force consistency (waiting for all writes in the database to finish) and, by doing so, degrade the highly available characteristic of the database.
This facade might still be easy to use, but it now eliminates what was previously a desired attribute of the subsystem. Forcing consistency in this way increases the facade’s complexity and the application team’s maintenance burden by adding extra behavior they must now maintain – behavior that would ideally be the database’s purview.
Avoiding this pitfall requires a solid understanding of the attributes of the subsystem, which can’t always be expected since there are infinite sub-systems in software engineering, and many of them are deeply complex. The best we can do to avoid this pitfall is to research the system as thoroughly as is reasonable before deciding to write a facade on top of it.
It’s time to build your great idea.
The Distributed Facade
A typical facade is an interface local to your codebase. Some organizations, however, especially large organizations with many teams and codebases, may choose to put a single team in charge of stitching together many different subsystems for use by other teams. In a microservice environment, this team is creating a “macro service” that organizes many different microservices behind a single API. This “distributed facade” has the benefit of providing an easier-to-use method of getting the information that the other teams need, but it comes with increased complexity.
First, the distributed system likely requires large amounts of maintenance. When any subsystem team decides to change their API, that change could break many downstream systems if made haphazardly.
Second, the distributed facade suffers from the added complexity of adding another network hop to any data source for the end user. Every network call introduces a risk of failure, which must now be accounted for.
To be clear, the type of distributed facade I’m talking about here is not your typical API catalog software but rather a brand new API added to the catalog to simplify over-complex APIs that are also in the catalog. In this case, instead of opting for the facade API, I recommend simplifying the existing APIs so other teams can use them.
The Compounding Facade
When building any abstraction, it’s important to consider your users, whether end users or other programmers. If a facade’s interface is not considered carefully, it could lead to users deciding that they need to build a secondary abstraction on top of your facade. It’s here we get the compounding facade effect where the system devolves into needless abstractions on top of needless abstractions simply because the base abstraction was not quite useful enough. A “facade of facades,” if you will.
To avoid this issue, communicate with the people who will be using your interface. Ask them what kind of information they need or what type of tasks they need to do. Discuss their “ideal” interface for doing such things. Also, be aware that a facade should not limit access to the subsystem and presents an easier interface for most cases.
Just like any abstraction, facades come with tradeoffs. Being aware of these tradeoffs and making an appropriate decision based on them is a mark of a skilled software engineer. If you’re looking for more content about this design pattern, check out the continued reading below.