Home >
Extracting Microservices from a Ports-and-Adapters Architecture
A typical application controlling a machine interfaces with users, with the actuators and sensors of the machine, with cameras, with the IoT cloud, with local and remote databases, with a fleet management server, and with other system parts. The application core, which implements the business rules, must not know which technology components are used to communicate with the user, machine, cloud, or database. Well-defined ports (interfaces) hide the implementation of the adapters (components) from the core. This is the ports-and-adapters architecture in a nutshell.
The ports-and-adapters architecture leads to loosely coupled and cohesive components with well-defined interfaces. This sounds a lot like a microservice architecture, doesn’t it? It does, indeed—with one difference. Microservices run in their own processes (often on different computers) and talk to each other over a lightweight protocol. In contrast, the ports-and-adapters architecture is mostly implemented in a monolithic application running in a single process. The core and adapters communicate through function calls.
We can find good reasons to extract adapters from the monolithic application as services. The application keeps a proxy for the extracted service. The proxy and the service implement the same port. The proxy and service are very much like the two USB ports connected by a USB cable. Here are two examples.
These days, the main HMI application should have a component performing OTA updates of the embedded Linux system. OTA updates require root privileges. For security reasons, user applications should not have root privileges. If we extract an update service from the application, the application can run with user privileges and the service with root privileges. Only a properly authenticated application may use the service.
HMIs of agricultural and construction machines provide functionality for normal operation by the driver and for diagnosis by a support technician. When we split the monolith into two applications accordingly, we face a resource-sharing problem. If one application reads a message from the CAN bus, the other application will never see this message. The solution is the introduction of a service doing all the CAN message processing and replicating the messages for all the interested applications.
As long as it makes sense, we can transform the adapters from our applications into services. As the services will most likely implement the ports-and-adapters architecture, we may be able to identify even more services. The operating-system and hardware-abstraction layers are fertile ground for more services. The applications and services use inter-process communication like DBus or gRPC to talk to each other.
We have transformed a few monolithic applications into several smaller applications and services talking to each other over a lightweight protocol. In short, we end up with a microservice architecture, where each microservice (including applications) implements the ports-and-adapters architecture.