Hierarchy

Hierarchy #

Hierarchy

Command and conquer. Build a tree of responsibilities.

Variants:

By structure:

  • Polymorphic children,
  • Functionally distinct children.

By direction:

Structure: A tree of components.

Type: Main or extension.

Benefits Drawbacks
Very good in decoupling logic Global use cases may be hard to debug
Supports multiple development teams and technologies Poor latency for global use cases
Components may vary in qualities Operational complexity
Low-level components are easy to replace Slow start of the project

References: None good I know of.

Though not applicable to every domain, hierarchical decomposition is arguably the best way to distribute responsibilities between components. It limits the connections (thus the number of interfaces and contracts to keep in mind) of each component to its parent and a few children, allowing for the building of complex (and even complicated) systems in a simple way. The hierarchical structure is very flexible as it features multiple layers of indirection (and often polymorphism), which makes addition, replacement, or stubbing/mocking of leaf components trivial. It is also quite fault-tolerant as individual subtrees operate independently.

This architecture is not ubiquitous because few domains are truly hierarchical. Its high fragmentation results in increased latency and poor debugging experience. Moreover, component interfaces should be designed beforehand and are hard to change.

Performance #

No kind of distributed hierarchy is latency-friendly as many use cases involve several network hops. The fewer layers of the hierarchy are involved in a task, the better its performance.

Hierarchy - speed

Maintaining high throughput usually requires deploying multiple instances of the root component, which is not possible if it is stateful (in control systems) and the state cannot be split into Shards. The following tricks may help unloading the root:

  • Aggregation (first met in Layers): a node of a hierarchy collects reports from its children, aggregates them into a single package, and sends the aggregated data up to its parent. This greatly reduces traffic to the root in large IIoT networks.
  • Delegation (resembles strategy injection and batching for Layers): a node should try to handle all the low-level details of communication with its children without consulting its parent node. For a control system that means that its mid-level nodes should implement control loops for the majority of incoming events. For a processing system that means that its mid-level nodes should expose coarse-grained interfaces to their parent(s) while translating each API method call into multiple calls to their child nodes.
  • Direct communication channels (previously described for Orchestrator): if low-level nodes need to exchange data, their communication should not always go through the higher-level nodes. Instead, they may negotiate a direct link (open a socket) that bypasses the root of the hierarchy.

Hierarchy - optimizations

Dependencies #

A parent node would usually define one (for polymorphic children) or more (otherwise) SPIs for its child nodes to implement. The interfaces reside on the parent side because low-level nodes tend to be less stable (new types of them are often added and old ones replaced) therefore we don’t want our main business logic to depend on them.

Hierarchy

Applicability #

Hierarchy fits with:

  • Large and huge projects. The natural division by both level of abstractness and subdomain allows for using smaller modules, ideally with intuitive interfaces. The APIs for each team to learn are limited to just a few which their component interacts with directly.
  • Systems of hardware devices. Real-world IIoT systems may use a hierarchy of controllers to benefit from autonomous decision-making and data aggregation.
  • Customization. The tree-like structure provides opportunities for easy customization. A medium-sized hierarchical system may integrate hundreds of leaf types.
  • Survivability. A distributed hierarchy retains limited functionality even if several of its nodes fail.

Hierarchy fails with:

  • Cohesive domains. Horizontal interactions (those between nodes that belong to the same layer) bloat interfaces as they have to pass through parent nodes.
  • Quick start. Finding (and verifying) a good hierarchical domain model may be hard if at all possible. Debugging an initial implementation will not be easy.
  • Low latency. System-wide scenarios involve many cross-component interactions which are slow in distributed systems.

Relations #

Hierarchy

Hierarchy:

Variants by structure (may vary per node) #

Hierarchy comes in various shapes as it is more of a design approach than a ready-to-use pattern:

Polymorphic children #

All the managed child nodes expose the same interface and contract. This tends to simplify the implementation of the parent node and resembles inheritance of OOD.

Example: a fire alarm system may treat all of its fire sensors as identical devices, even though the real hardware comes from many manufacturers.

Functionally distinct children #

The managing node is aware of several kinds of children that vary in their APIs and contracts, just like with composition in OOD.

Example: an intrusion alarm logic may need to discern between cat-affected IR sensors and mostly cat-proof glass break detectors.

Variants by direction #

Top-Down Hierarchy, Orchestrator of Orchestrators, Presentation-Abstraction-Control (PAC), Hierarchical Model-View-Controller (HMVC) #

Hierarchy - Top-down

In the most common case Hierarchy is applied to business logic to build a layered system which grows from a single generic high-level root into a swarm of specialized low-level pieces. The most obvious applications are protocol parsers, decision trees, IIoT (e.g. a fire alarm system of a building), and modern automotive networks. A marketplace that allows for customized search and marketing algorithms within each category of its goods may also be powered by a hierarchy of category-specific services.

Presentation-Abstraction-Control (PAC) [POSA1, POSA4] applies Top-Down Hierarchy to a user-facing application, providing each of the resulting layered nodes with its own widget (presentation) on the UI screen (which is the presentation of the root node). Controls are responsible for inter-node communication and integration logic, while domain logic and data reside in abstractions.

Hierarchical Model-View-Controller (HMVC) is similar, but its views access models directly, like in MVC, and every model synchronizes with the global data. This pattern was used in rich clients.

PAC

Bottom-Up Hierarchy, Bus of Buses, Network of Networks #

Hierarchy - Bottom-up

Other cases require building a common base for intercommunication between several networks which vary in their protocols (and maybe even their hardware). The root of such a Hierarchy is a Middleware generic and powerful enough to cover the needs of all the specialized networks which it interconnects.

Example: Automotive networks, integration of corporate networks, the Internet.

In-Depth Hierarchy, Cell-Based (Microservice) Architecture (WSO2 version), Segmented Microservice Architecture, Services of Services, Clusters of Services #

Cell-Based Architecture

When several services in a system grow large, in some cases it is possible to divide each of them into subservices. Each group of the resulting subservices (known as a Cell, Domain or Cluster [DEDS]) usually implements a bounded context [DDD]. It is hidden behind its own Cell Gateway and may even use its own Middleware. Subservices of a Cell may share a database and may be deployed as a single unit. This keeps the system’s integration complexity (the length of its APIs and the number of deployable units) reasonable while still scaling development among many teams, each owning a service. If each instance of a Cell owns a shard of its database, the system becomes more stable as there is no single point of failure (except for the Load Balancer called Cell Router). Another benefit is that Cells can be deployed to regional data centers to improve locality for users of the system. However, that will likely cause data synchronization traffic between the data centers.

The Cell-Based Architecture (Segmented Microservice Architecture) may be seen as a combination of an Orchestrator of Orchestrators and a Bus of Buses where the subservices are leaf nodes of both hierarchies while the API Gateways of the Cells are their internal nodes.

Uber compacted 2200 Microservices into 70 Cells arranged in SOA-style Layers called Domain-Oriented Microservice Architecture.

Evolutions #

Hierarchy - 1

Summary #

Hierarchy fits a project of any size as it evenly distributes complexity among the system’s many components. However, it is not without drawbacks in performance, debuggability, and operational complexity. Moreover, very few domains allow for seamless application of this architecture.

CC BY Denys Poltorak. Editor: Lars Noodén. Download from Leanpub or GitHub. Powered by odt2wiki and Hugo Book.