Dependency Injection on Node.JS
Sérgio Marcelino
52

Injeção de Dependências no Node.JS

Há algum tempo venho trabalhando com Node.JS e uma das coisas que amei na plataforma foi o NPM (Node Package Manager), ela permite gerenciarmos as dependências entre os módulos e também compartilharmos nossos projetos para que qualquer pessoa use.

Mas chega a ser muito chato quando você precisa gerenciar as dependências dentro de um mesmo projeto, linhas do tipo require(‘../../foo’); são muito confusas e quebram as premissas da Injeção de Dependência.

Uma premissa muito importante neste padrão de projeto é que os módulos não devem gerenciar as dependências que tem, quem deve gerenciar tem que ser um container externo com a responsabilidade única e exclusiva.

Dependency Injection (DI) is a software design pattern that deals with how components get hold of their dependencies. (AngularJS)

Acredito que o fato dos componentes (módulos) conhecerem onde as suas dependências se encontram no filesystem é um anti-pattern. Então pensei que é muito importante ter uma biblioteca que gerencia todos os módulos dentro do projeto, a injeção de dependência se torna transparente e fácil de plugar outras sem a necessidade de alterar qualquer código, deixando fácil inclusive os testes unitários.

Outra coisa que não curti muito no Node.JS é que, para cada módulo que criar, eu precisava colocar um require no meu app.js (main) ou em qualquer outro lugar, um exemplo clássico disto são os Roteadores Express.

Por estes motivos decidi desenvolver um módulo simples e leve que gerencia o carregamento de Módulos dentro de um projeto e injeta automaticamente as dependências durante o carregamento.

O Acqua

Este módulo tem como objetivo o carregamento automático (e recursivo) de módulos dentro de um projeto junto com a injeção de dependências em cada um destes módulos.

O que precisa ser configurado?

Basicamente nada precisa ser configurado para começar a utilizar o Acqua, você pode optar por simplesmente dar um new Acqua(); ou adicionar alguns loggers para que possa ser consultado o que está acontecendo durante o carregamento dos módulos. Segue um exemplo abaixo:

var acqua = new Acqua({ // new Acqua();
log : console.log, // used to log module imports, optional
err : console.err // used to log errors on module imports, optional
});
acqua.loadDir(__dirname + ‘/models’);
acqua.loadDir(__dirname + ‘/services’);
acqua.loadDir(__dirname + ‘/routers’);

Como precisa ficar a estrutura dos meus módulos a serem carregados?

A única peculiaridade do Acqua é que ele exige uma certa estrutura dentro de cada módulo. Eles precisam ser uma função:

module.exports = function ModuleName (dependencias, pelo, nome) {
this.myFunction = function () {
// implementação
};
return this;
};

Repare que no exemplo acima eu associei uma função nomeada ao module.exports e, dentro dela, precisei retornar this. Neste caso foi necessário, mas podem existir casos que não há a necessidade de nomear.

Quando o Acqua carregar este módulo, ele vai nomeá-lo com “ModuleName”, injetará todas as dependências que a função receber por parâmetro, buscando-as dentro do contexto pelo nome dado ao parâmetro, irá executar a função e, caso haja algum retorno desta função, o Acqua irá adicionar o retorno ao contexto, caso contrário, apenas irá executar a função.

E se eu quiser adicionar um módulo já carregado ao contexto?

Também é possível adicionar um módulo ou variável ao contexto sem a necessidade de carregar um arquivo.

acqua.add(‘myStringVar’, ‘myValue’);
acqua.add(‘myIntegerVar’, 10);
acqua.add(‘app’, app);

Poderemos usar as variáveis inseridas acima em qualquer módulo carregado (desde que o carregamento ocorra após a inserção das variáveis).

module.exports = function (myStringVar, myIntegerVar, app) {
console.log(myStringVar, myIntegerVar);
};

Conclusão

Depois que desenvolvi este módulo, venho trabalhando com ele em alguns projetos e estou bastante satisfeito, nunca foi tão simples gerenciar as dependências sem um unico require na maioria dos módulos.

Qualquer dúvida que você tiver ou sugestão, deixa um comentário para poder discutirmos, toda opinião é bem-vinda! ☺

Show your support

Clapping shows how much you appreciated Sérgio Marcelino’s story.