web-dev-qa-db-fra.com

Ai-je besoin d'une injection de dépendance dans NodeJS ou comment faire face à ...?

Je crée actuellement des projets expérimentaux avec nodejs. J'ai beaucoup programmé Java applications Web EE avec Spring et j'ai apprécié la facilité d'injection de dépendance.

Maintenant, je suis curieux de savoir comment faire une injection de dépendance avec le noeud? Ou en ai-je même besoin? Existe-t-il un concept de remplacement, car le style de programmation est différent?

Je parle de choses simples, comme le partage d'un objet de connexion à une base de données, mais je n'ai pas trouvé de solution qui me satisfasse.

197
Erik

En bref, vous n'avez pas besoin d'un conteneur d'injection de dépendance ou d'un service de localisation, 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 atteindre ce que vous voulez. Cela est très utile pour les tests.

Voici mon exemple très boiteux.

MyClass.js:

var fs = require('fs');

MyClass.prototype.errorFileExists = function(dir) {
    var dirsOrFiles = fs.readdirSync(dir);
    for (var d in dirsOrFiles) {
        if (d === 'error.txt') return true;
    }
    return false;
};

MyClass.test.js:

describe('MyClass', function(){
    it('should return an error if error.txt is found in the directory', function(done){
        var mc = new MyClass();
        assert(mc.errorFileExists('/tmp/mydir')); //true
    });
});

Remarquez comment MyClass dépend du module fs? Comme @ShatyemShekhar l'a mentionné, vous pouvez en effet faire l'injection de constructeur ou de propriété comme dans d'autres langages. Mais ce n'est pas nécessaire en Javascript.

Dans ce cas, vous pouvez faire deux choses.

Vous pouvez remplacer la méthode fs.readdirSync ou vous pouvez renvoyer un module entièrement différent lorsque vous appelez require.

Méthode 1:

var oldmethod = fs.readdirSync;
fs.readdirSync = function(dir) { 
    return ['somefile.txt', 'error.txt', 'anotherfile.txt']; 
};

*** PERFORM TEST ***
*** RESTORE METHOD AFTER TEST ****
fs.readddirSync = oldmethod;

Méthode 2:

var oldrequire = require
require = function(module) {
    if (module === 'fs') {
        return {
            readdirSync: function(dir) { 
                return ['somefile.txt', 'error.txt', 'anotherfile.txt']; 
            };
        };
    } else
        return oldrequire(module);

}

La clé est de tirer parti de la puissance de Node.js et de Javascript. Notez que je suis un gars de CoffeeScript, donc ma syntaxe JS est peut-être incorrecte quelque part. En outre, je ne dis pas que c'est la meilleure façon, mais c'est une façon. Les gourous de Javascript pourraient peut-être ajouter d'autres solutions.

Mise à jour:

Cela devrait répondre à votre question spécifique concernant les connexions à la base de données. Je créerais un module séparé pour que vous puissiez encapsuler la logique de connexion à votre base de données. Quelque chose comme ça:

MyDbConnection.js: (veillez à choisir un meilleur nom)

var db = require('whichever_db_vendor_i_use');

module.exports.fetchConnection() = function() {
    //logic to test connection

    //do I want to connection pool?

    //do I need only one connection throughout the lifecyle of my application?

    return db.createConnection(port, Host, databasename); //<--- values typically from a config file    
}

Ensuite, tout module nécessitant une connexion à une base de données n'inclura alors que votre module MyDbConnection.

SuperCoolWebApp.js:

var dbCon = require('./lib/mydbconnection'); //wherever the file is stored

//now do something with the connection
var connection = dbCon.fetchConnection(); //mydbconnection.js is responsible for pooling, reusing, whatever your app use case is

//come TEST time of SuperCoolWebApp, you can set the require or return whatever you want, or, like I said, use an actual connection to a TEST database. 

Ne suivez pas cet exemple textuellement. C’est un mauvais exemple en essayant de communiquer que vous utilisez le modèle module pour gérer vos dépendances. Espérons que cela aide un peu plus.

