I think in today’s software development it’s seems we are having a lot of people using inversion of control containers and not really understanding why, people use IoC in frameworks as (angular, asp.net core).
They’re being told this is the right thing to do, but sometimes they’re not getting that background of what is the actual problem we’re trying to solve.
Since inversion of control is a larger topic than most people think. To really understand what inversion of control is we should have a short vocabulary lesson on some of the topics related to inversion of control.
Dependency inversion principle (DIP): is a principle used in architecture software and is basically the idea that you would invert dependencies from the traditional way that they are constructed.
Inversion of control (IoC): is a pattern that is going to be used in order to implement that principle of dependency inversion. Inversion of control is also used for some other principles, but it is basically a pattern used commonly as a way to invert interfaces and dependencies.
Dependency injection(DI): is an implementation of specifically IoC that is used to invert the dependencies. So we are able to apply dependency inversion through dependency injection.
Inversion of control container: is basically the framework used in order to implement this set of patterns and implementations and principles.
What I have found commonly between talks, discussions and even in conferences is that
The terms dependency inversion, IoC and dependency injection kind of tend to be grouped up as one thing and really the words are being used interchangeably which is something creating confusion among developers.
First I’m going to look at the academic definition and then I will make it super simple by showing what it means with a real use example.
The basic idea here, is that instead of having it so that your low level modules are defining the way that the high level modules interact with them. The Idea is to invert that dependency so no longer are the high level modules going to be dependent on the low level ones, but the high level modules will define some kind of an interface that the low level modules must obey and then the high level module depends on that interface. And the low level modules depend on that interface or abstraction basically in order to function.
The definition of dependency inversion principle has two parts. The first part: high-level modules should not depend on low-level modules. Both should depend on abstractions. Let’s see what this means in action. A typical real life example is in the Unit of work controller usage.
Here the controller is a high-level module and UnitOfWork is a low-level module. What happens here is that the controller is dependent or tightly coupled to UnitOfWork, which violates the dependency inversion principle. With this coupling there is a problem that if we change UnitOfWork, the controller may have to be changed, or at least it needs to be recompiled, and the assembly in which it’s defined needs to be redeployed. This will prevent us from independently deploy our assemblies or properly componentize our application.
Dependency inversion principle helps us remove this coupling. So instead of controller being dependent on UnitOfWork, it will be dependent on an abstraction, or an interface that will be implemented by UnitOfWork. With this, the controller no longer knows about the concrete UnitOfWork implementation. If it changes implementation, the controller will not be affected as long as the contract stays the same.
Design could be using NHibernate Session, DbContext and Entity Framework, later, we can replace it with another ORM or a newer version of Entity Framework with a different API. and the controller will not be affected. It doesn’t need recompilation, because it’s only dependent on the contract.
The second part of definition of dependency inversion principle. Abstractions should not depend on details. Details should depend on abstractions. Basically this means that in IUnitOfWork, should not be a concrete implementation, which will make again become the design tightly coupled to DbContext and Entity Framework. which means if our controller is dependent on IUnitOfWork, even though this is an interface, the controller will still be tightly coupled to Entity Framework. So we need to replace UserRepository with an abstraction like IUserRepository that knows absolutely nothing about Entity Framework. And this is the meaning of the second part of dependency inversion principle. Abstractions should not depend on details.
So inversion of control really is a high level pattern, it can be applied in different ways to invert different kinds of control. It can be applied over the control the actual flow of the application and over the creation of objects and dependencies.
IoC and DIP
What is the difference between inversion of control and dependency inversion principle? Well if you look at what we did with the dependency inversion principle by taking a high level module and we said that it should not depend on the low level module. Both of them should depend on some abstraction.
Well inversion of control is a way that we provide that abstraction, it’s a way that we actually change the control.
Dependency injection is more about providing a solution, an actual implementation, of an inversion of control.
To summarize we should understand that the dependency inversion principle is a principle used as a guide on how we develop our software. We don’t want to have the high level modules depending on the lower level modules.
We should always be asking the question about what type of controller am I trying to invert and why? Because every time we’re applying inversion of control, we are trying to invert some sort of control.