Atualmente, estou criando alguns projetos experimentais com o nodejs. Programei muitas aplicações web Java EE com o Spring e apreciei a facilidade de injeção de dependência lá.
Agora estou curioso: como faço para injetar dependência no nó? Ou: eu preciso disso? Existe um conceito de substituição, porque o estilo de programação é diferente?
Estou falando de coisas simples, como compartilhar um objeto de conexão com o banco de dados até agora, mas não encontrei uma solução que me satisfaça.
Respostas:
Em resumo, você não precisa de um contêiner de injeção de dependência ou localizador de serviço, como faria em C # / Java. Desde o Node.js, aproveita o
module pattern
, não é necessário executar a injeção de construtor ou propriedade. Embora você ainda possa.O melhor de JS é que você pode modificar praticamente qualquer coisa para alcançar o que deseja. Isso é útil quando se trata de testes.
Eis meu exemplo artificial muito coxo.
MyClass.js
:MyClass.test.js
:Observe como
MyClass
depende dofs
módulo? Como o @ShatyemShekhar mencionou, você pode realmente executar injeção de construtor ou propriedade como em outros idiomas. Mas não é necessário em Javascript.Nesse caso, você pode fazer duas coisas.
Você pode stub o
fs.readdirSync
método ou você pode retornar um módulo totalmente diferente quando você chamarequire
.Método 1:
Método 2:
A chave é aproveitar o poder do Node.js e Javascript. Observe que eu sou do tipo CoffeeScript, então minha sintaxe JS pode estar incorreta em algum lugar. Além disso, não estou dizendo que esse é o melhor caminho, mas é um caminho. Os gurus do Javascript podem ser capazes de acompanhar outras soluções.
Atualizar:
Isso deve abordar sua pergunta específica sobre conexões com o banco de dados. Eu criaria um módulo separado para encapsular sua lógica de conexão com o banco de dados. Algo assim:
MyDbConnection.js
: (certifique-se de escolher um nome melhor)Então, qualquer módulo que precise de uma conexão com o banco de dados incluiria apenas o seu
MyDbConnection
módulo.SuperCoolWebApp.js
:Não siga este exemplo literalmente. É um exemplo fraco ao tentar comunicar que você aproveita o
module
padrão para gerenciar suas dependências. Espero que isso ajude um pouco mais.fonte
require('my_logger_library')
, as pessoas que usam meu componente precisarão substituir a necessidade de usar sua própria biblioteca. Em vez disso, posso permitir que as pessoas passem um retorno de chamada que agrupe uma implementação do criador de logs no método "construtor" ou "init" dos componentes. Esse é o objetivo do DI.require
é a maneira de gerenciar dependências no Node.js e certamente é intuitivo e eficaz, mas também possui suas limitações.Meu conselho é dar uma olhada em alguns dos contêineres de injeção de dependência disponíveis hoje para o Node.js para ter uma idéia de quais são seus prós / contras. Alguns deles são:
Só para citar alguns.
Agora, a verdadeira questão é: o que você pode obter com um contêiner DI do Node.js. comparado a um simples
require
?Prós:
Contras:
require
definitivamente parece que você está se desviando da maneira de pensar do Nó.Como em qualquer coisa relacionada ao desenvolvimento de software, a escolha entre DI ou
require
depende de seus requisitos, complexidade do sistema e estilo de programação.fonte
Eu sei que esse tópico é bastante antigo neste momento, mas achei que iria concordar com meus pensamentos sobre isso. O TL; DR é que, devido à natureza dinâmica e sem tipo do JavaScript, você pode realmente fazer bastante sem recorrer ao padrão de injeção de dependência (DI) ou usar uma estrutura de DI. No entanto, à medida que um aplicativo se torna maior e mais complexo, o DI pode definitivamente ajudar na manutenção do seu código.
DI em C #
Para entender por que o DI não é tão necessário em JavaScript, é útil olhar para uma linguagem fortemente tipada como C #. (Desculpas para quem não conhece C #, mas deve ser fácil o suficiente.) Diga que temos um aplicativo que descreve um carro e sua buzina. Você definiria duas classes:
Existem alguns problemas ao escrever o código dessa maneira.
Car
classe está fortemente acoplada à implementação específica da buzina naHorn
classe. Se queremos mudar o tipo de buzina usada pelo carro, temos que modificar aCar
classe, mesmo que o uso da buzina não mude. Isso também dificulta o teste porque não podemos testar aCar
classe isoladamente de sua dependência, aHorn
classe.Car
classe é responsável pelo ciclo de vida daHorn
classe. Em um exemplo simples como esse, não é um grande problema, mas em aplicativos reais as dependências terão dependências, que terão dependências etc. ACar
classe precisaria ser responsável por criar a árvore inteira de suas dependências. Isso não é apenas complicado e repetitivo, mas viola a "responsabilidade única" da classe. Deve se concentrar em ser um carro, não em criar instâncias.Agora, vamos refatorar isso para usar um padrão de injeção de dependência.
Fizemos duas coisas importantes aqui. Primeiro, introduzimos uma interface que nossa
Horn
classe implementa. Isso nos permite codificar aCar
classe para a interface, em vez da implementação específica. Agora o código pode levar qualquer coisa que seja implementadaIHorn
. Segundo, retiramos a instanciação da buzinaCar
e a transmitimos. Isso resolve os problemas acima e deixa a função principal do aplicativo gerenciar instâncias específicas e seus ciclos de vida.O que isto significa é que poderia introduzir um novo tipo de buzina para o carro usar sem tocar na
Car
classe:O main pode apenas injetar uma instância da
FrenchHorn
classe. Isso também simplifica drasticamente os testes. Você pode criar umaMockHorn
classe para injetar noCar
construtor para garantir que você está testando apenas aCar
classe isoladamente.O exemplo acima mostra injeção de dependência manual. Normalmente, o DI é feito com uma estrutura (por exemplo, Unity ou Ninject no mundo C #). Essas estruturas farão toda a fiação de dependência para você, percorrendo seu gráfico de dependência e criando instâncias, conforme necessário.
A maneira padrão do Node.js
Agora vamos ver o mesmo exemplo no Node.js. Provavelmente dividiríamos nosso código em 3 módulos:
Como o JavaScript não é digitado, não temos o mesmo acoplamento rígido que tínhamos antes. Não há necessidade de interfaces (nem elas existem), pois o
car
módulo apenas tentará chamar ohonk
método em qualquer que seja ahorn
exportação do módulo.Além disso, como o Node
require
armazena em cache tudo, os módulos são essencialmente singletons armazenados em um contêiner. Qualquer outro módulo que execute umrequire
nohorn
módulo obterá exatamente a mesma instância. Isso facilita muito o compartilhamento de objetos singleton, como conexões com o banco de dados.Agora ainda há o problema de o
car
módulo ser responsável por buscar sua própria dependênciahorn
. Se você quisesse que o carro usasse um módulo diferente para a buzina, seria necessário alterar arequire
declaração nocar
módulo. Isso não é algo muito comum, mas causa problemas com o teste.A maneira usual pelas quais as pessoas lidam com o problema de teste é com o proxyquire . Devido à natureza dinâmica do JavaScript, o proxyquire intercepta chamadas para exigir e retorna todos os stubs / zombarias que você fornece.
Isso é mais do que suficiente para a maioria dos aplicativos. Se funcionar para o seu aplicativo, vá com ele. No entanto, na minha experiência à medida que os aplicativos se tornam maiores e mais complexos, manter um código como esse se torna mais difícil.
DI em JavaScript
O Node.js é muito flexível. Se você não estiver satisfeito com o método acima, poderá escrever seus módulos usando o padrão de injeção de dependência. Nesse padrão, todo módulo exporta uma função de fábrica (ou um construtor de classe).
Isso é muito análogo ao método C # anteriormente, pois o
index.js
módulo é responsável por ciclos de vida e fiação. O teste de unidade é bastante simples, pois você pode simplesmente passar zombarias / stubs para as funções. Novamente, se isso for bom o suficiente para o seu aplicativo, vá com ele.Bolus DI Framework
Ao contrário do C #, não existem estruturas de DI padrão estabelecidas para ajudar no gerenciamento de dependências. Há várias estruturas no registro npm, mas nenhuma possui adoção generalizada. Muitas dessas opções já foram citadas nas outras respostas.
Eu não estava particularmente feliz com nenhuma das opções disponíveis, então escrevi meu próprio bolus . O Bolus foi projetado para funcionar com o código escrito no estilo DI acima e tenta ser muito SECO e muito simples. Usando exatamente o mesmo
car.js
e oshorn.js
módulos acima, você pode reescrever oindex.js
módulo com bolus como:A idéia básica é que você crie um injetor. Você registra todos os seus módulos no injetor. Então você simplesmente resolve o que precisa. O Bolus percorrerá o gráfico de dependência e criará e injetará dependências, conforme necessário. Você não economiza muito em um exemplo de brinquedo como esse, mas em aplicativos grandes com árvores de dependência complicadas, as economias são enormes.
O Bolus suporta vários recursos interessantes, como dependências opcionais e globais de teste, mas há dois benefícios principais que eu já vi em relação à abordagem padrão do Node.js. Primeiro, se você tiver muitos aplicativos semelhantes, poderá criar um módulo npm privado para sua base que crie um injetor e registre objetos úteis nele. Em seguida, seus aplicativos específicos podem adicionar, substituir e resolver, conforme necessário, da mesma forma que o AngularJSinjetor funciona. Segundo, você pode usar o bolus para gerenciar vários contextos de dependências. Por exemplo, você pode usar o middleware para criar um injetor secundário por solicitação, registrar o ID do usuário, o ID da sessão, o registrador etc. no injetor, juntamente com quaisquer módulos, dependendo deles. Em seguida, resolva o que você precisa para atender solicitações. Isso fornece instâncias de seus módulos por solicitação e evita a necessidade de passar o criador de logs etc. para todas as chamadas de função do módulo.
fonte
proxyquire
como osinon
que lhe permite fazer simulações muito concisas, por exemplo,let readFileStub = sinon.stub(fs, 'readFile').yields(new Error('something went wrong'));
e, em seguida, chamadas subsequentes parafs.readFile
retornará erro até que você reverter o stub viareadFileStub.restore()
. Pessoalmente, acho o DI de uso questionável porque sinto que quase requer o uso de classes / objetos, o que é uma suposição dúbia, considerando as inclinações funcionais do javascript.jest
,rewire
,proxyquire
, etc.? Obrigado.Também escrevi um módulo para fazer isso, chamado rewire . Basta usar
npm install rewire
e depois:Eu fui inspirado pelo injectr de Nathan MacInnes, mas usei uma abordagem diferente. Eu não uso
vm
para avaliar o módulo de teste, na verdade eu uso o próprio nó exige. Dessa forma, seu módulo se comporta exatamente como o usorequire()
(exceto suas modificações). Também a depuração é totalmente suportada.fonte
Eu construí o Electrolyte para esse fim. As outras soluções de injeção de dependência lá fora eram muito invasivas para o meu gosto, e mexer com o global
require
é uma queixa particular minha.O Electrolyte abrange módulos, especificamente aqueles que exportam uma função de "configuração" como você vê no middleware do Connect / Express. Essencialmente, esses tipos de módulos são apenas fábricas para algum objeto que eles retornam.
Por exemplo, um módulo que cria uma conexão com o banco de dados:
O que você vê na parte inferior é anotações , um bit extra de metadados que o Electrolyte usa para instanciar e injetar dependências, conectando automaticamente os componentes do aplicativo.
Para criar uma conexão com o banco de dados:
O eletrólito atravessa transitivamente o
@require
dependências e injeta instâncias como argumentos para a função exportada.A chave é que isso é minimamente invasivo. Este módulo é completamente utilizável, independente do próprio eletrólito. Isso significa que seus testes de unidade podem testar apenas o módulo sob teste , passando objetos simulados sem a necessidade de dependências adicionais para reconectar os internos.
Ao executar o aplicativo completo, o Electrolyte entra no nível entre módulos, conectando as coisas sem a necessidade de globais, singletons ou encanamento excessivo.
fonte
connect()
? Mesmo que eu não esteja familiarizado com a API do MySQL para Nó, eu esperaria que esta chamada fosse assíncrona, portanto a ilustração não é clara.ioc.create
de um teste de unidade. Um teste de unidade deve testar apenas o módulo em teste e não gerar outras dependências, incluindo Eletrólito. Seguindo esse conselho, você fariaobjToTest = require('modulename')(mockObj1, mockObj2);
Eu mesmo examinei isso. Não gosto de introduzir bibliotecas de utilitários de dependência mágica que fornecem mecanismos para seqüestrar minhas importações de módulos. Em vez disso, criei uma "diretriz de design" para minha equipe declarar explicitamente quais dependências podem ser zombadas ao introduzir uma exportação de função de fábrica em meus módulos.
Faço uso extensivo dos recursos do ES6 para parâmetros e desestruturação, a fim de evitar alguns clichês e fornecer um mecanismo de substituição de dependência nomeado.
Aqui está um exemplo:
E aqui está um exemplo de seu uso
Desculpe a sintaxe do ES6 para quem não está familiarizado.
fonte
Recentemente, verifiquei esse tópico pelo mesmo motivo que o OP - a maioria das bibliotecas que encontrei reescreve temporariamente a instrução require. Eu tive vários graus de sucesso com esse método e, por isso, acabei usando a seguinte abordagem.
No contexto de um aplicativo expresso - envolvo app.js em um arquivo bootstrap.js:
O mapa de objetos passado para o carregador se parece com o seguinte:
Em vez de chamar diretamente, é necessário ...
Se nenhum alias estiver localizado no carregador - o padrão será apenas um requisito regular. Isso tem dois benefícios: eu posso trocar em qualquer versão da classe e remover a necessidade de usar nomes de caminho relativos em todo o aplicativo (por isso, se eu precisar de uma lib personalizada abaixo ou acima do arquivo atual, não preciso percorrer , e require irá armazenar em cache o módulo na mesma chave). Também me permite especificar simulações em qualquer ponto do aplicativo, e não no conjunto de testes imediato.
Acabei de publicar um pequeno módulo npm por conveniência:
https://npmjs.org/package/nodejs-simple-loader
fonte
A realidade é que você pode testar o node.js sem o contêiner de IoC porque o JavaScript é uma linguagem de programação realmente dinâmica e você pode modificar quase tudo em tempo de execução.
Considere o seguinte:
Assim, você pode substituir o acoplamento entre os componentes em tempo de execução. Eu gosto de pensar que devemos procurar desacoplar nossos módulos JavaScript.
A única maneira de obter desacoplamento real é removendo a referência ao
UserRepository
:Isso significa que em outro lugar você precisará fazer a composição do objeto:
Eu gosto da ideia de delegar a composição do objeto em um contêiner de IoC. Você pode aprender mais sobre essa idéia no artigo O estado atual da inversão de dependência em JavaScript . O artigo tenta desmascarar alguns "mitos de container JavaScript IoC":
Se você também gosta da ideia de usar um contêiner de IoC, consulte o InversifyJS. A versão mais recente (2.0.0) suporta muitos casos de uso:
Você pode aprender mais sobre isso no InversifyJS .
fonte
Para o ES6, desenvolvi este contêiner https://github.com/zazoomauro/node-dependency-injection
Depois, você pode definir, por exemplo, a escolha de transporte no contêiner:
Essa classe agora é muito mais flexível, pois você separou a opção de transporte da implementação e para o contêiner.
Agora que o serviço de mala direta está no contêiner, você pode injetá-lo como uma dependência de outras classes. Se você tem uma classe NewsletterManager como esta:
Ao definir o serviço newsletter_manager, o serviço de mala direta ainda não existe. Use a classe Reference para instruir o contêiner a injetar o serviço de correspondência quando inicializar o gerenciador de boletins:
Você também pode configurar o contêiner com arquivos de configuração, como arquivos Yaml, Json ou JS
O contêiner de serviço pode ser compilado por vários motivos. Esses motivos incluem a verificação de possíveis problemas, como referências circulares, e a eficiência do contêiner.
fonte
Depende do design do seu aplicativo. Obviamente, você pode fazer uma injeção como java onde cria um objeto de uma classe com a dependência passada no construtor como esta.
Se você não estiver executando OOP em javascript, poderá criar uma função init que configure tudo.
No entanto, existe outra abordagem que você pode adotar, que é mais comum em um sistema baseado em eventos, como o node.js. Se você pode modelar seu aplicativo para agir (na maioria das vezes) apenas em eventos, tudo o que você precisa fazer é configurar tudo (o que eu costumo chamar chamando uma função init) e emitir eventos de um stub. Isso torna o teste bastante mais fácil e legível.
fonte
Eu sempre gostei da simplicidade do conceito de IoC - "Você não precisa saber nada sobre ambiente, será chamado por alguém quando necessário"
Mas todas as implementações de IoC que vi fizeram exatamente o oposto - elas confundem o código com ainda mais coisas do que sem ele. Então, eu criei minha própria IoC que funciona como eu gostaria que fosse - ela fica oculta e invisível 90% do tempo .
É usado na estrutura da web MonoJS http://monojs.org
É feito assim - registre o componente uma vez na configuração.
E use-o em qualquer lugar
Você pode ver o código completo de definição de componente (com DB Connection e outros componentes) aqui https://github.com/sinizinairina/mono/blob/master/mono.coffee
Este é o único lugar em que você precisa informar à IoC o que fazer, depois que todos esses componentes serão criados e conectados automaticamente e você não precisará mais ver o código específico da IoC em seu aplicativo.
O IoC em si https://github.com/alexeypetrushin/miconjs
fonte
Acho que ainda precisamos da Injeção de Dependências no Nodejs, porque isso afrouxa as dependências entre os serviços e torna a aplicação mais clara.
Inspirado no Spring Framework , também implemento meu próprio módulo para suportar a injeção de dependência no Nodejs. Meu módulo também é capaz de detectar o
code changes
eauto reload
os serviços sem reiniciar o aplicativo.Visite meu projeto em: Buncha - container IoC
Obrigado!
fonte
Veja os mergulhos (Uma estrutura de gerenciamento de entidades e arquivos (injeção) de dependência simples e poderosa para o Node.js.
https://github.com/devcrust/node-dips
fonte
Eu trabalhei com .Net, PHP e Java por muito tempo, então também queria ter uma injeção de dependência conveniente no NodeJS. As pessoas disseram que o DI interno no NodeJS é suficiente, como podemos obter com o Module. Mas isso não me satisfez bem. Eu queria manter um módulo não mais do que uma classe. Além disso, eu queria que o DI tivesse um suporte completo para o gerenciamento do ciclo de vida do módulo (módulo singleton, módulo transitório etc.), mas com o módulo Node, tive que escrever código manual com muita frequência. Por fim, eu queria facilitar o teste de unidade. Por isso, criei uma injeção de dependência para mim.
Se você estiver procurando por uma DI, tente. Pode ser encontrado aqui: https://github.com/robo-creative/nodejs-robo-container . Está totalmente documentado. Ele também aborda alguns problemas comuns com o DI e como resolvê-los da maneira OOP. Espero que ajude.
fonte
Eu criei recentemente uma biblioteca chamada circuitbox que permite usar injeção de dependência com o node.js. Realiza injeção de dependência em comparação com muitas das bibliotecas baseadas em pesquisa de dependência que eu já vi. O Circuitbox também suporta rotinas de criação e inicialização assíncronas. Abaixo está um exemplo:
Suponha que o código a seguir esteja em um arquivo chamado consoleMessagePrinter.js
Suponha que o seguinte esteja no arquivo main.js
O Circuitbox permite definir seus componentes e declarar suas dependências como módulos. Uma vez inicializado, ele permite recuperar um componente. O Circuitbox injeta automaticamente todos os componentes exigidos pelo componente de destino e o fornece para uso.
O projeto está na versão alfa. Seus comentários, idéias e feedback são bem-vindos.
Espero que ajude!
fonte
Acho que outras postagens fizeram um ótimo trabalho no argumento de usar DI. Para mim, as razões são
Injete dependências sem conhecer seus caminhos. Isso significa que, se você alterar um local do módulo no disco ou trocá-lo por outro, não precisará tocar em todos os arquivos que dependem dele.
Torna muito mais fácil zombar de dependências para teste sem a necessidade de substituir a
require
função global de uma maneira que funcione sem problemas.Ajuda a organizar e raciocinar sobre sua aplicação como módulos fracamente acoplados.
Mas tive muita dificuldade em encontrar uma estrutura de DI que eu e minha equipe possamos adotar facilmente. Então, eu criei recentemente uma estrutura chamada deppie com base nesses recursos
require
módulosfonte
Deve ser flexível e simples assim:
Eu escrevi artigo sobre Injeção de Dependência em node.js.
Espero que possa ajudá-lo com isso.
fonte
O Node.js requer DI tanto quanto qualquer outra plataforma. Se você estiver criando algo grande, o DI tornará mais fácil zombar das dependências do seu código e testá-lo completamente.
Os módulos da camada de banco de dados, por exemplo, não devem ser apenas necessários nos módulos de código comercial, porque, ao testar a unidade desses módulos de código comercial, os daos carregarão e se conectarão ao banco de dados.
Uma solução seria passar as dependências como parâmetros do módulo:
Dessa forma, as dependências podem ser ridicularizadas fácil e naturalmente, e você pode se concentrar em testar seu código, sem usar nenhuma biblioteca de terceiros complicada.
Existem outras soluções por aí (broadway, architect etc) que podem ajudá-lo com isso. embora eles possam fazer mais do que você deseja ou usar mais desorganização.
fonte
Eu desenvolvi uma biblioteca que lida com a injeção de dependência de uma maneira simples, que diminui o código padrão. Cada módulo é definido por um nome exclusivo e uma função de controlador. Os parâmetros do controlador refletem as dependências do módulo.
Leia mais sobre KlarkJS
Breve exemplo:
myModuleName1
é o nome do módulo.$nodeModule1
é uma biblioteca externa denode_module
. O nome resolve paranode-module1
. O prefixo$
indica que é um módulo externo.myModuleName2
é o nome de um módulo interno.myModuleName1
.fonte
Eu descobri essa pergunta ao responder a um problema no meu próprio módulo DI, perguntando por que alguém precisaria de um sistema DI para a programação do NodeJS.
A resposta estava claramente tendendo àquelas dadas neste tópico: depende. Existem trade-offs para ambas as abordagens e a leitura das respostas desta pergunta fornece uma boa forma delas.
Portanto, a resposta real para essa pergunta deve ser que, em algumas situações, você usaria um sistema DI, em outras não.
Dito isto, o que você deseja como desenvolvedor é não se repetir e reutilizar seus serviços em seus vários aplicativos.
Isso significa que devemos escrever serviços prontos para serem usados no sistema DI, mas não vinculados às bibliotecas de DI. Para mim, isso significa que devemos escrever serviços como este:
Dessa forma, seu serviço funciona, não importa se você o usa com ou sem uma ferramenta de DI.
fonte
TypeDI é o mais doce de todos os mencionados aqui, veja este código em TypeDI
Veja este código também:
fonte