101
JP Richardson

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 coup d'œil à certains des conteneurs d'injection de dépendance disponibles aujourd'hui pour Node.js afin d'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 obtenir avec un conteneur Node.js DI, comparé à un simple require?

Avantages:

  • meilleure testabilité: les modules acceptent leurs dépendances en entrée
  • Inversion of Control: décidez comment câbler vos modules sans toucher au code principal de votre application.
  • un algorithme personnalisable pour la résolution de modules: les dépendances ont des identifiants "virtuels", ils ne sont généralement pas liés à un chemin sur le système de fichiers.
  • Meilleure extensibilité: activée par l'IoC et les identifiants "virtuels".
  • Autres choses fantaisistes possibles:
    • Initialisation Async
    • Gestion du cycle de vie des modules
    • Extensibilité du conteneur DI lui-même
    • Peut facilement implémenter des abstractions de niveau supérieur (par exemple, AOP)

Inconvénients:

  • "Expérience" différente de Node.js: ne pas utiliser require a vraiment le sentiment que vous vous écartez de la façon dont vous pensez Node.
  • La relation entre une dépendance et sa mise en œuvre n'est pas toujours explicite. Une dépendance peut être résolue au moment de l'exécution et influencée par divers paramètres. Le code devient plus difficile à comprendre et à déboguer
  • Temps de démarrage plus lent
  • Maturité (pour le moment): aucune des solutions actuelles n’est vraiment très populaire à l’heure actuelle, donc pas beaucoup de tutoriels, pas d’écosystème, pas de test de bataille.
  • Certains conteneurs DI ne fonctionneront pas bien avec les bundles de modules comme Browserify et Webpack.

Comme pour tout ce qui concerne le développement logiciel, choisir entre DI et require dépend de vos exigences, de la complexité de votre système et de votre style de programmation.

67
Mario

Je sais que ce fil est assez ancien à ce stade-ci, mais je me suis dit que je voudrais en dire plus à ce sujet. TL; DR est qu’en raison de la nature dynamique et non typée de JavaScript, il est possible de faire beaucoup de choses sans recourir au modèle d’injection de dépendance (DI) ni à l’utilisation d’un framework DI. Cependant, lorsqu'une application devient de plus en plus vaste et complexe, DI peut certainement améliorer la maintenabilité de votre code.

DI en C #

Pour comprendre pourquoi la nécessité de DI n’est pas aussi importante en JavaScript, il est utile de consulter 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:

class Horn
{
    public void Honk()
    {
        Console.WriteLine("beep!");
    }
}

class Car
{
    private Horn horn;

    public Car()
    {
        this.horn = new Horn();
    }

    public void HonkHorn()
    {
        this.horn.Honk();
    }
}

class Program
{
    static void Main()
    {
        var car = new Car();
        car.HonkHorn();
    }
}

Il y a peu de problèmes à écrire le code de cette façon.

  1. La classe Car est étroitement liée à la mise en œuvre particulière du cornet dans la classe Horn. Si nous voulons changer le type de klaxon utilisé par la voiture, nous devons modifier la classe Car même si son utilisation du klaxon ne change pas. Cela rend également les tests difficiles, car nous ne pouvons pas tester la classe Car indépendamment de sa dépendance, la classe Horn.
  2. La classe Car est responsable du cycle de vie de la classe Horn. 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. La classe Car devra ê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 convient de se concentrer sur le fait d'être une voiture et non de créer des instances.
  3. Il n'y a aucun moyen de réutiliser les mêmes instances de dépendance. Encore une fois, ce n'est pas important dans cette application de jouet, mais considérons une connexion de base de données. Vous auriez généralement une seule instance partagée par votre application.

Voyons maintenant comment utiliser un modèle d’injection de dépendance.

interface IHorn
{
    void Honk();
}

class Horn : IHorn
{
    public void Honk()
    {
        Console.WriteLine("beep!");
    }
}

