Component

Last updated 18 days ago

Components are the central building block in Polylith. They allow us to divide our systems into encapsulated and composable blocks of code.

Components achieve their encapsulation and composability by separating their implementation from their interface. The metaphor visualises this by showing the component with two layers:

The light-green layer is the interface and the dark-green layer is the implementation

A well designed component has a single responsibility, a descriptive name, and exposes a clear set of functionality that only relates to its responsibility.

To give you a few examples, a component can:

  • be a domain entity, such as user, account or invoice

  • manage integration with an external system

  • handle a specific infrastructure feature, such as email, logging or storage

You can look at the source code for the components in the RealWorld example app to see some examples of domain and infrastructure components.

Let's take a closer look at the RealWorld's user component to see how its code relates to the metaphor.

Interface

A component's interface is a single namespace which exposes all of the component's public functionality to other building blocks.

If we flip the user component over, we can see its interface:

This 'user' component exposes four function signatures

Just as a function signature is a function's contract towards other functions, a component's interface is its contract towards other building blocks.

If we examine the source code for this user component's interface, we see that it exposes many functions.

To simplify the metaphor, we pretend that the interface only has four functions: login!, register!, user-by-token and update-user!

The yellow signature hole represents the 'login!' function

Notice that the login! function passes through to a matching function (core/login!) from the component's implementation layer. Let's pop open the component to see how this works:

The interface's dependency on the implementation's 'login!' function

Now let's take a look at how user's implementation layer.

Implementation

A component's implementation layer contains all the logic required to fulfil its interface. This logic can be divided across any number of namespaces.

Remember that public functions in implementation namespaces are only exposed to the component's interface and not to other building blocks.

The 'login!' function exposed from the 'core' namespace in the implementation

If we examine the source code for all three of this user component's implementation namespaces (core, spec, and store), we see that they expose many functions to the component's interface.

To simplify the metaphor, we only show four function signatures in the implementation layer.

Just as with the function and namespace metaphors, component dependencies are represented as coloured studs with a particular shape:

The orange hexagonal stud is a dependency on the `check` function in a cryptography library

If we examine the source code for all three of this user component's implementation namespaces (core, spec, and store), we see that they have many dependencies.

To simplify the metaphor, we only show four dependencies.

We'll return to components in the Architecture section, so don't worry if you haven't understood everything. For now, let's keep exploring the metaphor by looking at a base.