Dependency Injection Design Pattern Part 1
Are you using dependency injections but you really don’t know how it works and what is the benefit of using DI?
Are you an old model of building software system with three tiers architecture? Where all tiers tightly coupled?
Are you data model centric architecture? Where the database and its schema is the center of the system?
Three tiers architecture and data model centric architecture approaches are still alive and sometimes could be wrong choice for your application.
If you are still using architecture such as this or if you don’t know the benefit of using DI then you have to read this blog about Dependency Injection as a design pattern.
So what is dependency injection? And why we need to use it?
You can download source code of the project from github
The definition of the DI changed by the time, by looking into Wiki you will see that in 2006 this was the definition.
Dependency injection (DI) is a programming design pattern and architectural model, sometimes also referred to as Inversion of Control or IoC, although technically speaking, dependency injection specifically refers to an implementation of a particular form of IoC. The pattern seeks to establish a level of abstraction via a public interface, and to remove dependency on components by (for example) supplying a plug-in architecture. The architecture unites the components rather than the components linking themselves or being linked together. Dependency injection is a pattern in which responsibility for object creation and object linking is removed from the objects themselves and transferred to a factory. Dependency injection therefore is inverting the control for object creation and linking, and can be seen to be a form of IoC.
This definition has changed couples of times, and in 2018 the definition on wiki became as following:
In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client's state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
This fundamental requirement means that using values (services) produced within the class from new or static methods is prohibited. The client should accept values passed in from outside. This allows the client to make acquiring dependencies someone else's problem.
The intent behind dependency injection is to decouple objects to the extent that no client code has to be changed simply because an object it depends on needs to be changed to a different one.
But wait what does that mean!? , I am assuming that you still confused, to make things more easily in a nutshell and simple words, Dependency injection is a Design pattern that enables the developer to loosely coupled code.
But!! Why loosely coupled? , In this blog, you will see it is not that hard to add loosely coupled code to your applications by examples and step by step.
There are several benefits for using loosely coupled code
Extensibility (code that can response quickly for new requirement)
Testability ( you will be able to create unit testing )
Parallel development (to speed up development no need to wait for others modules)
Maintainability (the maintenance process will be super easy in future)
From now on, you will start see these two terms loosely coupled and tightly coupling a lot
We will start by looking to an application that doesn’t use dependency application and see why the applications are suffering from tightly coupling, we will see some scenarios that should be easy to implement but are really hard without dependency injection.
DI has 5 types (patterns) as following:
We will explain all of them but we will focus on constructor and property injection because those are most DI common used.
When we implement DI for our application most likely we will not do this manually, instead we will use native DI like Microsoft extension dependency injection or one of many DI containers and frameworks likes Unity, Ninject, Autofac and many others, But first we will do it manually so that we have good understanding how the DI working. From my experience, I note that if you are a developer using frameworks or new tools without even asking yourself how all the magic works have been done believe me you are missing very great information that can help you to be more creative with solving problems.
Let’s start with our simple web MVC example without using DI J , this example could be applied on any type of projects, like desktop application , web application or even mobile applications.
Let’s assume that we have a data analysis page that shows basic report showing students' attendance during the first semester, and the page will read this data from SQL database
I will create this project using features (modules) architecture and MVC areas, this is for a reason I will tell you why later on, our application interface will look like this
And our project architecture will look like this
This is a lot of work for this type of application, even if we have good application design it seems that we have separate layers, but in fact those layers are tightly coupled.
I will not explain the tiers architecture or Domain Driven Design in this blog because this is another story and it is needs a separate blog, so for now let’s draw simple diagram for our application
Presentations can’t provide it is feature without relying on the repository class , and the repository is a low class which interoperate with data store which for now is SQL , while the presentation is the high level domain object so in this particular case a high-level object depends on low level object, this how beginners build software traditionally, this is a high level view on a classic application with three tiers.
On the top we have a user page interface calls API of objects which implements business logic, objects which implements business logic call API of low-level that interoperateswith data store layer in our case it is SQL database please note that this data store layer could be changed in any time to be CSV files or external third party API or different type of databases like noSql or any things else represent data store.
The controller references a concrete type of repository, so the controller takes responsibility for creating and managing reporting repository, Based on this we see that there is tightly coupled between controller and repository (StudentReports)
But wait things getting worse.
The repository references a concrete type of data store layer (FakeDB) , so the repository takes responsibility for creating and managing the service or data store layer.
This does not seem that bad especially for small application like this, but for much large application this could be a nightmare, what if for client change the data store to be Mysql or noSql or even CSV file, how we can add this for our application?
Most of the developers will use switch case to solve this issue, they will add key value to the configuration file and based on that key they will create the new object
This is not the optimal solution for this case, because StudentReport class should not be responsible of checking and creating data store object also what if you want to cache CSV file, code will get more complicated, also how you will be able to create unit test for this?
The responsibility of the StudentReport is to get the report data, do some mapping, it should not select which data store to use, we should not need to change our Business logic if we want to add a different data store,
Our application got more complicated because we avoided SOLID principles
So what is the solution? Loose coupling will help us resolve this issue.
You may be wondering why this solution has a lot of layers and the application like it has good design architecture, believe me I meet a lot of people their job title architect that they think Organize classes into folders and layers is a good architecture for the application and I was thinking this true for long time , but this totally wrong, if you are not using the right design pattern in right cases you are not adding any real useful values for your application
Accessing Different Data Stores
we can solve this problem by introducing an extra level of indirection, First we need to create common interface for repository (IFakeDBREpository) that allow us to plug any type of repository that implement this interface
This seems to allow us to inject any dependency we want which of course implements that interface, this is a simple powerful technique to invert dependencies achieving low coupling between components.
Now that we have the interface we will start to break our tight coupling By Inject the repository in the constructor
The most beneficial aspect of constructor injection is that it protects the invariants of the object. The object once created will always be in a valid state.
Interface repository: This means that this field is not tight to specific concrete class we can assign any class implement this interface
We need to make the injection someone else problem and our class is not responsible of creating the concrete type of the repository.
Now our class only cares about the interface but not of any particular concrete type, so the StudentReports class accepts any class implementing IFakeDBRepositroy.
Now we can add repositories as much as we need, and create the repository type that you want.
In the controller we are choosing a Sql repository, and we can create the repository type that we want.
Now go and publish the project and see the result
Ok good let’s change the repository to CSV and see the result again
So far so good, it's working fine we got different results.
But wait a minute we still have tight coupling in our controller, our controller should not be responsible for creating data store object, it should be responsible for preparing view Model object and pass it to view, so far we have done the removing of tight coupling manually, but will see that the DI (dependency injection) container gives us some really important functionality and simplicity our code.
DI Container can do much more for us a really big feature is life time management
Two ways of dealing with Dependency injection
1) Custom DI builds your own.
2) Use an IoC-Container(also known as DI-Containers) , which is a framework helps to apply DI and has many features
Before we start using one of the DI Containers framework, for more understanding let’s build simple DI and see how it works.
let's convert creating new object to Native DI container EX: new StudentsReports(new CsvDb()) , simply we need two methods Register class and Resolve class in other words we need to register the StudentsReports , CsvDb class and resolve them when needed.
Ioc-Container recursively creates all the required dependencies, imagine that we make a call to an Ioc-Container to resolve the StudentsReports, in other words we want from Ioc-container to get an instance of the StudentsReports class the IoC will look further to find how to resolve it , in our case StudentsReports takes interface in the constructor so the container will try to create the CsvDb in order to create the StudentsReports class the container has to pass to its constructor the CsvDb repository since the StudentsReports class depends on this interface to resolve the CsvDb repository , so this is how our containers are recursively resolved dependencies.
Let’s build IContainer interface that has Register and Resolver methods
Now let’s create SimpleID that implements the IContainer
This map will contain one to one relationships between dependences
This is generic method which takes two types and adds them to our map
The T parameter is the type which the client wants to be resolved and this method calls another private method that implements all the logic.
Here we get the corresponding type from the map; if we didn’t find it we throw an exception.
By using the reflection we can get request constructors , for simplicity let's get the first one and request it's parameters, if there is no parameters we just create an instance of the dependency and return it, otherwise we need to resolve them recursively, this is very simple Ioc-container you will find a lot of examples on the internet.
Simply we need to register StudentsReports and CsvDb that implementing IFakeDBErpository , let's debug the program to ensure that everything works fine.
Great it works fine and we resolved the dependencies properly.
In DI part 2 blog i will take you to next level, and i will be talking about
how we can inject our class directlly to controller constructor
we will add new implementaion to our simple DI in order to support multi repository injection
i will show you how we can integerate our simple IoC with asp.net resolver