I am wondering about how to make lower-level code reusable when the Dependency Inversion Principle (DIP) is used.
In the book Clean Architecture by Robert C. Martin, the DIP is described such that the higher-level components define the interfaces they need, and then the lower-level components implement these interfaces, as in
+-----------------------------------+ | | | Application ------> Service <I> | | ^ | +-------------------------|---------+ | +----|--------+ | ServiceImpl | +-------------+
- the application code is protected from changes in the lower-level components
- the application does not depend on the source of the
- "the source code dependencies are inverted against the flow of control".
Maybe the image in my head is wrong, but I always imagine that the
Service in the picture above may be something like a logging service, so maybe the interface has methods like
info() etc., and then the implementation could log to a file, to a database, or whatever.
I find two things a bit weird about this approach:
- The general-purpose logging code in
ServiceImplthat doesn't know anything about the data it is logging needs to import an interface from the big valuable application module, is that right? Somewhere in my lower-level code I have a line that looks like
from high_level_app import ServiceInterface, that doesn't feel right. It feels like I am creating an artificial dependency to the higher-level module here.
- From a more semantic viewpoint, obviously the functionality in my lower-level logging implementation could be reused across the whole organization, but since the higher-level code defines the interface and (as per the point above) I probably have a source code dependency on the higher-level code, does that limit the reuse?
So I would like to ask:
- Does the Dependency Inversion Principle in general inhibit the reuse of lower-level libraries? Or is my example of the logging service flawed, because that's not what the DIP is aiming at?
- When using the DIP, how would I go and create reusable lower-level components, rather than only making them usable for the particular higher-level component I am dealing with right now?