Injeção de Dependência (Dependency Injection) em JavaScript: Guia Completo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que seu código precisa de Dependency Injection?



Todo desenvolvedor JavaScript, mais cedo ou mais tarde, enfrenta o problema do código "fortemente acoplado" (tightly coupled). Imagine uma classe UserService que cria internamente uma instância de Database e Logger. O que acontece quando você quiser testar UserService sem um banco de dados real? Ou substituir Logger por outra implementação? No melhor dos casos — você reescreve a classe inteira; no pior — quebra toda a aplicação.



A solução para esse problema é Dependency Injection (DI), ou injeção de dependências. Este é um dos princípios-chave de design que está na base do conceito de Inversion of Control (IoC). Neste artigo, vamos explorar o que é DI, como implementá-lo em JavaScript puro (Node.js) sem usar frameworks, e por que isso é fundamental para criar aplicações escaláveis e testáveis.



O que é Dependency Injection em termos simples?



Imagine que você está construindo uma casa. Em vez de embutir tomadas nas paredes para sempre, você instala conectores padrão. Hoje você conecta uma lâmpada antiga, amanhã — uma luminária inteligente. Você não muda as paredes — você muda as dependências.



Em programação, Dependency Injection é um padrão no qual um objeto recebe suas dependências (outros objetos, serviços, configurações) de fora, em vez de criá-las ele mesmo. Em vez de escrever:



// ❌ Ruim: acoplamento forteclass UserService {    constructor() {        this.db = new Database('mongodb://...');        this.logger = new ConsoleLogger();    }}


Nós escrevemos:



// ✅ Bom: injeção de dependênciasclass UserService {    constructor(database, logger) {        this.db = database;        this.logger = logger;    }}


Agora UserService não sabe como Database e Logger são criados. Sua tarefa é apenas usá-los. Isso é chamado de inversão de controle: o controle sobre a criação das dependências passa da classe para o código chamador.



Tipos de injeção de dependências



Existem três formas principais de injeção:

  • Através do construtor (Constructor Injection) — a forma mais comum e preferida. As dependências são passadas na criação do objeto.
  • Através de propriedades (Property Injection) — as dependências são definidas após a criação do objeto. Útil para dependências opcionais.
  • Através de métodos (Method Injection) — a dependência é passada para um método específico, quando necessária apenas para uma única ação.


Escrevendo um contêiner DI simples em Node.js



Em projetos grandes, gerenciar dependências manualmente se torna complicado. É aí que entra o contêiner DI — um objeto que sabe como criar e armazenar todas as dependências da sua aplicação. Vamos escrever um contêiner simples, mas funcional, em JavaScript puro.



Nosso contêiner suportará dois modos: singleton (uma única instância para toda a aplicação) e transient (uma nova instância a cada requisição).



// di-container.jsclass DIContainer {    constructor() {        this.registry = new Map();        this.instances = new Map();    }

// Registra uma dependência com uma função factory register(name, factory, { singleton = true } = {}) { this.registry.set(name, { factory, singleton }); if (singleton) { this.instances.delete(name); // Limpa o cache ao registrar novamente } }

// Obtém uma instância da dependência resolve(name) { const item = this.registry.get(name); if (!item) { throw new Error(`Dependência "${name}" não encontrada`); }

Blogs

Book Recommendations