Services: add layers #
The most common modifications to a system of Services involve supplementary system-wide layers which compensate for the inability of the Services to share anything among themselves:
- A Middleware knows of all the deployed service instances. It mediates communication between them and may manage their scaling and failure recovery.
- Sidecars [DDS] of a Service Mesh make a virtual layer of shared libraries for the Microservices it hosts.
- A Shared Database simplifies the initial phases of development and provides data consistency and interservice communication.
- Proxies stand between the system and its clients and take care of shared aspects that otherwise would need to be implemented by every service.
- An Orchestrator is the single place for the high-level logic of every use case.
Those layers may also be merged into Combined Components:
- Message Bus is a Middleware that supports multiple protocols.
- API Gateway combines Gateway (a kind of Proxy) and Orchestrator.
- Event Mediator is an orchestrating Middleware.
- Shared Event Store combines Middleware and Shared Repository.
- Enterprise Service Bus (ESB) is an orchestrating Message Bus.
- Space-Based Architecture employs all the four layers: Gateway, Orchestrator, Shared Repository and Middleware.
Add a Middleware #
Patterns: Middleware, Services.
Goal: take care of scaling, recovery, and interservice communication without programming it.
Prerequisite: communication between the services is uniform.
Distributed systems may fail in a zillion ways. You want to ruminate neither on that nor on heisenbugs. And you probably want to have a framework for scaling the services and restarting them after failure. Get a third-party Middleware! Let your programmers write the business logic, not infrastructure.
Pros:
- You don’t invest your time in infrastructure.
- Scaling and error recovery are made easy.
Cons:
- There may be a performance penalty which becomes worse for uncommon patterns of communication.
- The Middleware may be a single point of failure.
Further steps:
- Use a Service Mesh for dynamic scaling and as a way to implement shared aspects.
Use a Service Mesh #
Patterns: Service Mesh (Mesh, Middleware), Sidecar (Proxy), Services.
Goal: support dynamic scaling and interservice communication out of the box; share libraries among the services.
Prerequisite: service instances are mostly stateless.
The Microservices architecture boasts dynamic scaling under load thanks to its Mesh-based Middleware. It also allows for the services to share libraries in Sidecars [DDS] – additional containers co-located with each service instance – to avoid duplication of generic code among the services.
Pros:
- Dynamic scaling and error recovery.
- Available out of the box.
- Provides a way to implement shared aspects (cross-cutting concerns) once and use the resulting libraries in every service.
Cons:
- Performance degrades because of the complex distributed infrastructure.
- You may suffer vendor lock-in.
Use a Shared Repository #
Patterns: Shared Repository, Services.
Goal: let the services share data, don’t invest in operating multiple databases.
Prerequisite: the services use a uniform approach to persisting their data.
You don’t really need every service to have a private database. A shared one is enough in many cases.
Pros:
- It is easy for the services to share and synchronize data.
- Lower operational complexity.
Cons:
- All the services depend on the database schema which becomes hard to alter.
- The single database will limit performance of the system.
- It may also become a single point of failure.
Further steps:
- Space-Based Architecture scales the data layer but it is a simple key-value store.
- Polyglot Persistence is about having multiple specialized databases.
Add a Proxy #
Goal: use a common infrastructure component on behalf of your entire system.
Prerequisite: the system serves its clients in a uniform way.
Putting a generic component between the system and its clients helps the programmers concentrate on business logic rather than protocols, infrastructure or even security.
Pros:
- You get a choice of generic functionality without investing development time.
- It is an additional layer that isolates your system from both clients and attackers.
Cons:
- There is a latency penalty caused by the extra network hop.
- Each Proxy may be a single point of failure or at least needs some admin oversight.
Further steps:
- You can always add another kind of Proxy.
- If there are multiple clients that differ in their protocols, you can employ a stack of Proxies per client, resulting in Backends for Frontends.
Use an Orchestrator #
Patterns: Orchestrator, Services.
Goal: have the high-level logic of use cases distilled as intelligible code.
Prerequisite: the use cases comprise sequences of high-level steps (which is very likely to be true for a system of subdomain services).
When a use case jumps over several services in a dance of choreography, there is no easy way to understand it as there is no single place to see it in the code. It may be even worse with Pipelined systems where use cases are embodied in the structure of event channels between the components.
Extract the high-level business logic from the choreographed services or their interconnections and put it into a dedicated component.
Pros:
- You are not limited in the number and complexity of use cases anymore.
- Global use cases become much easier to debug.
- You have a new team dedicated to the interaction with customers, freeing the other teams to study their parts of the domain or work on improvements.
- Many changes in the high-level logic can be implemented and deployed without touching the main services.
- The extra layer decouples the main services from the system’s clients and from each other.
Cons:
- There is a performance penalty because the number of messages per use case doubles.
- The Orchestrator may become a single point of failure.
- Some flexibility is lost as the Orchestrator couples qualities of the services.
Further steps:
- If there are several clients that strongly vary in workflows, you can apply Backends for Frontends with an Orchestrator per client.
- If the Orchestrator grows too large, it can be divided into layers, services or both, the latter option resulting in a Top-Down Hierarchy.
- The Orchestrator can be scaled and can have its own database.