Je crée actuellement des projets expérimentaux avec nodejs. J'ai programmé beaucoup d'applications Web Java EE avec Spring et apprécié la facilité d'injection de dépendances là-bas.
Maintenant, je suis curieux: comment puis-je faire l'injection de dépendance avec le nœud? Ou: en ai-je même besoin? Y a-t-il un concept de remplacement, car le style de programmation est différent?
Je parle de choses simples, comme partager un objet de connexion à une base de données, jusqu'à présent, mais je n'ai pas trouvé de solution qui me satisfasse.
Réponses:
En bref, vous n'avez pas besoin d'un conteneur d'injection de dépendances ou d'un localisateur de services comme vous le feriez en C # / Java. Depuis Node.js, exploite le
module pattern
, il n'est pas nécessaire d'effectuer une injection de constructeur ou de propriété. Bien que vous puissiez toujours.La grande chose à propos de JS est que vous pouvez modifier à peu près n'importe quoi pour obtenir ce que vous voulez. Cela est très utile pour les tests.
Voici mon exemple artificiel très boiteux.
MyClass.js
:MyClass.test.js
:Remarquez comment cela
MyClass
dépend dufs
module? Comme l'a mentionné @ShatyemShekhar, vous pouvez en effet faire une injection de constructeur ou de propriété comme dans d'autres langues. Mais ce n'est pas nécessaire en Javascript.Dans ce cas, vous pouvez faire deux choses.
Vous pouvez bloquer la
fs.readdirSync
méthode ou renvoyer un module entièrement différent lorsque vous appelezrequire
.Méthode 1:
Méthode 2:
La clé est de tirer parti de la puissance de Node.js et Javascript. Remarque, je suis un gars CoffeeScript, donc ma syntaxe JS peut être incorrecte quelque part. De plus, je ne dis pas que c'est la meilleure façon, mais c'est une façon. Les gourous de Javascript pourraient être en mesure d'intégrer d'autres solutions.
Mettre à jour:
Cela devrait répondre à votre question spécifique concernant les connexions à la base de données. Je créerais un module distinct pour que vous encapsuliez votre logique de connexion à la base de données. Quelque chose comme ça:
MyDbConnection.js
: (assurez-vous de choisir un meilleur nom)Ensuite, tout module qui a besoin d'une connexion à une base de données inclurait alors simplement votre
MyDbConnection
module.SuperCoolWebApp.js
:Ne suivez pas cet exemple textuellement. C'est un exemple boiteux d'essayer de communiquer que vous exploitez le
module
modèle pour gérer vos dépendances. Espérons que cela aide un peu plus.la source
require('my_logger_library')
, les personnes utilisant mon composant devront remplacer le besoin d'utiliser leur propre bibliothèque. Au lieu de cela, je peux permettre aux gens de passer un rappel qui encapsule une implémentation de l'enregistreur dans la méthode "constructeur" ou "init" des composants. C'est le but de DI.require
est le moyen de gérer les dépendances dans Node.js et il est sûrement intuitif et efficace, mais il a aussi ses limites.Mon conseil est de jeter un œil à certains des conteneurs d'injection de dépendance disponibles aujourd'hui pour Node.js pour avoir une idée de leurs avantages / inconvénients. Certains d'entre eux sont:
Juste pour en nommer quelques-uns.
Maintenant, la vraie question est, que pouvez-vous réaliser avec un conteneur DI Node.js, par rapport à un simple
require
?Avantages:
Les inconvénients:
require
l'impression de s'écarter de la façon de penser de Node.Comme pour tout ce qui concerne le développement logiciel, le choix entre DI ou
require
dépend de vos besoins, de la complexité de votre système et de votre style de programmation.la source
Je sais que ce fil est assez ancien à ce stade, mais je pensais que je ferais écho à mes réflexions à ce sujet. Le TL; DR est qu'en raison de la nature dynamique et non typée de JavaScript, vous pouvez en fait faire beaucoup de choses sans recourir au modèle d'injection de dépendance (DI) ou utiliser un framework DI. Cependant, à mesure qu'une application devient plus grande et plus complexe, DI peut certainement aider à la maintenabilité de votre code.
DI en C #
Pour comprendre pourquoi DI n'est pas un besoin aussi important en JavaScript, il est utile de regarder un langage fortement typé comme C #. (Excuses à ceux qui ne connaissent pas C #, mais cela devrait être assez facile à suivre.) Disons que nous avons une application qui décrit une voiture et son klaxon. Vous définiriez deux classes:
Il y a peu de problèmes avec l'écriture du code de cette façon.
Car
classe est étroitement couplée à l'implémentation particulière du klaxon dans laHorn
classe. Si nous voulons changer le type de klaxon utilisé par la voiture, nous devons modifier laCar
classe même si son utilisation du klaxon ne change pas. Cela rend également les tests difficiles car nous ne pouvons pas tester laCar
classe indépendamment de sa dépendance, laHorn
classe.Car
classe est responsable du cycle de vie de laHorn
classe. Dans un exemple simple comme celui-ci, ce n'est pas un gros problème, mais dans les applications réelles, les dépendances auront des dépendances, qui auront des dépendances, etc. LaCar
classe devrait être responsable de la création de l'arborescence complète de ses dépendances. Ce n'est pas seulement compliqué et répétitif, mais cela viole la "responsabilité unique" de la classe. Il devrait se concentrer sur le fait d'être une voiture, pas sur la création d'instances.Maintenant, refactorisons ceci pour utiliser un modèle d'injection de dépendance.
Nous avons fait deux choses clés ici. Tout d'abord, nous avons introduit une interface que notre
Horn
classe implémente. Cela nous permet de coder laCar
classe sur l'interface au lieu de l'implémentation particulière. Maintenant, le code peut accepter tout ce qui est implémentéIHorn
. Deuxièmement, nous avons retiré l'instanciation du klaxon et l'avonsCar
plutôt transmise. Cela résout les problèmes ci-dessus et laisse à la fonction principale de l'application le soin de gérer les instances spécifiques et leurs cycles de vie.Cela signifie que cela pourrait introduire un nouveau type de klaxon à utiliser sans toucher la
Car
classe:Le principal pourrait simplement injecter une instance de la
FrenchHorn
classe à la place. Cela simplifie également considérablement les tests. Vous pouvez créer uneMockHorn
classe à injecter dans leCar
constructeur pour vous assurer de tester uniquement laCar
classe de manière isolée.L'exemple ci-dessus montre l'injection de dépendance manuelle. Typiquement DI est fait avec un framework (par exemple Unity ou Ninject dans le monde C #). Ces frameworks feront tout le câblage de dépendance pour vous en parcourant votre graphique de dépendance et en créant des instances selon les besoins.
La méthode Node.js standard
Voyons maintenant le même exemple dans Node.js. Nous diviserions probablement notre code en 3 modules:
Parce que JavaScript n'est pas typé, nous n'avons pas le même couplage étroit que nous avions auparavant. Il n'y a pas besoin d'interfaces (elles n'existent pas non plus) car le
car
module tentera simplement d'appeler lahonk
méthode sur tout ce que lehorn
module exporte.De plus, étant donné que Node
require
met tout en cache, les modules sont essentiellement des singletons stockés dans un conteneur. Tout autre module qui effectue unrequire
sur lehorn
module obtiendra exactement la même instance. Cela facilite le partage d'objets singleton comme les connexions à la base de données.Maintenant, il y a toujours le problème que le
car
module est chargé de récupérer sa propre dépendancehorn
. Si vous vouliez que la voiture utilise un module différent pour son klaxon, vous devriez changer larequire
déclaration dans lecar
module. Ce n'est pas une chose très courante à faire, mais cela pose des problèmes avec les tests.La manière habituelle de gérer le problème de test est avec proxyquire . En raison de la nature dynamique de JavaScript, proxyquire intercepte les appels pour exiger et retourner tous les talons / maquettes que vous fournissez à la place.
C'est plus que suffisant pour la plupart des applications. Si cela fonctionne pour votre application, allez-y. Cependant, d'après mon expérience, les applications devenant plus grandes et plus complexes, il est plus difficile de maintenir ce code.
DI en JavaScript
Node.js est très flexible. Si vous n'êtes pas satisfait de la méthode ci-dessus, vous pouvez écrire vos modules en utilisant le modèle d'injection de dépendance. Dans ce modèle, chaque module exporte une fonction d'usine (ou un constructeur de classe).
Ceci est très similaire à la méthode C # plus tôt dans la mesure où le
index.js
module est responsable des cycles de vie et du câblage par exemple. Le test unitaire est assez simple car vous pouvez simplement passer des simulations / stubs aux fonctions. Encore une fois, si cela suffit pour votre application, allez-y.Bolus DI Framework
Contrairement à C #, il n'y a pas de framework DI standard établi pour vous aider dans votre gestion des dépendances. Il existe un certain nombre de cadres dans le registre npm, mais aucun n'est largement adopté. Beaucoup de ces options ont déjà été citées dans les autres réponses.
Je n'étais pas particulièrement satisfait de l'une des options disponibles, j'ai donc écrit mon propre bolus . Bolus est conçu pour fonctionner avec du code écrit dans le style DI ci-dessus et essaie d'être très SEC et très simple. En utilisant exactement les mêmes modules
car.js
ethorn.js
modules ci-dessus, vous pouvez réécrire leindex.js
module avec bolus comme:L'idée de base est de créer un injecteur. Vous enregistrez tous vos modules dans l'injecteur. Ensuite, vous résolvez simplement ce dont vous avez besoin. Bolus parcourra le graphique des dépendances et créera et injectera des dépendances selon les besoins. Vous n'économisez pas beaucoup dans un exemple de jouet comme celui-ci, mais dans les grandes applications avec des arbres de dépendance complexes, les économies sont énormes.
Bolus prend en charge un tas de fonctionnalités astucieuses comme les dépendances optionnelles et les globaux de test, mais il y a deux avantages clés que j'ai vus par rapport à l'approche Node.js standard. Premièrement, si vous avez beaucoup d'applications similaires, vous pouvez créer un module npm privé pour votre base qui crée un injecteur et y enregistre des objets utiles. Ensuite, vos applications spécifiques peuvent ajouter, remplacer et résoudre au besoin, tout comme la façon dont AngularJSl'injecteur fonctionne. Deuxièmement, vous pouvez utiliser un bolus pour gérer divers contextes de dépendances. Par exemple, vous pouvez utiliser un middleware pour créer un injecteur enfant par demande, enregistrer l'ID utilisateur, l'ID de session, l'enregistreur, etc. sur l'injecteur ainsi que les modules qui en dépendent. Ensuite, résolvez ce dont vous avez besoin pour répondre aux demandes. Cela vous donne des instances de vos modules par demande et évite d'avoir à transmettre l'enregistreur, etc. à chaque appel de fonction de module.
la source
proxyquire
telle quesinon
qui vous permet de faire des simulations très concises, par exemplelet readFileStub = sinon.stub(fs, 'readFile').yields(new Error('something went wrong'));
, puis les appels suivants pourfs.readFile
retourner une erreur jusqu'à ce que vous reveniez le stub viareadFileStub.restore()
. Personnellement, je trouve DI d'une utilisation douteuse car j'ai l'impression que cela nécessite presque l'utilisation de classes / objets, ce qui est une hypothèse douteuse étant donné les tendances fonctionnelles de javascript.jest
,rewire
,proxyquire
, etc.? Merci.J'ai également écrit un module pour accomplir cela, il s'appelle rewire . Utilisez simplement
npm install rewire
et ensuite:J'ai été inspiré par l'injecteur de Nathan MacInnes mais j'ai utilisé une approche différente. Je n'utilise pas
vm
pour évaluer le module de test, en fait j'utilise les propres exigences du nœud. De cette façon, votre module se comporte exactement comme l'utilisationrequire()
(sauf vos modifications). Le débogage est également entièrement pris en charge.la source
J'ai construit Electrolyte dans ce but. Les autres solutions d'injection de dépendance là-bas étaient trop envahissantes à mon goût, et jouer avec le monde
require
est un grief particulier à moi.Electrolyte comprend des modules, en particulier ceux qui exportent une fonction de "configuration" comme vous le voyez dans le middleware Connect / Express. Essentiellement, ces types de modules ne sont que des usines pour certains objets qu'ils retournent.
Par exemple, un module qui crée une connexion à une base de données:
Ce que vous voyez en bas sont des annotations , un bit supplémentaire de métadonnées qu'Electrolyte utilise pour instancier et injecter des dépendances, reliant automatiquement les composants de votre application.
Pour créer une connexion à une base de données:
L'électrolyte traverse de manière transitoire les
@require
dépendances 'd et injecte des instances comme arguments à la fonction exportée.La clé est que cela est peu invasif. Ce module est entièrement utilisable, indépendant de l'électrolyte lui-même. Cela signifie que vos tests unitaires peuvent tester uniquement le module testé , en passant des objets fictifs sans avoir besoin de dépendances supplémentaires pour recâbler les composants internes.
Lors de l'exécution de l'application complète, Electrolyte intervient au niveau inter-modules, câblant les choses ensemble sans avoir besoin de globales, de singletons ou de plomberie excessive.
la source
connect()
lancer? Même si je ne connais pas l'API MySql pour Node, je m'attends à ce que cet appel soit asynchrone, donc l'illustration n'est pas tout à fait claire.ioc.create
partir d'un test unitaire. Un test unitaire ne doit tester que le module testé et ne pas apporter d'autres dépendances, y compris l'électrolyte. En suivant ce conseil, vous feriezobjToTest = require('modulename')(mockObj1, mockObj2);
J'ai examiné cela moi-même. Je n'aime pas introduire des bibliothèques d'utilitaires de dépendance magique qui fournissent des mécanismes pour détourner les importations de mes modules. Au lieu de cela, je suis venu avec une «directive de conception» pour mon équipe pour indiquer de manière plutôt explicite quelles dépendances peuvent être moquées en introduisant une exportation de fonction d'usine dans mes modules.
J'utilise largement les fonctionnalités d'ES6 pour les paramètres et la déstructuration afin d'éviter un passe-partout et de fournir un mécanisme de remplacement de dépendance nommé.
Voici un exemple:
Et voici un exemple de son utilisation
Excusez la syntaxe ES6 pour ceux qui ne la connaissent pas.
la source
J'ai récemment vérifié ce fil pour la même raison que l'OP - la plupart des bibliothèques que j'ai rencontrées réécrivent temporairement l'instruction require. J'ai eu des succès mitigés avec cette méthode, et j'ai donc fini par utiliser l'approche suivante.
Dans le cadre d'une application express - j'enveloppe app.js dans un fichier bootstrap.js:
Le mappage d'objet transmis au chargeur ressemble à ceci:
Alors, plutôt que d'appeler directement, il faut ...
Si aucun alias n'est situé dans le chargeur - il sera par défaut simplement requis. Cela a deux avantages: je peux permuter dans n'importe quelle version de la classe, et cela supprime la nécessité d'utiliser des noms de chemin relatifs dans toute l'application (donc si j'ai besoin d'une bibliothèque personnalisée en dessous ou au-dessus du fichier actuel, je n'ai pas besoin de parcourir , et require mettra le module en cache avec la même clé). Cela me permet également de spécifier des simulations à tout moment dans l'application, plutôt que dans la suite de tests immédiate.
Je viens de publier un petit module npm pour plus de commodité:
https://npmjs.org/package/nodejs-simple-loader
la source
La réalité est que vous pouvez tester votre node.js sans conteneur IoC car JavaScript est un langage de programmation vraiment dynamique et vous pouvez modifier presque tout au moment de l'exécution.
Considérer ce qui suit:
Vous pouvez donc remplacer le couplage entre les composants lors de l'exécution. J'aime à penser que nous devrions viser à découpler nos modules JavaScript.
Le seul moyen de réaliser un véritable découplage consiste à supprimer la référence à
UserRepository
:Cela signifie que quelque part ailleurs, vous devrez faire la composition de l'objet:
J'aime l'idée de déléguer la composition de l'objet à un conteneur IoC. Vous pouvez en savoir plus sur cette idée dans l'article L'état actuel de l'inversion des dépendances en JavaScript . L'article tente de démystifier certains «mythes du conteneur JavaScript IoC»:
Si vous aimez également l'idée d'utiliser un conteneur IoC, vous pouvez jeter un œil à InversifyJS. La dernière version (2.0.0) prend en charge de nombreux cas d'utilisation:
Vous pouvez en savoir plus à ce sujet sur InversifyJS .
la source
Pour ES6, j'ai développé ce conteneur https://github.com/zazoomauro/node-dependency-injection
Ensuite, vous pouvez définir, par exemple, le choix du transport dans le conteneur:
Cette classe est maintenant beaucoup plus flexible car vous avez séparé le choix du transport hors de l'implémentation et dans le conteneur.
Maintenant que le service de messagerie est dans le conteneur, vous pouvez l'injecter en tant que dépendance d'autres classes. Si vous avez une classe NewsletterManager comme celle-ci:
Lors de la définition du service newsletter_manager, le service mailer n'existe pas encore. Utilisez la classe Reference pour indiquer au conteneur d'injecter le service de messagerie lorsqu'il initialise le gestionnaire de newsletter:
Vous pouvez également configurer le conteneur avec des fichiers de configuration comme les fichiers Yaml, Json ou JS
Le conteneur de services peut être compilé pour diverses raisons. Ces raisons incluent la vérification de tout problème potentiel tel que les références circulaires et l'amélioration de l'efficacité du conteneur.
la source
Cela dépend de la conception de votre application. Vous pouvez évidemment faire une injection de type Java où vous créez un objet d'une classe avec la dépendance passée dans le constructeur comme ceci.
Si vous ne faites pas de POO en javascript, vous pouvez créer une fonction init qui configure tout.
Cependant, il existe une autre approche que vous pouvez adopter, qui est plus courante dans un système basé sur des événements tel que node.js. Si vous pouvez modéliser votre application pour qu'elle agisse uniquement (la plupart du temps) sur les événements, il vous suffit de tout configurer (ce que je fais habituellement en appelant une fonction init) et d'émettre des événements à partir d'un stub. Cela rend les tests assez faciles et lisibles.
la source
J'ai toujours aimé la simplicité du concept IoC - "Vous n'avez rien à savoir sur l'environnement, vous serez appelé par quelqu'un en cas de besoin"
Mais toutes les implémentations IoC que j'ai vues ont fait exactement le contraire - elles encombrent le code avec encore plus de choses que sans lui. J'ai donc créé mon propre IoC qui fonctionne comme je le voudrais - il reste caché et invisible 90% du temps .
Il est utilisé dans le framework Web MonoJS http://monojs.org
C'est fait comme ceci - enregistrez le composant une fois dans la configuration.
Et utilisez-le partout
Vous pouvez voir le code de définition complet des composants (avec DB Connection et d'autres composants) ici https://github.com/sinizinairina/mono/blob/master/mono.coffee
C'est le seul endroit où vous devez dire à IoC quoi faire, après quoi tous ces composants seront créés et câblés automatiquement et vous n'aurez plus à voir le code spécifique IoC dans votre application.
L'IoC lui-même https://github.com/alexeypetrushin/miconjs
la source
Je pense que nous avons encore besoin de l'injection de dépendances dans Nodejs car cela desserre les dépendances entre les services et rend l'application plus claire.
Inspiré par Spring Framework , j'implémente également mon propre module pour prendre en charge l'injection de dépendances dans Nodejs. Mon module est également capable de détecter les
code changes
etauto reload
les services sans redémarrer votre application.Visitez mon projet à: Buncha - Conteneur IoC
Je vous remercie!
la source
Jetez un oeil à dips (Un cadre de gestion d'injection de dépendances et d'entités (fichiers) simple mais puissant pour Node.js)
https://github.com/devcrust/node-dips
la source
J'ai travaillé avec .Net, PHP et Java pendant longtemps, donc je voulais aussi avoir une injection de dépendance pratique dans NodeJS. Les gens ont dit que la DI intégrée dans NodeJS était suffisante car nous pouvons l'obtenir avec Module. Mais cela ne m'a pas bien satisfait. Je ne voulais pas garder un module plus qu'une classe. De plus, je voulais que la DI ait un support complet pour la gestion du cycle de vie du module (module singleton, module transitoire, etc.) mais avec le module Node, je devais écrire du code manuel très souvent. Enfin, je voulais faciliter le test unitaire. C'est pourquoi je me suis créé une injection de dépendance.
Si vous recherchez une DI, essayez-la. Il peut être trouvé ici: https://github.com/robo-creative/nodejs-robo-container . C'est entièrement documenté. Il aborde également certains problèmes courants avec DI et comment les résoudre de manière OOP. J'espère que ça aide.
la source
J'ai récemment créé une bibliothèque appelée circuitbox qui vous permet d'utiliser l'injection de dépendance avec node.js. Il fait une véritable injection de dépendance par rapport à la plupart des bibliothèques basées sur la recherche de dépendance que j'ai vues. Circuitbox prend également en charge les routines de création et d'initialisation asynchrones. Voici un exemple:
Supposons que le code suivant se trouve dans un fichier appelé consoleMessagePrinter.js
Supposons que ce qui suit se trouve dans le fichier main.js
Circuitbox vous permet de définir vos composants et de déclarer leurs dépendances en tant que modules. Une fois initialisé, il vous permet de récupérer un composant. Circuitbox injecte automatiquement tous les composants requis par le composant cible et vous les donne pour utilisation.
Le projet est en version alpha. Vos commentaires, idées et retours sont les bienvenus.
J'espère que ça aide!
la source
Je pense que d'autres articles ont fait un excellent travail dans l'argument de l'utilisation de DI. Pour moi, les raisons sont
Injectez des dépendances sans connaître leur chemin. Cela signifie que si vous changez l'emplacement d'un module sur le disque ou que vous le permutez avec un autre, vous n'avez pas besoin de toucher tous les fichiers qui en dépendent.
Il est beaucoup plus facile de se moquer des dépendances pour les tests sans avoir à remplacer la
require
fonction globale d'une manière qui fonctionne sans problème.Il vous aide à organiser et à raisonner votre application en tant que modules à couplage lâche.
Mais j'ai eu beaucoup de mal à trouver un framework DI que mon équipe et moi pouvons facilement adopter. J'ai donc récemment construit un framework appelé deppie basé sur ces fonctionnalités
require
modulesla source
Il devrait être flexible et simple comme ceci:
J'ai écrit un article sur l'injection de dépendances dans node.js.
J'espère que cela peut vous aider.
la source
Node.js nécessite autant de DI que n'importe quelle autre plateforme. Si vous construisez quelque chose de gros, DI vous permettra de vous moquer plus facilement des dépendances de votre code et de tester votre code à fond.
Vos modules de couche de base de données, par exemple, ne devraient pas seulement être requis au niveau de vos modules de code métier car, lors du test unitaire de ces modules de code métier, les daos se chargeront et se connecteront à la base de données.
Une solution serait de passer les dépendances en paramètres de module:
De cette façon, les dépendances peuvent être simulées facilement et naturellement et vous pouvez rester concentré sur le test de votre code, sans utiliser de bibliothèque tierce compliquée.
Il existe d'autres solutions (Broadway, architecte, etc.) qui peuvent vous aider. bien qu'ils puissent faire plus que ce que vous voulez ou utiliser plus d'encombrement.
la source
J'ai développé une bibliothèque qui gère l'injection de dépendances d'une manière simple, qui diminue le code passe-partout. Chaque module est défini par un nom unique et une fonction de contrôleur. Les paramètres du contrôleur reflètent les dépendances du module.
En savoir plus sur KlarkJS
Bref exemple:
myModuleName1
est le nom du module.$nodeModule1
est une bibliothèque externe denode_module
. Le nom se résout ennode-module1
. Le préfixe$
indique qu'il s'agit d'un module externe.myModuleName2
est le nom d'un module interne.myModuleName1
.la source
J'ai découvert cette question en répondant à un problème sur mon propre module DI demandant pourquoi on aurait besoin d'un système DI pour la programmation NodeJS.
La réponse tendait clairement à celles données dans ce fil: cela dépend. Il existe des compromis pour les deux approches et la lecture des réponses à cette question en donne une bonne forme.
Donc, la vraie réponse à cette question devrait être que dans certaines situations, vous utiliseriez un système DI, dans d'autres non.
Cela dit, ce que vous voulez en tant que développeur, c'est de ne pas vous répéter et de réutiliser vos services dans vos différentes applications.
Cela signifie que nous devons écrire des services prêts à être utilisés dans le système DI mais non liés aux bibliothèques DI. Pour moi, cela signifie que nous devrions écrire des services comme celui-ci:
De cette façon, votre service fonctionne, peu importe si vous l'utilisez avec ou sans outil DI.
la source
TypeDI est le plus doux de tous mentionné ici, regardez ce code dans TypeDI
Regardez aussi ce code:
la source