Dependency Injection ?
Dependency Injection (also known as DI), is one of the best Software Engineering principles we can apply in software projects. According to Wikipedia:
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.
DI in statically typed languages
Java was the programming language I have used for longer period in my young career. In 2009, one of my former university colleagues challenged me to switch from PHP to Java: “the promised land”… and somehow I ended up using Java for the next 6 years intensively in production.
Thanks GOD, the migration to Node.js 🚀came afterwards in 2015… and yet: If something I miss from Java, that is the “Spring IOC module” 😥…
There are certainly DI implementations for every programming language or stacks. For statically typed languages like Java or TypeScript, implementations are really impressive, due to the power of interfaces and modern OOP resources like annotations (see TypeScript example).
Let’s analyse a basic example in TypeScript using the inversify framework:
What have just happened:
- We have declared two injectable services (Katana and Shuriken) using their correspondent interfaces (Weapon and ThrowableWeapon).
- We have declared another injectable service that will receive two dependencies through it constructor, in concrete a Weapon and a ThrowableWeapon.
Then we create a DI container:
And finally we resolve assembled dependencies:
And VOILA, magic just happened!
The DI Container have instantiated the required objects and resolved their dependencies behind the scene. So instead of managing objects creation and their dependencies in our code, a “magic” container is doing this for us 🙌!
This process is a sub-set of a bigger design principle called “Inversion of Control”.
As types can’t be inferred using Class or Interface names, we can use the function parameters name to refer to the dependencies. Let’s get again into this process:
While registering a service, we indicate the dependencies that are required to be resolved first, so we can instantiate the “app” service. Here we use the callback function parameters name to indicate the DI container what dependencies to inject🏌🙌.
If you are a Frontend Developer this days, you are most probably using DI through TypeScript. Just to mention one example:
- Angular Framework DI — https://angular.io/guide/dependency-injection
While DI is pretty used in one way or another for frontend applications (because here we produce a lot of objects 😉), it is not the same case for the backend. I tried to answer my self this question, and I found what I guess is the root cause:
“Humm, adding a DI framework to rule my application setup might just not be correct!”
As a positive example of promoting DI on the backend we can mention the Nestjs framework, although it is not so popular and uses TypeScript as a required language. Anyways 👏
So I want to finish this article by reminding us some of the benefits we get by embracing DI:
So if you want to know more, this is the next story to read: http://tutorials.jenkov.com/dependency-injection/dependency-injection-benefits.html
Thanks for reading, your feedback is gold!