Inversion of control and Dependency injection in Typescript
The following article is about inversion of control(IoC) and dependency injection(DI) in Typescript. The main aims of that techniques is to provide loose coupling between modules and classes.
IoC and DI are part of the SOLID topics, SOLID principles using Typescript can gives you more notions about SOLID combined with the Typescript world.
I have already written about Dependency injection: Dependency Injection overview. The article contains some general informations about Dependency injection base concepts.
I also suggest another cool explanation of Dependency injection: How to explain dependency injection to a 5-year-old? .
Reasons for use Inversion of control and Dependency Injection
Here are some reasons for use Inversion of control and Dependency injection:
- Decoupling: dependency injection makes your modules less coupled resulting in a more maintainable codebase;
- Easier unit testing: instead of using hardcoded dependencies you can pass them into the module you would like to use;
- Faster development: with dependency injection, after the interfaces are defined it is easy to work without any merge conflicts;
Inversify JS
InversifyJS is a lightweight inversion of control (IoC) container for TypeScript and JavaScript apps. A IoC container uses a class constructor to identify and inject its dependencies. InversifyJS has a friendly API and encourage the usage of the best OOP and IoC practices.
Philosophy
InversifyJS has been developed with 4 main goals:
- Allow JavaScript developers to write code that adheres to the SOLID principles.
- Facilitate and encourage the adherence to the best OOP and IoC practices.
- Add as little runtime overhead as possible.
- Provide a state of the art development experience.
Inversify JS in action
The following example will use Typescript combined with InversifyJS to implement some basic logics. All classes will implement interfaces. An unit tests suite, written over Jest, will cover all the code base. It will use Dependency injection to mock behaviours and functions behind the classes.
Firstly, let’s give you an overview of the project structure:
The Track
class defines the Domain model of our system. The IMusicRepository
defines an independent interface which aim is to expose repository common functions, such as CRUD operations. The VinylCatalog
class implements the IMusicRepository
and query a fake db which it will return/update an collection of vinyl. Each IMusicRepository
consumer, for example MusicCatalogService
, does not know anything about VinylCatalog
: this is one of the main aims of the SOLID programming principles and Dependency injection.
The following code shows the typescript implementation of the previous UML schema:
Finally, we are ready to setup InversifyJs, which is our Dependency injection container. The main aims of InversifyJS is to register and map interfaces with concrete classes. There is always a key component when we talk about Inversion of control container: Installer. The installer provides mapping between interfaces and their concrete classes. We can find installer concept in every DI container framework and in every language, from Javascript to C#.
Let’s create the Installer module:
@ line 9 we can find the mapping between the IMusicRepository
and VinylCatalog
. InversifyJs also require to decorate our concrete class with the @injectable
attribute, like this:
At least, we can implement our composition root (It will be implemented in a main file for demo purpose):
Unit Testing all the things
Our loose coupling structure facilitates unit testing over our services and classes. The following example will use Jest as unit test framework. Jest is a testing platform powered by Facebook, it is used by Facebook to test all JavaScript code including React applications.
Jest also offers an mocking built-in library, it will be useful in our demo to mock up the repository functions. Let’s get started by testing the MusicCatalogService.get
method:
The previous test covers the MusicCatalogService.get
. First of all, it generates the mock result for the method get
. Secondly, it initialize the MusicCatalogServices
by using the generated mock.
Finally, we can test others method of MusicCatalogService
by using the same pattern:
Final thought
You can find the following demo on GitHub.
In conclusion, I think we should follow those suggestions:
- stop thinking that IoC containers have no place in JavaScript applications;
- start writing Object-oriented JavaScript code that follows the SOLID principles;
For more informations:
SOLID principles using Typescript
Cheers 🙂