InversifyJS: Injeção de Dependência em Projetos Node.js

A injeção de dependência (Dependency Injection – DI) é um padrão de design que melhora a modularidade e testabilidade do código. No ecossistema Node.js, o uso de DI pode ser extremamente benéfico, especialmente em aplicações escaláveis e orientadas a serviços. O InversifyJS é uma biblioteca poderosa para implementar DI em projetos Node.js que utilizam TypeScript.

O que é o InversifyJS?

O InversifyJS é um contêiner de injeção de dependência para TypeScript e JavaScript que segue os princípios SOLID. Ele permite a inversão de controle (IoC), promovendo um código desacoplado e de fácil manutenção.

Benefícios do InversifyJS

  • Desacoplamento: Reduz a dependência direta entre os módulos.
  • Facilidade de Testes: Permite a substituição fácil de dependências por mocks.
  • Organização: Ajuda na estruturação de código modular.
  • Compatibilidade: Integração simples com outras bibliotecas e frameworks.

Instalando o InversifyJS

Para começar, instale o InversifyJS e os tipos necessários:

npm install inversify reflect-metadata
Terminal

Adicione a opção emitDecoratorMetadata e experimentalDecorators ao arquivo tsconfig.json:

{
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  }
}
JSON

Implementando InversifyJS em um Projeto Node.js

1. Criando Interfaces e Classes

Defina uma interface para a dependência que deseja injetar:

export interface ILogger {
    log(message: string): void;
}
JavaScript

Agora, crie uma implementação concreta dessa interface:

import { injectable } from "inversify";
import { ILogger } from "./ILogger";

@injectable()
export class ConsoleLogger implements ILogger {
    log(message: string): void {
        console.log(`Log: ${message}`);
    }
}
JavaScript

2. Configurando o Contêiner de Injeção de Dependência

Crie um contêiner para gerenciar as dependências:

import { Container } from "inversify";
import { ILogger } from "./ILogger";
import { ConsoleLogger } from "./ConsoleLogger";

const container = new Container();
container.bind<ILogger>("ILogger").to(ConsoleLogger);

export { container };
JavaScript

3. Injetando Dependências em uma Classe

Crie uma classe que dependa do ILogger:

import "reflect-metadata";
import { inject, injectable } from "inversify";
import { ILogger } from "./ILogger";

@injectable()
export class Application {
    private logger: ILogger;

    constructor(@inject("ILogger") logger: ILogger) {
        this.logger = logger;
    }

    run(): void {
        this.logger.log("Aplicação iniciada!");
    }
}
JavaScript

4. Resolvendo Dependências e Executando o Código

Agora, podemos resolver as dependências e executar a aplicação:

import { container } from "./inversify.config";
import { Application } from "./Application";

const app = container.resolve(Application);
app.run();
JavaScript

Resolvendo dependências assíncronas

Ao trabalhar com projetos Node.js na maioria das vezes usamos funções assíncronas, as vezes é necessário que a construção de uma dependência seja de forma assíncrona, para isso o inversify nos dá uma opção para construir essas injeções mais elaboradas, por exemplo, com o método toDynamicValue podemos construir nossas dependências com uma lógica:

import { DataSource } from "typeorm";

container.bind<DataSource>("DataSource").toDynamicValue(async () => {
    const datasource = new DataSource({
        type: "postgres",
        host: "localhost",
        port: 5432,
        username: "root",
        password: "root"
    });
    
    if(!datasource.isInitialized) {
       await datasource.initialize(); 
    }
    return datasource;
}).inSingletonScope();
JavaScript

A criação da conexão com banco de dados é um exemplo muito bom para o uso do toDynamicValue, pois temos uma chamada assíncrona e que precisamos ao instanciar nossa classe passar uma séria de informações, mas quando vamos obter a instancia utilizando o próprio container, precisamos nos atentar a utilizar o getAsync. Ex.

const datasource = container.getAsync<DataSource>("DataSource");
JavaScript

Uma observação que podemos analisar nesse exemplo é o método que essa injeção acontecerá, já que podemos escolher algumas formas possíveis:

  • inSingletonScope: Uma única instância será compartilhada em todo o aplicativo.
  • inTransientScope: Cada vez que a dependência for solicitada, uma nova instância será criada.
  • inRequestScope: A mesma instância será usada durante o ciclo de vida de uma requisição.

Utilizando Constantes no InversifyJS

O InversifyJS permite a injeção de valores constantes usando bindConstantValue. Isso é útil para armazenar configurações globais ou valores fixos que não precisam de instância de classe.

container.bind<string>("API_URL").toConstantValue("https://api.exemplo.com");
JavaScript

Para injetar essa constante em uma classe:

@injectable()
export class ApiService {
    private apiUrl: string;

    constructor(@inject("API_URL") apiUrl: string) {
        this.apiUrl = apiUrl;
    }

    fetchData(): void {
        console.log(`Buscando dados da API em: ${this.apiUrl}`);
    }
}
JavaScript

Conclusão

O InversifyJS é uma excelente ferramenta para implementar Injeção de Dependência em projetos Node.js utilizando TypeScript. Ele melhora a estrutura do código, facilita a testabilidade e permite um desenvolvimento mais escalável e organizado.

Se você deseja um código mais limpo e modular, vale a pena adotar o InversifyJS em seu projeto!

Links úteis

Mauricio Lima
Mauricio Lima

Bacharel em Ciência da Computação, profissional dedicado ao desenvolvimento de software e entusiasta da tecnologia.

Artigos: 72