Facades and backends#
This page explains what “facades” and “backends” mean in the context of Pigweed and provides guidelines on when to use them.
What are facades and backends?#
Let’s take a top-down approach, starting with high-level definitions. In the context of a Pigweed module:
A facade is an API contract of a module that must be satisfied at compile-time, i.e. a swappable dependency that changes the implementation of an API at compile-time.
A backend is an implementation of a facade’s contract.
Usually, the facade and the backend are in different modules. One module
exposes the facade contract, and another module is the backend that implements
the facade contract. The naming pattern that Pigweed follows is
Facades, by design, don’t need to be configured until they’re actually used. This makes it significantly easier to bring up features piece-by-piece.
Example: pw_log and pw_log_string#
Here’s a step-by-step walkthrough of how a real backend module implements another module’s facade.
#include "pw_log/log_backend.h"line in //pw_log/public/pw_log/log.h represents the facade contract of
pw_log_string is a backend module, It implements the
pw_log/log_backend.hfacade contract in //pw_log_string/public_overrides/pw_log_backend/log_backend.h.
In the build system there is a variable of some sort that specifies the backend. In Bazel there’s a label flag at
//targets:pw_log_backend. In CMake there’s a
pw_log_BACKENDvariable set to
pw_log_string. In GN there’s a
pw_log_BACKENDvariable set to
There are a few more steps needed to get
pw_log_string hooked up as the
pw_log but they aren’t essential for the current discussion.
See Get started (GN) for the details.
Example: Swappable OS libraries#
The facade and backend system is similar to swappable OS libraries: you can
write code against
libc (for example) and it will work so long as you have
an object to link against that has the symbols you’re depending on. You can
swap in different versions of
libc and your code doesn’t need to know about
A similar example from Windows is
d3d9.dll. Developers often swap this DLL
with a different library like ReShade to customize shading behavior.
Can a module have multiple facades?#
Yes. The module-to-facade relationship is one-to-many. A module can expose 0, 1, or many facades. There can be (and usually are) multiple backend modules implementing the same facade.
Is the module facade the same thing as its API?#
No. It’s best to think of them as different concepts. The API is the interface that downstream projects interact with. There’s always a one-to-one relationship between a module and its API. A facade represents the build system “glue” that binds a backend to an API.
This is a common point of confusion because there are a few modules that blur this distinction. Historically, the facade comprised the API as well as the build system concept. We’re moving away from this perspective.
Are Pigweed facades the same as the GoF facade design pattern?#
There are some loose similarities but it’s best to think of them as different things. The goal of the Gang of Four facade pattern is to compress some ugly, complex API behind a much smaller API that is more aligned with your narrow business needs. The motivation behind some Pigweed facades is loosely the same: we don’t want a device HAL to leak out into your include paths when a facade is implemented.
Why does Pigweed have facades?#
Pigweed’s facades are basically a pattern that builds off the ideas of link-time subsitution. Link-time subsitution only allows you to replace one source file with another, whereas facades enable substituting program elements defined in header files.
Pigweed facades enable implementation flexibility with zero performance cost.
There are two ways to look at this. Continuing with the
Organizations can reuse more code across projects by adopting the
pw_logAPI as their logging layer. Facades enable each project to customize how its logs are handled.
For projects that want to integrate with
pw_logbut cannot adopt its API, facades can wrap the existing API which enables Pigweed to be compatible with the existing system.
Two of the major aspects of “implementation flexibility” enabled by facades are:
Portability. For example,
pw_syncneeds platform-specific implementations to work correctly.
Customized behavior. For example, you can make a fully portable
pw_logimplementation, but what makes it special is the ability to tune it to your needs.
Resolving facades and backends at compile-time enables:
Call-site control from backends.
Static allocation of backend-provided types.
Explicit backend includes so it’s visually obvious you’re poking through abstraction.
When to use facades and backends#
If you’re trying to use a Pigweed module, and that module exposes a facade, then you’ve got no choice: you’ve got to hook up a backend to fulfill that facade contract or else the module won’t work.
When to roll your own facades and backends#
You need a global function or macro.
You absolutely must avoid the overhead of virtual functions.
When to NOT roll your own facades and backends#
If you can afford the runtime cost of dependency injection, use that. In all other cases where link-time subsitution will work, use that. Only if the API contract requires a backend to provide a header (which link-time substitution doesn’t let you do) should you reach for a facde.
You’re trying to use globals to avoid dependency injection. Use the dependency injection! It makes testing much easier.
Your needs can be served by a standard mechanism like virtual interfaces. Use the standard mechanism.