class Car
{
    private IHorn horn;

    public Car(IHorn horn)
    {
        this.horn = horn;
    }

    public void HonkHorn()
    {
        this.horn.Honk();
    }
}

class Program
{
    static void Main()
    {
        var horn = new Horn();
        var car = new Car(horn);
        car.HonkHorn();
    }
}

Nous avons fait deux choses clés ici. Premièrement, nous avons introduit une interface que notre classe Horn implémente. Cela nous permet de coder la classe Car à l'interface au lieu de l'implémentation particulière. Maintenant, le code peut prendre tout ce qui implémente IHorn. Deuxièmement, nous avons retiré l'instanciation de klaxon de Car et l'avons passée à la place. Cela résout les problèmes ci-dessus et laisse à la fonction principale de l'application la gestion des instances spécifiques et de leurs cycles de vie.

Cela signifie que l'on pourrait introduire un nouveau type de klaxon que la voiture peut utiliser sans toucher à la classe Car:

class FrenchHorn : IHorn
{
    public void Honk()
    {
        Console.WriteLine("le beep!");
    }
}

Le principal pourrait simplement injecter une instance de la classe FrenchHorn. Cela simplifie également considérablement les tests. Vous pouvez créer une classe MockHorn à injecter dans le constructeur Car pour vous assurer de tester uniquement la classe Car de manière isolée.

