Dependency Injection, Dependency Inversion and Inversion of Control Explained (TypeScript example)

If you’re familiar with software development techniques, you may have stumbled upon the terms “Dependency Injection” (DI), “Dependency Inversion Principle” (DIP) or “Inversion of Control” (IoC). They have been made famous thanks to programming gurus like R. Martin and M. Fowler and implementations in popular backend frameworks. Let’s explain them in an easy, straightforward and JavaScript-friendly way.

What is Dependency Injection?

First, let’s describe what dependency injection is. It’s a programming technique where classes/functions don’t create their dependencies, instead they receive them from somewhere else. Take a look at this simple example:


// without DI
class Lamp {
  bulb: IE27Bulb;
  constructor() {
    this.bulb = new E27PhilipsLed11WBulb();
  }
}

// with DI
class Lamp {
  bulb: IE27Bulb;
  constructor(bulb: IE27Bulb) {
    this.bulb = bulb;
  }
}

Thanks to using dependency injection in the second example, we can just tell the class ‘Lamp’ which interface to use. That’s why I’ve used a contrived example to show this. If a class were a Lamp without dependency injection, it would come preassembled with one particular bulb, without the possibility to change it. With dependency injection, we just assign a type of ‘bulb’ using an interface, that fits a lamp, and it’s up to the user which one to use (or inject).

The code you see above is written in TypeScript, which supports interfaces; and is strongly typed, which is very common with other languages using DI. However, that technique can also be done in JavaScript. There are no interfaces, but you can still inject an object and type checks can be done in runtime if needed.

Dependency injection is often done in an automated manner with Dependency Injection Containers (also called Inversion of Control containers). Containers also control the lifecycle of the dependency (normally using the constructor on classes) which allows us to pass around instances of classes without providing (and passing in) their dependencies explicitly. You can say that container controls the apps flow.

What Is Dependency Inversion Principle and Inversion of Control?

Talking about Dependency Injection, it’s worth mentioning the other two terms and how they differ. They are often confused with each other, even used interchangeably. It’s wrong since all three are different but connected.

The most general one is the Dependency Inversion Principle (DIP) — the D from SOLID. It states the following:

  • High-level modules should not depend on low-level modules. They should depend on abstractions like interfaces
  • Abstractions should not depend on details, like concrete implementations.

So, instead of “I want class ABC” you say: “I want something that implements interface ABC” This means we ‘depend’ on interfaces. The same goes for abstractions. Therefore these also shouldn’t have on any specific implementations.

Finally, we also have Inversion of Control (IoC). It’s a programming principle where we are, as the name says, inverting the flow of control. Traditionally, apps have a main function, where logic was used and tailored for desired user flow. With IoC, it’s different. The application’s core is now in a framework where we provide custom logic for behaviors by implementing abstractions of those dependencies (from the DIP we already talked about). There are many implementation techniques for IoC, just to name a few: service locator pattern, template method design pattern, strategy design pattern, and the dependency injection itself.

In general, IoC is what makes the difference between frameworks and libraries. However, you don’t need to use a framework to have an IoC in your app. You still can benefit from this approach by implementing IoC techniques on your own (or with the use of proper libraries).

To Conclude

Dependency Injection is a programming technique where dependencies are not created by function or classes themselves but are provided via injections. It’s one of the implementations of the Inversion of Control principle where the framework controls the app’s flow and up to the developer is to provide the custom logic. Both rely on the Dependency Inversion Principle, which tells developers to always depend on abstractions for dependencies, not implementations. Finally, we have Dependency Injection Containers (also called IoC Containers) that allow you to invert control by implementing dependency injection and encourages developers to comply to DIP. Suppose you’d like to see these in action. In that case, you may want to see my next article, where I will explain how IoC can be implemented in JavaScript with a closer look at the Inversify library, which we are using in our 12-Week UI Architecture Academy. Be sure to download our book “Think! Like A UI Architect” where you can grasp using these ideas in practice!


Author: Tom Swistak

Tom is a UI Architecture Consultant he contributes to Logic Rooms growing ecosystem of content and provides consultancy services in Mobx, Redux, Dependency Injection and Reactive Architecture

LinkedIn

Want to learn 5 critical lessons for framework-agnostic JavaScript UI app design and structure?

Download our 28-page book exploring big picture ideas to help you create better architecture with any UI framework (inc. React, Angular or Vue).

Download
Close

50% Complete

Book Trial

Please fill out your details if you would like to trial our system.