1

I am confused about Dependency Inversion in general. Lets's say we have 2 layers: Layer0 the low level layer, and Layer1 the high level layer. Layer1 should define the abstract interface, and Layer0 should implement it. However if Layer1 requires other types then the built in types, then in order to use Dependency Inversion I must extract the complex types into a separate library and use those types in both layers.

My question is, for reassurance, that there is no way to use dependency inversion without sharing complex classes or applying an adapter between the 2 layers?

  • 1
    I'm sure there's a way to do that, but it's probably not worth the hassle. Some classes, like entities or DTOs are perfectly acceptable to be used across layers. Other classes, such as concrete service implementations are not. – MetaFight Dec 28 '16 at 14:40
1

My question is, for reassurance, that there is no way to use dependency inversion without sharing complex classes or applying an adapter between the 2 layers?

No, there most certainly are ways to use the Dependency Inversion Principle without sharing complex classes. Share simple messages.

If you put an adapter between two layers what you have is a third layer. This is not a bad thing so long as the layer is useful and focused.

@Metafight is exactly right. DTOs (or value objects) can be passed around. But...

Return types? Who said you had to return?

The Clean Architecture

One of the neatest tricks I've seen that follows the Dependency Inversion Principle is the plugin diagram in the lower right corner here:

edit me

This lets the flow of control start in the outer layer, dive into the inner layer, and come back out in the outer layer again. All without having to return a thing. A very simple message can be sent this way. Here's an example.


It doesn't know, it doesn't want to know

I am confused about Dependency Inversion in general. Lets's say we have 2 layers: Layer0 the low level layer, and Layer1 the high level layer. Layer1 should define the abstract interface, and Layer0 should implement it. However if Layer1 requires other types then the built in types, then in order to use Dependency Inversion I must extract the complex types into a separate library and use those types in both layers.

The key thing here is the high level layer doesn't know about the low level layer at all. The high level layer defines interfaces that it can listen through or talk to without knowing what is on the other side. The low level layer is a slave to these interfaces. It doesn't own them but it must match them. That means the low level layer can be completely replaced without the high level layer being changed at all.

So what does this mean for DTO's? The highest level layer that knows about the DTO defines it's interface. Anything that high and lower may know about the interface and use the DTO. However, whatever constructed the DTO is what defines it's implementation.

The DTOs don't need to be complicated. They can become complicated if you don't keep your use cases focused and simple.

2

Dependency inversion is when compile time dependency point to the opposite direction then run-time dependency.
Even without classes when you change abstraction(some interface) then both layers need to be recompiled.
So unless your complex types do not contain any functionality they can be and they are part of the abstraction.

----------------------
|                    |
|    Higher layer    |
|                    |    
----------------------  
         |
         | Higher layer depends on abstraction
         |
         V
--------------------------------
|                              |
|    Abstraction and classes   |
|                              |    
--------------------------------  
         ^
         |
         | Lower layer depends on abstraction
         |
----------------------
|                    |
|    Lower layer     |
|                    |    
----------------------  

So Lower layer's compile time dependency still pointing "up"(different then run-time dependency) which mean you are still using "Dependency inversion".

  • Thanks for the answer Fabio. Sorry, but I don't understand the diagram. I think a main point of dependency inversion is that abstraction (interfaces) is a part of the higher layer. The lower layer implements the interfaces of the higher level. Regarding complex types, if the object is a POCO with methods and logic then it should have an interface, otherwise if it is a DTO or class without logic it should be extracted and the lower layer should use them in order to pass data to the higher level. My question was regarding DTOs that is it okay to extract them into a separate project. – Icebraker Dec 28 '16 at 22:06
  • Or do I misunderstand something? – Icebraker Dec 28 '16 at 22:06
  • 1
    Compile time/runtime semantics don't have anything to do with the definition of dependency inversion. Read the Wikipedia article for clarification. – Robert Harvey Dec 28 '16 at 23:19
  • I think you're confused POCO while can have method and logic, it shouldn't have do that when using SI, because it will create extra layer of confusion. Also I dont think you suppose to stick interface to your complex type why would you do that, do you want to introduce abstraction to your return value?? – kirie Dec 29 '16 at 0:25
  • @Icebraker, the abstraction module can be part of Higher level, in my diagramm I have separated them for more clarity and my point was DTO classes are part of the abstraction. – Fabio Dec 29 '16 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.