L'exemple ci-dessus montre l'injection de dépendance manuelle. Typiquement, DI est réalisé avec un framework (par exemple, nité ou Ninject dans le monde C #). Ces frameworks effectuent tout le câblage de dépendance pour vous en parcourant votre graphique de dépendance et en créant des instances si nécessaire.

La manière standard Node.js

Examinons maintenant le même exemple dans Node.js. Nous diviserions probablement notre code en 3 modules:

// horn.js
module.exports = {
    honk: function () {
        console.log("beep!");
    }
};

// car.js
var horn = require("./horn");
module.exports = {
    honkHorn: function () {
        horn.honk();
    }
};

// index.js
var car = require("./car");
car.honkHorn();

Comme 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 module car tentera simplement d'appeler la méthode honk quel que soit le nombre d'exportations du module horn.

De plus, étant donné que require de Node met tout en cache, les modules sont essentiellement des singletons stockés dans un conteneur. Tout autre module effectuant une require sur le module horn obtiendra exactement la même instance. Cela facilite le partage d'objets singleton, tels que les connexions de base de données.

Maintenant, il reste le problème que le module car est chargé d'extraire sa propre dépendance horn. Si vous souhaitez que la voiture utilise un module différent pour son klaxon, vous devez modifier l'instruction require dans le module car. Ce n'est pas une chose très commune à faire, mais cela pose des problèmes avec les tests.

La façon habituelle de traiter le problème de test est avec proxyquire . En raison de la nature dynamique de JavaScript, proxyquire intercepte les appels à requérir et retourne à la place les stubs/mocks que vous fournissez.

var proxyquire = require('proxyquire');
var hornStub = {
    honk: function () {
        console.log("test beep!");
    }
};

var car = proxyquire('./car', { './horn': hornStub });

// Now make test assertions on car...

C'est plus que suffisant pour la plupart des applications. Si cela fonctionne pour votre application, alors allez-y. Toutefois, compte tenu de la taille et de la complexité croissantes des applications, la maintenance d'un code de ce type devient plus difficile.

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 à l'aide du modèle d'injection de dépendance. Dans ce modèle, chaque module exporte une fonction fabrique (ou un constructeur de classe).

// horn.js
module.exports = function () {
    return {
        honk: function () {
            console.log("beep!");
        }
    };
};

// car.js
module.exports = function (horn) {
    return {
        honkHorn: function () {
            horn.honk();
        }
    };
};

// index.js
var horn = require("./horn")();
var car = require("./car")(horn);
car.honkHorn();

Ceci est très similaire à la méthode C # précédemment dans la mesure où le module index.js est responsable des cycles de vie et du câblage, par exemple. Le test unitaire est assez simple, vous pouvez simplement passer des fonctions/moches aux fonctions. Encore une fois, si cela est suffisant pour votre application, allez-y.

Cadre DI bolus

Contrairement à C #, il n'y a pas de framework DI standard établi pour vous aider dans votre gestion de dépendance. Le registre npm comporte un certain nombre de cadres, mais aucun n’a été adopté à grande échelle. Beaucoup de ces options ont déjà été citées dans les autres réponses.

Je n'étais pas particulièrement satisfait des options disponibles et j'ai donc écrit le mien bolus . Bolus est conçu pour fonctionner avec du code écrit dans le style DI ci-dessus et tente d’être très SEC et très simple. En utilisant exactement les mêmes modules car.js et horn.js ci-dessus, vous pouvez réécrire le module index.js avec le bolus suivant:

// index.js
var Injector = require("bolus");
var injector = new Injector();
injector.registerPath("**/*.js");

var car = injector.resolve("car");
car.honkHorn();

L'idée de base est que vous créez un injecteur. Vous enregistrez tous vos modules dans l'injecteur. Ensuite, vous résolvez simplement ce dont vous avez besoin. Bolus parcourt le graphique des dépendances et crée et injecte des dépendances en fonction des besoins. Vous ne faites pas beaucoup d'économies dans un exemple de jouet comme celui-ci, mais dans les grandes applications avec des arbres de dépendances compliqués, les économies sont énormes.

Bolus prend en charge de nombreuses fonctionnalités intéressantes, telles que les dépendances facultatives et les tests globaux, mais il existe deux avantages clés par rapport à l’approche standard Node.js. 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 enregistre des objets utiles dessus. Ensuite, vos applications spécifiques peuvent ajouter, remplacer et résoudre à leur guise, de la manière suivante: AngularJS's injector fonctionne. Deuxièmement, vous pouvez utiliser bolus pour gérer divers contextes de dépendances. Par exemple, vous pouvez utiliser un middleware pour créer un injecteur enfant par requête, enregistrer l'ID utilisateur, l'ID de session, le consignateur, etc. sur l'injecteur, ainsi que tous 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 requête et évite de devoir transmettre l'enregistreur, etc. à chaque appel de fonction de module.

44
Dave Johnson

J'ai également écrit un module pour accomplir cela, il s'appelle rewire . Il suffit d'utiliser npm install rewire puis:

var rewire = require("rewire"),
    myModule = rewire("./path/to/myModule.js"); // exactly like require()

// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123


// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
    readFile: function (path, encoding, cb) {
        cb(null, "Success!");
    }
});
myModule.readSomethingFromFileSystem(function (err, data) {
    console.log(data); // = Success!
});

