Decoupling, modularisation, and clean architecture
As usual, I start with Wikipedia citation:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.
There is no such division on A and B items in original Robert Martin’s book. Wikipedia has it, but unfortunately, it doesn’t elaborate on why is it so. Besides, it’s not clear what is assumed by high-level and low-level modules. I think it deserves some notes.
Objects exist to carry out some work. They can do it themselves, or with a help of some other objects. Chances are that those “helper” objects represent some useful abstraction, that is, there are more than one classes capable of implementing the certain contract. So I want to reflect this fact in code:
public function __construct(B $b)
$this->b = $b;
Object $b is an implementation detail for object of class A. Thus, interface B is, or, however, should be defined in terms of A, and in the same package as A. So that’s why any object implementing interface B is considered a lower-level with respect to A.
So according to its definition, Dependency Inversion Principle is about two things: decoupling and correct modularisation.
Let’s elaborate a bit further
At good old times, when nobody used interfaces, objects were instantiated right inside the classes that used them. So they depended directly on those implementation details (Figure 1 in the image below). Today we value decoupling and polymorphism, so we use interfaces to make our dependencies explicit. This is not Dependency Inversion Principle still, this is just a Dependency Injection. In order for it to become an inversion, we must claim that consumer class owns his dependency abstractions. Now, modules representing an implementation detail, depend on more abstract modules (Figure 2 on an image below). Thus, Dependency Inversion Principle reversed the dependency direction between a consumer code and dependency implementation code.
Evolution to Clean Architecture
As a special case, and further evolution, DIP states that business-logic should not directly depend on technical details. Instead, it should define an interface that it owns, so that all the implementation code (for example, database queries, http communication, email sending and pretty much any infrastructure logic) depends on this central layer — policy layer. For me it sounds like a harbinger of a Clean Architecture:
Well, this principle basically boils down to good old loose coupling thing, which has been around for at least fifty years. And David Parnas has already said everything that was necessary on how to define modules. I guess the only advantage of Dependency Inversion Principle is that it’s way less ambiguous than the others in SOLID family.