Orchestrator: #
Employing an Orchestrator has two pitfalls:
- The system becomes slower because too much communication is involved.
- A single Orchestrator may be found to be too large and rigid.
There is one way to counter the first point and more ways to solve the second one:
- Subdivide the Orchestrator by the system’s subdomains, forming Layered Services and minimizing network communication.
- Subdivide the Orchestrator by the type of client, forming Backends for Frontends.
- Add another layer of orchestration.
- Build a Top-Down Hierarchy.
Subdivide to form Layered Services #
Patterns: Orchestrated Three-Layered Services (Layered Services (Services, Layers)).
Goal: simplify the Orchestrator, let the service teams own orchestration, decouple forces for the services, improve performance.
Prerequisite: the high-level (orchestration) logic is weakly coupled between the subdomains.
If the orchestration logic mostly follows the subdomains, it may be possible to subdivide it accordingly. Each service gets a part of the Orchestrator that mostly deals with its subdomain but may call other services when needed. As a result, each service orchestrates every other service. Still, a large part of orchestration becomes internal to the service, meaning that fewer calls over the network are involved.
Pros:
- You subdivide the large Orchestrator codebase.
- Performance is improved.
- The services become more independent in their quality attributes.
Cons:
- You lose the client-facing orchestration team – now each service’s team will need to face its clients.
- Service teams become interdependent (while having equal rights), which may result in slow development and suboptimal decisions.
- There is no way to share code between different use cases or even take a look at all of the scenarios at once.
Further steps:
- CQRS Views [MP] or a Query Service [MP] help a service access and join data that belongs to other services, further reducing the need for interservice communication.
Subdivide to form Backends for Frontends #
Patterns: Backends for Frontends, Orchestrator.
Goal: simplify the Orchestrator, employ a team per client type, decouple qualities for clients.
Prerequisite: clients vary in workflows and forces.
When use cases for clients vary, it makes sense for each kind of client to have a dedicated Orchestrator.
Pros:
- The smaller Orchestrators are independent in qualities, technologies and teams.
- The smaller Orchestrators are … well, smaller.
Cons:
- There is no good way to share code between the Orchestrators.
Further steps:
- You may want to add client-specific Proxies and, maybe, co-locate them with the Orchestrators to avoid the extra network hop.
- Adding another shared Orchestrator below the ones dedicated to clients creates a place for sharing functionality among the Orchestrators.
- If you are running Microservices over a Service Mesh, Sidecars [DDS] may help to share generic code.
Add a layer of orchestration #
Patterns: Orchestrator, Layers.
Goal: implement simple use cases quickly, while still supporting complex ones.
Prerequisite: use cases vary in complexity.
You may use two or three orchestration frameworks (engines) which differ in complexity. A simple declarative tool may be enough for the majority of user requests, reverting to custom-tailored code for rare complex cases.
Pros:
- Simple scenarios are easy to write.
- You retain good flexibility with hand-written code when it is needed.
Cons:
- Requires learning multiple technologies.
- More components mean more failures and more administration.
- Performance of complex requests may suffer from more indirection.
Further steps:
- Divide one or more of the resulting orchestration layers to form Layered Services, Backends for Frontends, Hierarchy, or Cell-Based Architecture.
Form a hierarchy #
Patterns: Top-Down Hierarchy (Hierarchy).
Goal: simplify the Orchestrator and, if possible, the services.
Prerequisite: the domain is hierarchical.
If an Orchestrator becomes too complex, some domains (e.g. IIoT or telecom) encourage using a tree of Orchestrators, with each layer taking care of one aspect of the domain, serving the most generic functionality at the root.
Pros:
- Multiple specialized teams and technologies.
- Small codebase per team.
- Reasonable testability.
- Some decoupling of quality attributes.
Cons:
- Hard to debug.
- Poor latency in global scenarios unless several layers of the hierarchy are colocated.