J'ai été inspiré par l'injectr 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 besoins du noeud. Ainsi, votre module se comporte exactement comme si vous utilisiez require() (à l'exception de vos modifications). Le débogage est également entièrement pris en charge.

37
Johannes Ewald

J'ai construit électrolyte pour cela. Les autres solutions d’injection de dépendance étaient trop invasives à mon goût, et jouer avec la solution globale require est un de mes griefs particuliers.

Electrolyte englobe les 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 fabriques pour certains objets qu’ils renvoient.

Par exemple, un module qui crée une connexion à une base de données:

var mysql = require('mysql');

exports = module.exports = function(settings) {
  var connection = mysql.createConnection({
    Host: settings.dbHost,
    port: settings.dbPort
  });

  connection.connect(function(err) {
    if (err) { throw err; }
  });

  return connection;
}

exports['@singleton'] = true;
exports['@require'] = [ 'settings' ];

En bas, vous voyez des annotations , un petit supplément 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:

var db = electrolyte.create('database');

L'électrolyte traverse de manière transitoire les dépendances @require 'd et injecte des instances en tant qu'arguments de la fonction exportée.

La clé est que ceci est minimalement invasif. Ce module est complètement utilisable, indépendamment de Electrolyte lui-même. Cela signifie que vos tests unitaires peuvent tester uniquement le module testé , en transmettant des objets fictifs sans nécessiter de dépendances supplémentaires pour recâbler les internes.

Lors de l'exécution de l'application complète, Electrolyte intervient au niveau inter-module, en connectant les éléments sans qu'il soit nécessaire de recourir à des globaux, à des singletons ou à une plomberie excessive.

17
Jared Hanson

J'ai examiné cela moi-même. Je n'aime pas introduire des bibliothèques utils de dépendance magique qui fournissent des mécanismes pour détourner mes importations de modules. Au lieu de cela, je suis arrivé à une "directive de conception" pour que mon équipe indique de manière assez explicite quelles dépendances peuvent être simulées en introduisant une exportation de fonctions d'usine dans mes modules.

J'utilise énormément les fonctionnalités de l'ES6 pour les paramètres et la déstructuration afin d'éviter certaines erreurs et de fournir un mécanisme de substitution de dépendance nommé.

Voici un exemple:

import foo from './utils/foo';
import bob from './utils/bob';

// We export a factory which accepts our dependencies.
export const factory = (dependencies = {}) => {
  const {
    // The 'bob' dependency.  We default to the standard 'bob' imp if not provided.
    $bob = bob, 
    // Instead of exposing the whole 'foo' api, we only provide a mechanism
    // with which to override the specific part of foo we care about.
    $doSomething = foo.doSomething // defaults to standard imp if none provided.
  } = dependencies;  

  return function bar() {
    return $bob($doSomething());
  }
}

// The default implementation, which would end up using default deps.
export default factory();

Et voici un exemple d'utilisation

import { factory } from './bar';

const underTest = factory({ $bob: () => 'BOB!' }); // only override bob!
const result = underTest();

Excusez la syntaxe ES6 pour ceux qui ne le connaissent pas.

9
ctrlplusb

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 résultats mitigés avec cette méthode et j'ai donc utilisé l'approche suivante.

Dans le contexte d'une application express, j'emballe app.js dans un fichier bootstrap.js:

var path = require('path');
var myapp = require('./app.js');

var loader = require('./server/services/loader.js');

// give the loader the root directory
// and an object mapping module names 
// to paths relative to that root
loader.init(path.normalize(__dirname), require('./server/config/loader.js')); 

myapp.start();

La mappe d'objets transmise au chargeur ressemble à ceci:

// live loader config
module.exports = {
    'dataBaseService': '/lib/dataBaseService.js'
}

// test loader config
module.exports = {
    'dataBaseService': '/mocks/dataBaseService.js'
    'otherService' : {other: 'service'} // takes objects too...
};

Ensuite, plutôt que d'appeler directement, il faut ...

var myDatabaseService = loader.load('dataBaseService');

Si aucun alias ne se trouve dans le chargeur, il utilisera simplement une valeur normale par défaut. Cela présente deux avantages: je peux permuter dans n’importe quelle version de la classe et éviter 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 vont mettre le module en cache contre la même clé). Cela me permet également de spécifier des simulacres à 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

5
sunwukung

La réalité est que vous pouvez tester votre node.js sans le conteneur IoC car JavaScript est un langage de programmation très dynamique et vous pouvez modifier presque tout au moment de l'exécution.

Considérer ce qui suit:

import UserRepository from "./dal/user_repository";

class UserController {
    constructor() {
        this._repository = new UserRepository();
    }
    getUsers() {
        this._repository.getAll();
    }
}

export default UserController;

Ainsi, vous pouvez remplacer le couplage entre les composants au moment 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 à la variable UserRepository:

class UserController {
    constructor(userRepository) {
        this._repository = userRepository;
    }
    getUsers() {
        this._repository.getAll();
    }
}

export default UserController;

Cela signifie que vous devrez faire la composition d'objet ailleurs:

import UserRepository from "./dal/user_repository";
import UserController from "./dal/user_controller";

