Smart Endpoints and Dumb Pipes
As your engineering organization transitions from building monolithic architectures to building microservices architectures, it will need to understand how to enable communications between microservices. In a monolith, the various components are all in the same process. In a microservices environment, components are separated by hard boundaries. At scale, a microservices environment will often have the various components distributed across a cluster of servers so that they are not even necessarily collocated on the same server.
This means there are two primary forms of communication between services:
-
Request/Response – One service explicitly invokes another service by making a request to either store data in it or retrieve data from it. For example, when a new user creates an account, the user service makes a request to the billing service to pass off the billing address from the user’s profile so that that billing service can store it.
-
Publish/Subscribe – Event-based architecture where one service implicitly invokes another service that was watching for an event. For example, when a new user creates an account, the user service publishes this new user signup event and the email service that was watching for it is triggered to email the user asking them to verify their email.
One architectural pitfall that generally leads to issues later on is
attempting to solve communication requirements by building your own
complex enterprise service bus for routing messages between
microservices. It is much better to use a message broker such as
Amazon MSK
Network communication often plays a central role in distributed systems. Service
meshes
Sidecars can also be used to build monitoring solutions. When you are running microservices using Kubernetes, there are multiple observability strategies, one of them is using sidecars. Due to the modular nature of the sidecars, you can use it for your logging and monitoring needs. For example, you can setup FluentBit
The core benefit of building smart endpoints and dumb pipes is the ability to decentralize the architecture, particularly when it comes to how endpoints are maintained, updated, and extended. One goal of microservices is to enable parallel work on different edges of the architecture that will not conflict with each other. Building dumb pipes enables each microservice to encapsulate its own logic for formatting its outgoing responses or supplementing its incoming requests.
The following are the key factors from the twelve-factor app pattern methodology that play a role in building smart endpoints and dumb pipes:
-
Port Binding – Services bind to a port to watch for incoming requests and send requests to the port of another service. The pipe in between is just a dumb network protocol such as HTTP.
-
Backing services – Dumb pipes allow a background microservice to be attached to another microservice in the same way that you attach a database.
-
Concurrency – A properly designed communication pipeline between microservices allows multiple microservices to work concurrently. For example, several observer microservices may respond and begin work in parallel in response to a single event produced by another microservice.