Onion Architecture – Applying Dependency Inversion Principle in Service Layer

dependency-inversiondesignonion-architecture

I'm trying to implement a project using the Onion Architecture (aka Hexagonal, aka Ports and Adapters, aka Clean Architecture).

I understand that the user interface uses Service interfaces instead of the actual implementations.
I also understand that if a Service needs something from an external source (e.g. a database or a Web API) or from another Bounded Context, you want you inject implementations of Infrastructure interfaces.

These two ideas follow the Dependency Inversion Principle (DIP). That's a good thing.

But let's zoom in on the Service layer itself:

Ideally you want most of the logic on the Entities in your model, but there will still be logic that has to be implemented in the Services. In my situation, it is more than I want in one single Service class, so I'm delegating responsibilities to other classes, I'll call them sub-services for now.

And now the question: Would it be wise to apply DIP on all of these sub-services too? They are just internals of the service implementation and would not serve a purpose outside of the domain.

In other words: The official definition of DIP talks about high level and low level modules. Should all (sub-)services be considered one module or are they all separate modules?

Best Answer

You apply DIP when crossing a significant boundary. What's on either side doesn't matter. What matters is having a good reason to keep what's on one side from having a source code dependency on the other side.

The magical thing DIP does is let you enforce that source code dependency rule while allowing flow of control to go in and out of that boundary without resorting to using return.

enter image description here

When organized like this the assumption is that the inner stuff is more stable than the outer stuff. DIP lets you strip off the outer stuff and add new outer stuff without breaking the inner stuff.

When you hear the words "higher" and "lower" understand those terms were developed when the style was to keep all inheritance arrows pointing up. These round diagrams make that metaphor a little meaningless. Here's a diagram that tries to give them meaning again:

enter image description here

This doesn't change your design at all. Just gives those words meaning again.

enter image description here

And now the question: Would it be wise to apply DIP on all of these sub-services too? They are just internals of the service implementation and would not serve a purpose outside of the domain.

I'll stick with the significant boundary rule here. Like anything, DIP can be over applied. I wouldn't be surprised to find every service that faced a significant boundary applying DIP. I would be surprised to find nests of DIP applied to every module in the service layer even if they don't face a significant boundary.

Now that said, circular dependencies in source code are a special kind of hell. DIP can also be used to help avoid that. In all cases, be sure you understand why you're applying it. Don't just apply it cause someone told you to. Know why.

In other words: The official definition of DIP talks about high level and low level modules. Should all (sub-)services be considered one module or are they all separate modules?

The rings in these diagrams depict layers not modules. Now sure, a layer could have only one module in it but it can easily have more. Here a layer is a bag of modules that are asked to follow the layers rules when they touch modules in a different layer.

I can't speak for every author but I have heard Robert Martin explain module to mean object if you happen to be in an object oriented context. Modularization is an older philosophy of designing so parts are easy to remove and swap. You don't have to be object oriented to use modules.

In an OOP context I'd expect one-to-many objects to make up a service and one-to-many services make up a layer (ring).

While Onion, Hex, Ports, and Clean all amount to the same idea they each have their own vocabulary (designed to drive you to different books and blog posts). Because of that be careful when asking these questions because some words mean different things with different authors.