export default new UserController(new UserRepository());

J'aime l'idée de déléguer la composition d'objet à un conteneur IoC. Vous pouvez en savoir plus sur cette idée dans l'article état actuel de l'inversion de dépendance en JavaScript . L'article tente de démystifier certains "mythes sur les conteneurs JavaScript IoC":

Mythe 1: Il n'y a pas de place pour les conteneurs IoC en JavaScript

Mythe 2: Nous n’avons pas besoin des conteneurs IoC, nous avons déjà des chargeurs de modules!

Mythe 3: Inversion des dépendances === injecter des dépendances

Si vous aimez également l’idée d’utiliser un conteneur IoC, vous pouvez jeter un coup d’œil à InversifyJS. La dernière version (2.0.0) prend en charge de nombreux cas d'utilisation:

  • Modules du noyau
  • Middleware du noyau
  • Utiliser des classes, des littéraux de chaîne ou des symboles comme identificateurs de dépendance
  • Injection de valeurs constantes
  • Injection de constructeurs de classe
  • Injection d'usines
  • Usine automatique
  • Injection de fournisseurs (async factory)
  • Gestionnaires d'activation (utilisés pour injecter des proxies)
  • Multi injections
  • Liaisons étiquetées
  • Décorateurs de tags personnalisés
  • Liaisons nommées
  • Liaisons contextuelles
  • Exceptions amicales (par exemple, dépendances circulaires)

Vous pouvez en apprendre plus à ce sujet sur InversifyJS .

3
Remo H. Jansen

Je pense que nous avons toujours besoin d'injection de dépendance dans Nodejs, car cela permet de réduire les dépendances entre les services et de rendre l'application plus claire.

Inspiré par Spring Framework , j'ai également implémenté mon propre module pour prendre en charge l'injection de dépendances dans Nodejs. Mon module est également capable de détecter les services code changes et auto reload sans redémarrer votre application.

Visitez mon projet à: Buncha - IoC container

Je vous remercie!

2
Tho

J'ai toujours aimé la simplicité du concept IoC - "Vous n'avez pas besoin de connaître l'environnement, vous serez appelé par quelqu'un en cas de besoin"

Mais toutes les implémentations IoC que j'ai vues faisaient exactement le contraire: elles encombrent le code avec encore plus de choses que sans. Donc, j'ai 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

Je parle de choses simples, comme le partage d'un objet de connexion à une base de données, mais je n'ai pas trouvé de solution qui me satisfasse.

C'est fait comme ça - inscrivez le composant une fois dans la configuration.

app.register 'db', -> 
  require('mongodb').connect config.dbPath

Et utilisez-le n'importe où

app.db.findSomething()

