-
Notifications
You must be signed in to change notification settings - Fork 53
Dependency inversion in architectural patterns
I am no fan of SOLID – to the extent of being unable to remember what those five letters mean – thus I was really surprised to notice that one of its principles, namely dependency inversion, is quite common with architectural patterns, which means that it is way more generic than OOP it is promoted for.
Let’s see how dependency inversion is used on system level.
Both Plugins and the derived Hexagonal Architecture rely on dependency inversion for the same reason – to protect the core, which contains the bulk of the code, from variability in the external components it uses. The core operates interfaces (SPIs) which it defines so that it may not care whatever is behind an interface.
It is the nature of the polymorphic components that distinguishes the patterns:
- Plugins allow for small pieces of code, typically contributed by outside developers, to provide customizable parts of the system’s algorithms and decision making. Oftentimes the core team has no idea of how many diverse plugins will be written for their product.
- Hexagonal Architecture is about breaking dependency of the core on external libraries or services by employing adapters. Each adapter depends both on the core’s SPI and on the API of the component which it adapts. As interfaces and contracts vary among vendors and even versions of software, which we want to be interchangeable, we need adapters to wrap the external components to make them look identical to our core. Besides, stub or mock adapters help develop and test the core in isolation.
A few more metapatterns tend to use this approach to earn its benefits, even though dependency inversion is not among their integral features:
- Microkernel, yet another metapattern derived from Plugins, distributes resources of providers among consumers. Polymorphism is crucial for some of its variants, including Operating System, but may rarely benefit others, such as Software Framework.
- Top-Down Hierarchy distributes responsibility over a tree of components. If the nodes of the tree are polymorphic, they are easier to operate, and there is dependency inversion. However, in practice, a parent node may often be strongly coupled to the types of its children and access them directly.
- In another kind of Hierarchy, namely Cell-Based Architecture (aka Services of Services), each Cell may employ a Cell Gateway and outbound Adapters to isolate its business logic from the environment – just like Hexagonal Architecture does for its monolithic core.
Finally, two basic architectures, Layers and Services, may resort to something similar to dependency inversion to decouple their constituents:
- We often see a higher layer to depend on and a lower layer to implement a standardized interface, like POSIX or SQL, to achieve interoperability with other implementations (which is yet another wording for polymorphism).
- A service may follow the concept of Hexagonal Architecture by using an Anti-Corruption Layer [DDD] or CQRS Views [MP] as Adapters that protect it from changes in other system components.
Many architectural patterns employ dependency inversion by adding:
- an interface to enable polymorphism of their lower-level components or
- Adapters to protect a component from changes in its dependencies.
The two approaches apply in different circumstances:
- If you can enforce your rules of the game on the suppliers of the external components, you merely define an SPI, and expect the suppliers to implement and obey it.
- If the suppliers are independent and it is your side that adapts to their rules, you should add Adapters to translate between your lovely SPI and their whimsical APIs.
| << Pipelines in architectural patterns | ^ Comparison of architectural patterns ^ | Indirection in commands and queries >> |
|---|
CC BY Denys Poltorak. Editor: Lars Noodén. Download the book from Leanpub or GitHub. Generated with odt2wiki.
Analytics
Appendices
- Acknowledgements
- Books referenced
- Copyright
- Disclaimer
-
Evolutions of architectures
- Evolutions of a Monolith that lead to Shards
- Evolutions of a Monolith that result in Layers
- Evolutions of a Monolith that make Services
- Evolutions of a Monolith that rely on Plugins
- Evolutions of Shards that share data
- Evolutions of Shards that share logic
- Evolutions of Layers that make more layers
- Evolutions of Layers that help large projects
- Evolutions of Layers to improve performance
- Evolutions of Layers to gain flexibility
- Evolutions of Services that add or remove services
- Evolutions of Services that add layers
- Evolutions of a Pipeline
- Evolutions of a Middleware
- Evolutions of a Shared Repository
- Evolutions of a Proxy
- Evolutions of an Orchestrator
- Evolutions of a Combined Component
- Format of a metapattern
- Glossary
- History of changes
- Index of patterns