aboutsummaryrefslogtreecommitdiff
path: root/docs/facades.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/facades.rst')
-rw-r--r--docs/facades.rst153
1 files changed, 153 insertions, 0 deletions
diff --git a/docs/facades.rst b/docs/facades.rst
new file mode 100644
index 000000000..c067e3052
--- /dev/null
+++ b/docs/facades.rst
@@ -0,0 +1,153 @@
+.. _docs-facades:
+
+====================
+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?
+------------------------------
+.. _top-down approach: https://en.wikipedia.org/wiki/Bottom%E2%80%93up_and_top%E2%80%93down_design
+
+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
+``{facade_name}_{backend_name}``.
+
+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.
+
+.. _//pw_log/public/pw_log/log.h: https://cs.opensource.google/pigweed/pigweed/+/main:pw_log/public/pw_log/log.h
+.. _//pw_log_string/public_overrides/pw_log_backend/log_backend.h: https://cs.opensource.google/pigweed/pigweed/+/main:pw_log_string/public_overrides/pw_log_backend/log_backend.h
+
+* :ref:`module-pw_log` is a module that exposes a facade. The macros listed in
+ :ref:`module-pw_log-macros` represent the API of the module.
+* The ``#include "pw_log/log_backend.h"`` line in `//pw_log/public/pw_log/log.h`_
+ represents the facade contract of ``pw_log``.
+* :ref:`module-pw_log_string` is a backend module, It implements the
+ ``pw_log/log_backend.h`` facade 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 <https://bazel.build/extending/config#label-typed-build-settings>`_
+ at ``//targets:pw_log_backend``. In CMake there's a ``pw_log_BACKEND``
+ variable set to ``pw_log_string``. In GN there's a ``pw_log_BACKEND``
+ variable set to ``dir_pw_log_string``.
+
+.. note::
+
+ There are a few more steps needed to get ``pw_log_string`` hooked up as the
+ backend for ``pw_log`` but they aren't essential for the current discussion.
+ See :ref:`module-pw_log_string-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
+it.
+
+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?
+==============================================================
+.. _facade pattern: https://en.wikipedia.org/wiki/Facade_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 <https://bramtertoolen.medium.com/91ffd4ef8687>`_.
+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 ``pw_log`` example:
+
+* Organizations can reuse more code across projects by adopting the ``pw_log``
+ API as their logging layer. Facades enable each project to customize how
+ its logs are handled.
+* For projects that want to integrate with ``pw_log`` but 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_sync`` needs platform-specific
+ implementations to work correctly.
+* Customized behavior. For example, you can make a fully portable ``pw_log``
+ implementation, but what makes it special is the ability to tune it to your
+ needs.
+
+Why compile-time?
+=================
+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.