Vous pouvez voir le code de définition complet du composant (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

2
Alexey Petrushin

Pour ES6, j'ai développé ce conteneur https://github.com/zazoomauro/node-dependency-injection

import {ContainerBuilder} from 'node-dependency-injection'

let container = new ContainerBuilder()
container.register('mailer', 'Mailer')

Ensuite, vous pouvez définir, par exemple, le choix du transport dans le conteneur:

import {ContainerBuilder} from 'node-dependency-injection'

let container = new ContainerBuilder()
container
  .register('mailer', 'Mailer')
  .addArgument('sendmail')

Cette classe est maintenant beaucoup plus flexible car vous avez séparé le choix du transport en dehors de la mise en œuvre 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 un cours NewsletterManager comme celui-ci:

class NewsletterManager {
    construct (mailer, fs) {
        this._mailer = mailer
        this._fs = fs
    }
}

export default NewsletterManager

Lors de la définition du service newsletter_manager, le service de messagerie 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:

import {ContainerBuilder, Reference, PackageReference} from 'node-dependency-injection'
import Mailer from './Mailer'
import NewsletterManager from './NewsletterManager'

let container = new ContainerBuilder()

container
  .register('mailer', Mailer)
  .addArgument('sendmail')

container
  .register('newsletter_manager', NewsletterManager)
  .addArgument(new Reference('mailer'))
  .addArgument(new PackageReference('fs-extra'))

Vous pouvez également configurer le conteneur avec des fichiers de configuration tels que les fichiers Yaml, Json ou JS.

Le conteneur de service peut être compilé pour diverses raisons. Ces raisons incluent la recherche de problèmes potentiels tels que les références circulaires et l’amélioration de l’efficacité du conteneur.

container.compile()
2
Mauro

Cela dépend de la conception de votre application. Vous pouvez évidemment faire un Java comme une injection où vous créez un objet d'une classe avec la dépendance passée dans le constructeur comme ceci.

function Cache(store) {
   this._store = store;
}

var cache = new Cache(mysqlStore);

Si vous ne faites pas OOP en javascript, vous pouvez créer une fonction init qui configure tout.

Cependant, il existe une autre approche que vous pouvez adopter et qui est plus courante dans un système basé sur des événements tel que node.js. Si vous ne pouvez modéliser votre application que pour (la plupart du temps) agir 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.

1
Satyam Shekhar

Le di.js de Google fonctionne sur nodejs (+ navigateur) (+ ES6)

1
Amit Portnoy

Jetez un coup d'œil à dips (Un cadre d'injection de dépendance simple mais puissant et un cadre de gestion d'entités (fichiers) pour Node.js)

https://github.com/devcrust/node-dips

1
Mario

J'ai récemment créé une bibliothèque appelée circuitbox qui vous permet d'utiliser dépendance-injection avec node.js. Il effectue une véritable injection de dépendance par rapport à beaucoup 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. Ci-dessous un exemple:

Supposons que le code suivant se trouve dans un fichier appelé consoleMessagePrinter.js

'use strict';

// Our console message printer
// deps is injected by circuitbox with the dependencies
function ConsoleMessagePrinter(deps) {
  return {
    print: function () {
      console.log(deps.messageSource.message());
    }
  };
}

module.exports = ConsoleMessagePrinter;

Supposons que ce qui suit se trouve dans le fichier main.js

'use strict';

// our simple message source
// deps is injected by circuitbox with the dependencies
var simpleMessageSource = function (deps) {
  return {
    message: function () {
      return deps.message;
    }
  };
};

// require circuitbox
var circuitbox = require('../lib');

// create a circuitbox
circuitbox.create({
  modules: [
    function (registry) {
      // the message to be used
      registry.for('message').use('This is the message');

      // define the message source
      registry.for('messageSource').use(simpleMessageSource)
        .dependsOn('message');

      // define the message printer - does a module.require internally
      registry.for('messagePrinter').requires('./consoleMessagePrinter')
        .dependsOn('messageSource');
    }
  ]
}).done(function (cbx) {

  // get the message printer and print a message
  cbx.get('messagePrinter').done(function (printer) {
    printer.print();
  }, function (err) {
    console.log('Could not recieve a printer');
    return;
  });

}, function (err) {
  console.log('Could not create circuitbox');
});

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 fournit.

Le projet est en version alpha. Vos commentaires, idées et commentaires sont les bienvenus.

J'espère que ça aide!

0
oddjobsman

Il devrait être flexible et simple comme ceci:

var MyClass1 = function () {}
var MyClass2 = function (myService1) {
    // myService1.should.be.instanceof(MyClass1); 
}


container.register('myService1', MyClass1);
container.register('myService2', MyClass2, ['myService1']);

J'ai écrit un article sur l'injection de dépendance dans node.js.

J'espère que cela peut vous aider avec ça.

0
slava

Je pense que d’autres publications ont fait un excellent travail dans l’argument en faveur de l’utilisation de DI. Pour moi les raisons sont

  1. Injectez des dépendances sans connaître leurs chemins. Cela signifie que si vous modifiez l'emplacement d'un module sur le disque ou échangez-le avec un autre, vous n'avez pas besoin de toucher tous les fichiers qui en dépendent.

  2. Il est beaucoup plus facile de simuler des dépendances lors de tests sans avoir à redéfinir la fonction globale require de manière à fonctionner sans problèmes.

  3. Il vous aide à organiser et à raisonner au sujet de votre application sous forme de modules à couplage lâche.

Mais j’ai eu beaucoup de mal à trouver un cadre d’ID que mon équipe et moi-même pouvons facilement adopter. Donc, j'ai récemment construit un framework appelé deppie basé sur ces fonctionnalités

  • API minimale pouvant être apprise en quelques minutes
  • Aucun code/config/annotations supplémentaire requis
  • Mappage direct un à un sur les modules require
  • Peut être adopté partiellement pour fonctionner avec le code existant
0
Gaafar

J'ai longtemps travaillé avec .Net, PHP et Java. Je souhaitais donc disposer d'une injection de dépendance pratique dans NodeJS. Les gens ont dit que l’ID intégré à NodeJS était suffisant car nous pouvons l’obtenir avec Module. Mais cela ne me satisfaisait pas bien. Je voulais garder un module pas plus qu'une classe. De plus, je souhaitais que l'ID prenne en charge de manière complète la gestion du cycle de vie des modules (module singleton, module transitoire, etc.), mais avec le module Node, je devais écrire très souvent du code manuel. Enfin, je voulais faciliter les tests unitaires. C'est pourquoi j'ai créé une injection de dépendance pour moi-même.

Si vous cherchez une DI, essayez-la. Vous pouvez le trouver ici: https://github.com/robo-creative/nodejs-robo-container . C'est complètement 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.

0
Robo

J'ai découvert cette question alors que répondant à un problème sur mon propre module DI demandant pourquoi on aurait jamais besoin d'un système DI pour la programmation NodeJS.

La réponse tendait clairement à ceux donnés dans ce fil: ça dépend. Il existe des compromis entre 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 d’ID, dans d’autres pas.

Cela dit, ce que vous voulez, en tant que développeur, est de ne pas vous répéter et de réutiliser vos services dans vos diverses applications.

Cela signifie que nous devrions é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:

module.exports = initDBService;

// Tells any DI lib what it expects to find in it context object
// The $inject prop is the de facto standard for DI imo 
initDBService.$inject = ['ENV'];

// Note the context object, imo, a DI tool should bring
// services in a single context object
function initDBService({ ENV }) {
/// actual service code
}

Ainsi, votre service ne fonctionnera pas si vous l'utilisez avec ou sans outil DI.

0
nfroidure

J'ai développé une bibliothèque qui gère l'injection de dépendance avec un moyen simple, qui diminue le code standard. Chaque module est défini par un nom unique et une fonction de contrôleur. Les paramètres de l'automate reflètent les dépendances du module.

En savoir plus sur KlarkJS

Bref exemple:

KlarkModule(module, 'myModuleName1', function($nodeModule1, myModuleName2) {
    return {
        log: function() { console.log('Hello from module myModuleName1') }
    };
});
  • myModuleName1 est le nom du module.
  • $nodeModule1 est une bibliothèque externe de node_module. Le nom se décompose en node-module1. Le préfixe $ indique qu'il s'agit d'un module externe.
  • myModuleName2 est le nom d'un module interne.
  • La valeur de retour du contrôleur est utilisée à partir des autres modules internes, lorsqu'ils définissent le paramètre myModuleName1.
0
Apostolidis

Node.js requiert DI autant que toute autre plate-forme. Si vous construisez quelque chose de grand, DI facilitera la simulation des dépendances de votre code et le testera à fond.

Par exemple, vos modules de couche de base de données ne doivent pas uniquement être requis pour vos modules de code métier car, lorsque l'unité teste ces modules de code métier, les daos se chargent et se connectent à la base de données.

Une solution serait de passer les dépendances en tant que paramètres de module:

module.exports = function (dep1, dep2) {
// private methods

   return {
    // public methods
       test: function(){...}
   }
}

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 délicate.

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 de fouillis.

0
user2468170