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 |
+-------------+
That way,
- the application code is protected from changes in the lower-level components
- the application does not depend on the source of the
ServiceImpl
- "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 debug()
, 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
ServiceImpl
that 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 likefrom 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?