Unleashing the Power of Dependency Injection and IoC Containers with InversifyJS and NestJS
Published: 2023-08-03 14:27:35

Welcome to an exploration into crafting robust, testable, and maintainable software. We'll delve into Dependency Inversion Principle (DIP) and Dependency Injection (DI), two design concepts crucial for achieving loose coupling and seamless dependency management in your applications.

Before we embark on this journey, let's understand each definition step by step:

**Dependency Inversion Principle** is one of the SOLID principles, emphasizing that high-level modules should not directly depend on low-level modules. Instead, both should depend on abstractions. By depending on abstractions, we achieve loose coupling and a modular design, making it easier to swap and extend components.

![Dependency Inversion Principle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0tmioabahocl7aymkui.jpg)

**Dependency Injection** is a practical implementation of DIP. It allows us to provide the dependencies of a class from an external source, typically through the constructor or setters. DI facilitates loose coupling, enhances testability, and promotes modular design.

🌟 Example: Without Dependency Injection

```
class NotificationService {
  private readonly emailService: EmailService;

  constructor() {
    // The NotificationService creates its own emailService.
    this.emailService = new EmailService();
  }

  sendNotification(message: string): void {
    // The NotificationService sends a notification.
    this.emailService.sendEmail(message);
  }
}
```

🌟 Example: With the Power of Dependency Injection!

```
class NotificationService {
  // The emailService joins the NotificationService as a companion.
  constructor(private readonly emailService: EmailService) {}

  sendNotification(message: string): void {
    // With the power of Dependency Injection, the NotificationService sends a notification.
    this.emailService.sendEmail(message);
  }
}
```


Now that we understand DIP and DI, let's talk a bit about the out-of-the-box solution:

**IoC Container** of InversifyJS - A Lightweight Solution:
Inversion of Control (IoC) containers are tools that manage the creation and resolution of dependencies in an application. InversifyJS is a powerful IoC container tailored for Typescript projects. It simplifies dependency management by allowing the registration of classes and interfaces, automating the resolution and instantiation of dependencies.

🌟 Example: using InversifyJS

```
import { Container, injectable, inject } from "inversify";

@injectable()
class EmailService {
  sendEmail(message: string): void {
    // Prepare for sending an email
    // The email is on its way! 💌🪄
  }
}

@injectable()
class NotificationService {
  constructor(@inject(EmailService) private readonly emailService: EmailService) {}

  sendNotification(message: string): void {
    // With InversifyJS, the NotificationService casts the spell of email sending! 🌈📧
    this.emailService.sendEmail(message);
  }
}

const container = new Container();
container.bind<EmailService>(EmailService).toSelf();
container.bind<NotificationService>(NotificationService).toSelf();

const notificationService = container.get<NotificationService>(NotificationService);
notificationService.sendNotification("Hello, World! Prepare to be amazed! 🌟🚀");
```

With InversifyJS, you can effortlessly manage dependencies and create a modular and organized architecture. It provides automation for your code.

But that's not all, folks! Now, let's teleport ourselves to the enchanting realm of NestJS - a powerful framework that knows how to dance with Dependency Injection and IoC too! NestJS offers a modular architecture that's perfect for large-scale projects. It's like having a magical beast that roars scalability and maintainability! 🦁🧭

🌟 Example: using NestJS

```
import { Controller, Get, Injectable, Module } from '@nestjs/common';
import { Inject } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';

@Injectable()
class EmailService {
  sendEmail(message: string): void {
    // Your email is on its way to the realm of the internet.
  }
}

@Controller()
class MagicController {
  constructor(private readonly emailService: EmailService) {}

  sendNotification(message: string): void {
    // Your message is being sent through the EmailService.
    this.emailService.sendEmail(message);
  }
}

@Module({
  controllers: [MagicController],
  providers: [EmailService],
})
class MagicModule {}

// Your NestJS app is about to get enchanted! ✨🔮
```

In this implementation, we've summoned the power of NestJS to create a MagicController that casts a spell using the EmailService. NestJS seamlessly injects the service into the controller.

To conclude our journey, remember that Dependency Injection and IoC containers promote flexibility and decoupling in software development. Embrace the lightweight spell-caster, InversifyJS, for smaller projects, and explore NestJS for larger endeavors. Together, these tools will transform your Typescript projects into remarkable software solutions.

Now, you can join us on this adventure! Let's code and create magic! 🧙‍♀️