Bases are the foundational building block in Polylith. They are a system's gateway to communicate with the outside world.
Bases achieve their encapsulation by separating their implementation from their interface. The metaphor visualises this by showing the base with two layers:
Every base has one responsibility: to expose a system's functionality to the outside world.
In Polylith, you're free to use any API technology in your base, for example REST, gRPC, GraphQL or just the plain old command line.
One difference between bases and components is that a base has a flat bottom. This means that bases aren't composable; they can't be depended on by other building blocks. A base always sit at the bottom (the "base") of a Polylith system.
A base's API exposes all of the system's public functionality.
This system exposes itself to the outside world as a RESTful API, so the base is called
rest-api. The code snippet shows the
api namespace within the base. Just as with a component, Polylith asks you to put a base's interface (API) in one namespace, though you can spread the implementation across as many namespaces as you like.
The code snippet shows all the API's endpoints for interacting with the various domain entities that the system manages.
You can imagine other systems (Polylith or otherwise) connecting to a base with cables that mate with its endpoint sockets (representing remote API calls).
Similar to a component's interface, a base's API is a thin layer that simply passes through any endpoint calls to the equivalent function in its implementation. Here we've highlighted the yellow stud which represents the dependency on the 'login' function in the base's implementation namespace.
Hopefully you now understand that bases share a very similar structure to components. The way a component's interface connects with its implementation matches how a base's API is connects with its implementation.
Where they differ is that a base's API exposes endpoints to the outside world, whereas a component's interface exposes function signatures for other building blocks.
A base has a thin "pass through" implementation layer which calls the appropriate functions in other building blocks.
Here we see that this base's implementation of 'login' is just a thin wrapper around a dependency on the 'login!' function from the 'user' component. The base's implementation adds some validation which allows it to return the values expected by the API.