Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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".

Licensed under: CC-BY-SA with attribution
scroll top