it-swarm.com.de

Benötige ich eine Abhängigkeitsinjektion in NodeJS oder wie gehe ich damit um?

Ich erstelle gerade einige experimentelle Projekte mit nodejs. Ich habe viele Java EE-Webanwendungen mit Spring programmiert und die Leichtigkeit der Abhängigkeitsinjektion dort geschätzt.

Jetzt bin ich neugierig: Wie mache ich eine Abhängigkeitsinjektion mit Node? Oder: Brauche ich das überhaupt? Gibt es ein Ersatzkonzept, weil der Programmierstil anders ist?

Ich spreche bisher von einfachen Dingen wie der Freigabe eines Datenbankverbindungsobjekts, habe jedoch keine Lösung gefunden, die mich zufriedenstellt.

197
Erik

Kurz gesagt, Sie benötigen keinen Abhängigkeitsinjektionscontainer oder Service-Locater wie in C #/Java. Da Node.js module pattern Verwendet, ist es nicht erforderlich, Konstruktoren oder Eigenschaften einzufügen. Obwohl du es immer noch kannst.

Das Tolle an JS ist, dass Sie so gut wie alles modifizieren können, um das zu erreichen, was Sie wollen. Dies ist praktisch, wenn es um das Testen geht.

Siehe, mein sehr lahmes erfundenes Beispiel.

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
    });
});

Beachten Sie, wie MyClass vom fs Modul abhängt. Wie @ShatyemShekhar bereits erwähnt hat, können Sie Konstruktoren oder Eigenschaften wie in anderen Sprachen einfügen. In Javascript ist dies jedoch nicht erforderlich.

In diesem Fall können Sie zwei Dinge tun.

Sie können die Methode fs.readdirSync Löschen oder ein ganz anderes Modul zurückgeben, wenn Sie require aufrufen.

Methode 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;

Methode 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);

}

Der Schlüssel ist, die Leistung von Node.js und Javascript zu nutzen. Hinweis: Ich bin ein CoffeeScript-Typ, daher ist meine JS-Syntax möglicherweise irgendwo falsch. Ich sage auch nicht, dass dies der beste Weg ist, aber es ist ein Weg. Javascript-Gurus sind möglicherweise in der Lage, mit anderen Lösungen mitzuhalten.

pdate:

Dies sollte sich mit Ihrer spezifischen Frage zu Datenbankverbindungen befassen. Ich würde ein separates Modul für Ihre erstellen, um Ihre Datenbankverbindungslogik zu kapseln. Etwas wie das:

MyDbConnection.js: (Achten Sie darauf, einen besseren Namen zu wählen)

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    
}

Dann würde jedes Modul, das eine Datenbankverbindung benötigt, nur Ihr MyDbConnection Modul enthalten.

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. 

Folgen Sie diesem Beispiel nicht wörtlich. Es ist ein lahmes Beispiel für den Versuch zu kommunizieren, dass Sie das Muster module nutzen, um Ihre Abhängigkeiten zu verwalten. Hoffentlich hilft das ein bisschen mehr.

101
JP Richardson

require ist die Methode zum Verwalten von Abhängigkeiten in Node.js. Sie ist zwar intuitiv und effektiv, hat aber auch ihre Grenzen.

Mein Rat ist, einen Blick auf einige der heute für Node.js verfügbaren Dependency Injection-Container zu werfen, um eine Vorstellung davon zu bekommen, welche Vor-/Nachteile sie haben. Einige von ihnen sind:

Nur um ein paar zu nennen.

Die eigentliche Frage ist nun, was Sie mit einem DI-Container von Node.js im Vergleich zu einem einfachen require erreichen können.

Vorteile:

  • bessere Testbarkeit: Module akzeptieren ihre Abhängigkeiten als Eingabe
  • Inversion of Control: Entscheiden Sie, wie Sie Ihre Module verdrahten möchten, ohne den Hauptcode Ihrer Anwendung zu berühren.
  • ein anpassbarer Algorithmus zum Auflösen von Modulen: Abhängigkeiten haben "virtuelle" Bezeichner, normalerweise sind sie nicht an einen Pfad im Dateisystem gebunden.
  • Bessere Erweiterbarkeit: Ermöglicht durch IoC und "virtuelle" Kennungen.
  • Andere ausgefallene Sachen möglich:
    • Asynchrone Initialisierung
    • Modul-Lebenszyklus-Management
    • Erweiterbarkeit des DI-Containers selbst
    • Kann einfach Abstraktionen höherer Ebenen (z. B. AOP) implementieren

Nachteile:

  • Anders als bei Node.js "Erfahrung": Wenn Sie require nicht verwenden, haben Sie definitiv das Gefühl, von der Node) Denkweise abzuweichen.
  • Die Beziehung zwischen einer Abhängigkeit und ihrer Implementierung ist nicht immer eindeutig. Eine Abhängigkeit kann zur Laufzeit aufgelöst und durch verschiedene Parameter beeinflusst werden. Der Code wird schwieriger zu verstehen und zu debuggen
  • Langsamere Startzeit
  • Maturity (im Moment): Keine der aktuellen Lösungen ist im Moment wirklich populär, also nicht so viele Tutorials, kein Ökosystem, nicht kampferprobt.
  • Einige DI-Container eignen sich nicht für Modulbündler wie Browserify und Webpack.

Wie bei allem, was mit der Softwareentwicklung zu tun hat, hängt die Wahl zwischen DI und require von Ihren Anforderungen, Ihrer Systemkomplexität und Ihrem Programmierstil ab.

67
Mario

Ich weiß, dass dieser Thread zu diesem Zeitpunkt ziemlich alt ist, aber ich dachte mir, ich würde mich daran halten. Das TL; DR ist, dass Sie aufgrund der untypisierten, dynamischen Natur von JavaScript tatsächlich eine ganze Menge tun können, ohne auf das Abhängigkeitsinjektionsmuster (DI) zurückzugreifen oder ein DI-Framework zu verwenden. Wenn eine Anwendung jedoch größer und komplexer wird, kann DI auf jeden Fall die Wartbarkeit Ihres Codes verbessern.

DI in C #

Um zu verstehen, warum DI in JavaScript nicht so wichtig ist, sollten Sie sich eine stark typisierte Sprache wie C # ansehen. (Entschuldigung an diejenigen, die C # nicht kennen, aber es sollte einfach genug sein zu folgen.) Sagen wir, wir haben eine App, die ein Auto und seine Hupe beschreibt. Sie würden zwei Klassen definieren:

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();
    }
}

Es gibt wenige Probleme beim Schreiben des Codes auf diese Weise.

  1. Die Klasse Car ist eng an die spezielle Implementierung des Horns in der Klasse Horn gekoppelt. Wenn wir den vom Auto verwendeten Hupentyp ändern möchten, müssen wir die Klasse Car ändern, auch wenn sich die Verwendung der Hupe nicht ändert. Dies erschwert auch das Testen, da wir die Klasse Car nicht unabhängig von ihrer Abhängigkeit, der Klasse Horn, testen können.
  2. Die Klasse Car ist für den Lebenszyklus der Klasse Horn verantwortlich. In einem einfachen Beispiel wie diesem ist dies kein großes Problem, aber in realen Anwendungen haben Abhängigkeiten Abhängigkeiten, die Abhängigkeiten usw. aufweisen. Die Klasse Car muss für die Erstellung des gesamten Baums ihrer Abhängigkeiten verantwortlich sein. Dies ist nicht nur kompliziert und sich wiederholend, sondern verletzt auch die "Einzelverantwortung" der Klasse. Es sollte sich darauf konzentrieren, ein Auto zu sein und keine Instanzen zu erzeugen.
  3. Es gibt keine Möglichkeit, dieselben Abhängigkeitsinstanzen wiederzuverwenden. Auch dies ist in dieser Spielzeuganwendung nicht wichtig, aber Sie sollten eine Datenbankverbindung in Betracht ziehen. Normalerweise verfügen Sie über eine einzelne Instanz, die in Ihrer Anwendung gemeinsam genutzt wird.

Lassen Sie uns dies nun umgestalten, um ein Abhängigkeitsinjektionsmuster zu verwenden.

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();
    }
}

Wir haben hier zwei wichtige Dinge getan. Zunächst haben wir eine Schnittstelle eingeführt, die von unserer Klasse Horn implementiert wird. Auf diese Weise können wir die Klasse Car anstelle der jeweiligen Implementierung für die Schnittstelle codieren. Jetzt kann der Code alles aufnehmen, was IHorn implementiert. Zweitens haben wir die Horninstanziierung aus Car herausgenommen und übergeben sie stattdessen. Dies behebt die oben genannten Probleme und überlässt es der Hauptfunktion der Anwendung, die spezifischen Instanzen und ihre Lebenszyklen zu verwalten.

Dies bedeutet, dass eine neue Art von Hupe für das Auto eingeführt werden könnte, ohne die Klasse Car zu berühren:

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

Der main könnte stattdessen einfach eine Instanz der Klasse FrenchHorn einspeisen. Dies vereinfacht auch das Testen erheblich. Sie können eine MockHorn -Klasse erstellen, die in den Car -Konstruktor eingefügt wird, um sicherzustellen, dass Sie nur die Car -Klasse isoliert testen.

Das obige Beispiel zeigt die manuelle Abhängigkeitsinjektion. Normalerweise erfolgt DI mit einem Framework (z. B. nity oder Ninject in der C # -Welt). Diese Frameworks erledigen die gesamte Abhängigkeitsverdrahtung für Sie, indem sie Ihr Abhängigkeitsdiagramm durchgehen und Instanzen nach Bedarf erstellen.

Der Standard Node.js Weg

Schauen wir uns nun dasselbe Beispiel in Node.js an. Wir würden unseren Code wahrscheinlich in drei Module aufteilen:

// 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();

Da JavaScript nicht typisiert ist, haben wir nicht die gleiche enge Kopplung wie zuvor. Schnittstellen sind nicht erforderlich (und existieren auch nicht), da das Modul car nur versucht, die Methode honk für alle Exporte des Moduls horn aufzurufen.

Da das require des Knotens alles zwischenspeichert, sind Module im Wesentlichen Singletons, die in einem Container gespeichert sind. Jedes andere Modul, das ein require für das horn Modul ausführt, erhält genau dieselbe Instanz. Dies macht das Teilen von Singleton-Objekten wie Datenbankverbindungen sehr einfach.

Jetzt gibt es immer noch das Problem, dass das Modul car für das Abrufen seiner eigenen Abhängigkeit horn verantwortlich ist. Wenn das Auto ein anderes Modul für die Hupe verwenden soll, müssen Sie die Anweisung require im Modul car ändern. Dies ist keine alltägliche Aufgabe, führt jedoch zu Problemen beim Testen.

Die übliche Art und Weise, wie Benutzer mit dem Testproblem umgehen, ist mit proxyquire . Aufgrund der Dynamik von JavaScript fängt Proxyquire Aufrufe ab und gibt stattdessen alle von Ihnen bereitgestellten Stubs/Mocks zurück.

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

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

// Now make test assertions on car...

Dies ist für die meisten Anwendungen mehr als ausreichend. Wenn es für Ihre App funktioniert, dann machen Sie es mit. Nach meiner Erfahrung mit immer größeren und komplexeren Anwendungen wird es jedoch schwieriger, Code wie diesen zu verwalten.

DI in JavaScript

Node.js ist sehr flexibel. Wenn Sie mit der obigen Methode nicht zufrieden sind, können Sie Ihre Module mit dem Abhängigkeitsinjektionsmuster schreiben. In diesem Muster exportiert jedes Modul eine Factory-Funktion (oder einen Klassenkonstruktor).

// 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();

Dies ist sehr ähnlich zu der C # -Methode, in der index.js Modul ist verantwortlich für zB Lebenszyklen und Verkabelung. Unit-Tests sind ganz einfach, da Sie nur Mocks/Stubs an die Funktionen übergeben können. Auch wenn dies für Ihre Anwendung gut genug ist, gehen Sie mit.

Bolus DI Framework

Im Gegensatz zu C # gibt es keine etablierten DI-Standardframeworks, die Sie beim Abhängigkeitsmanagement unterstützen. Es gibt eine Reihe von Frameworks in der npm-Registrierung, aber keines ist weit verbreitet. Viele dieser Optionen wurden bereits in den anderen Antworten genannt.

Ich war mit keiner der verfügbaren Optionen besonders zufrieden, also schrieb ich meine eigene mit dem Namen Bolus . Bolus wurde entwickelt, um mit Code zu arbeiten, der im obigen DI-Stil geschrieben wurde, und versucht, sehr TROCKEN und sehr einfach zu sein. Mit genau dem gleichen car.js und horn.js Module oben können Sie die index.js Modul mit Bolus als:

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

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

Die Grundidee ist, dass Sie einen Injektor erstellen. Sie registrieren alle Ihre Module im Injektor. Dann lösen Sie einfach, was Sie brauchen. Bolus durchläuft das Abhängigkeitsdiagramm und erstellt und injiziert nach Bedarf Abhängigkeiten. In einem Spielzeugbeispiel wie diesem sparen Sie nicht viel, aber in großen Anwendungen mit komplizierten Abhängigkeitsbäumen sind die Einsparungen enorm.

Bolus unterstützt eine Reihe von nützlichen Funktionen wie optionale Abhängigkeiten und Testglobals, aber es gibt zwei wichtige Vorteile, die ich im Vergleich zum Standardansatz von Node.js gesehen habe. Erstens, wenn Sie viele ähnliche Anwendungen haben, können Sie ein privates npm-Modul für Ihre Basis erstellen, das einen Injektor erstellt und nützliche Objekte darauf registriert. Dann können Ihre spezifischen Apps nach Bedarf hinzufügen, überschreiben und auflösen, ähnlich wie AngularJS's injector funktioniert. Zweitens können Sie mit dem Bolus verschiedene Abhängigkeitskontexte verwalten. Sie können beispielsweise Middleware verwenden, um auf Anforderung einen untergeordneten Injektor zu erstellen, die Benutzer-ID, die Sitzungs-ID, den Protokollierer usw. auf dem Injektor sowie die davon abhängigen Module zu registrieren. Lösen Sie dann die Anforderungen, die Sie für die Bearbeitung benötigen. Auf diese Weise erhalten Sie Instanzen Ihrer Module pro Anforderung und müssen den Logger usw. nicht an jeden Modulfunktionsaufruf weitergeben.

44
Dave Johnson

Ich habe auch ein Modul geschrieben, um dies zu erreichen. Es heißt rewire . Verwenden Sie einfach npm install rewire Und dann:

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!
});

Ich habe mich von Nathan MacInnes's injectr inspirieren lassen, aber einen anderen Ansatz gewählt. Ich benutze vm nicht, um das Testmodul zu evaluieren. Auf diese Weise verhält sich Ihr Modul genau wie bei der Verwendung von require() (mit Ausnahme Ihrer Änderungen). Auch das Debuggen wird voll unterstützt.

37
Johannes Ewald

Ich habe Elektrolyt für genau diesen Zweck gebaut. Die anderen Abhängigkeitsinjektionslösungen waren für meinen Geschmack zu invasiv, und das Durcheinander mit dem globalen require ist ein besonderes Problem von mir.

Electrolyte umfasst Module, insbesondere solche, die eine "Setup" -Funktion exportieren, wie Sie sie in der Connect/Express-Middleware sehen. Im Wesentlichen sind diese Modultypen nur Fabriken für ein Objekt, das sie zurückgeben.

Zum Beispiel ein Modul, das eine Datenbankverbindung erstellt:

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' ];

Was Sie unten sehen, sind Anmerkungen, zusätzliche Metadaten, die Electrolyte verwendet, um Abhängigkeiten zu instanziieren und einzufügen und die Komponenten Ihrer Anwendung automatisch miteinander zu verbinden.

So erstellen Sie eine Datenbankverbindung:

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

Elektrolyt durchquert transitiv das @require 'd Abhängigkeiten und fügt Instanzen als Argumente in die exportierte Funktion ein.

Der Schlüssel ist, dass dies minimal invasiv ist. Dieses Modul ist unabhängig von Elektrolyt selbst voll verwendbar. Das bedeutet, dass Ihre Komponententests nur das zu testende Modul testen können, indem Sie Scheinobjekte übergeben, ohne dass zusätzliche Abhängigkeiten erforderlich sind, um Interna neu zu verdrahten.

Wenn die vollständige Anwendung ausgeführt wird, greift Electrolyte auf der Ebene zwischen den Modulen ein und verdrahtet die Komponenten miteinander, ohne dass Globales, Singletons oder übermäßige Installationsarbeiten erforderlich sind.

17
Jared Hanson

Ich habe mich selbst darum gekümmert. Ich mag es nicht, Magic Dependency Utils-Bibliotheken einzuführen, die Mechanismen bereitstellen, um meine Modulimporte zu hijacken. Stattdessen habe ich eine "Gestaltungsrichtlinie" für mein Team ausgearbeitet, in der ausdrücklich dargelegt wird, welche Abhängigkeiten durch die Einführung eines Factory-Funktionsexports in meinen Modulen verspottet werden können.

Ich benutze ES6-Funktionen in großem Umfang für Parameter und Destrukturierung, um ein gewisses Maß an Boilerplate zu vermeiden und einen benannten Mechanismus zum Überschreiben von Abhängigkeiten bereitzustellen.

Hier ist ein Beispiel:

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();

Und hier ist ein Beispiel für die Verwendung

import { factory } from './bar';

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

Entschuldigen Sie die ES6-Syntax für diejenigen, die sie nicht kennen.

9
ctrlplusb

Ich habe diesen Thread vor kurzem aus dem gleichen Grund wie das OP überprüft - die meisten Bibliotheken, denen ich begegnet bin, haben die require-Anweisung vorübergehend umgeschrieben. Ich hatte gemischte Erfolge mit dieser Methode, und so folgte ich dem folgenden Ansatz.

Im Kontext einer Express-Anwendung - Ich wickle app.js in eine bootstrap.js-Datei ein:

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();

Die an den Loader übergebene Objektzuordnung sieht folgendermaßen aus:

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

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

Dann, anstatt direkt anrufen zu müssen ...

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

Befindet sich kein Alias ​​im Loader, wird dies standardmäßig benötigt. Dies hat zwei Vorteile: Ich kann jede Version der Klasse austauschen, und die Verwendung relativer Pfadnamen in der gesamten Anwendung ist nicht mehr erforderlich , und require zwischenspeichern das Modul unter demselben Schlüssel). Außerdem kann ich an jeder Stelle in der App Mocks angeben, anstatt in der unmittelbaren Testsuite.

Ich habe gerade ein kleines npm-Modul veröffentlicht:

https://npmjs.org/package/nodejs-simple-loader

5
sunwukung

Die Realität ist, dass Sie Ihre node.js ohne IoC-Container testen können, da JavaScript eine sehr dynamische Programmiersprache ist und Sie fast alles zur Laufzeit ändern können.

Folgendes berücksichtigen:

import UserRepository from "./dal/user_repository";

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

export default UserController;

So können Sie die Kopplung zwischen Komponenten zur Laufzeit außer Kraft setzen. Ich denke gerne, dass wir darauf abzielen sollten, unsere JavaScript-Module zu entkoppeln.

Die einzige Möglichkeit, eine echte Entkopplung zu erreichen, besteht darin, den Verweis auf UserRepository zu entfernen:

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

export default UserController;

Dies bedeutet, dass Sie die Objektzusammensetzung an einer anderen Stelle vornehmen müssen:

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

export default new UserController(new UserRepository());

Mir gefällt die Idee, die Objektzusammensetzung an einen IoC-Container zu delegieren. Weitere Informationen zu dieser Idee finden Sie im Artikel Der aktuelle Status der Abhängigkeitsinversion in JavaScript . Der Artikel versucht einige "JavaScript IoC Container Mythen" zu entlarven:

Mythos 1: In JavaScript ist kein Platz für IoC-Container

Mythos 2: Wir brauchen keine IoC-Container, wir haben bereits Modullader!

Mythos 3: Abhängigkeitsinversion === Abhängigkeiten injizieren

Wenn Ihnen auch die Idee gefällt, einen IoC-Container zu verwenden, können Sie sich InversifyJS ansehen. Die neueste Version (2.0.0) unterstützt viele Anwendungsfälle:

  • Kernel-Module
  • Kernel-Middleware
  • Verwenden Sie Klassen, Zeichenfolgenliterale oder Symbole als Abhängigkeitsbezeichner
  • Einspritzung konstanter Werte
  • Injection von Klassenkonstruktoren
  • Injektion von Fabriken
  • Auto-Fabrik
  • Injection von Providern (Async Factory)
  • Aktivierungshandler (zum Injizieren von Proxys)
  • Multi-Injektionen
  • Markierte Bindungen
  • Benutzerdefinierte Tag-Dekoratoren
  • Benannte Bindungen
  • Kontextbezogene Bindungen
  • Freundliche Ausnahmen (z. B. zirkuläre Abhängigkeiten)

Weitere Informationen finden Sie unter InversifyJS .

3
Remo H. Jansen

Ich denke, wir brauchen noch Dependency Injection in Nodejs, weil es die Abhängigkeiten zwischen Diensten löst und die Anwendung klarer macht.

Inspiriert von Spring Framework implementiere ich auch mein eigenes Modul, um die Abhängigkeitsinjektion in Nodejs zu unterstützen. Mein Modul kann auch die code changes und auto reload die Dienste ohne Neustart Ihrer Anwendung.

Besuchen Sie mein Projekt unter: Buncha - IoC Container

Vielen Dank!

2
Tho

Ich mochte immer die Einfachheit des IoC-Konzepts - "Sie müssen nichts über die Umgebung wissen, Sie werden bei Bedarf von jemandem angerufen"

Aber alle IoC-Implementierungen, die ich gesehen habe, haben genau das Gegenteil bewirkt - sie überladen den Code mit noch mehr Dingen als ohne. Also habe ich mein eigenes IoC erstellt, das so funktioniert, wie ich es gerne hätte - es bleibt 90% der Zeit verborgen und unsichtbar .

Es wird im MonoJS-Webframework verwendet http://monojs.org

Ich spreche bisher von einfachen Dingen wie der Freigabe eines Datenbankverbindungsobjekts, aber ich habe keine Lösung gefunden, die mich zufriedenstellt.

Es ist so gemacht - registriere die Komponente einmal in der Konfiguration.

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

Und überall einsetzen

app.db.findSomething()

Den vollständigen Code für die Komponentendefinition (mit DB Connection und anderen Komponenten) finden Sie hier https://github.com/sinizinairina/mono/blob/master/mono.coffee

Dies ist der einzige Ort, an dem Sie IoC mitteilen müssen, was zu tun ist. Danach werden alle diese Komponenten automatisch erstellt und verkabelt, und Sie müssen keinen IoC-spezifischen Code mehr in Ihrer Anwendung sehen.

Die IoC selbst https://github.com/alexeypetrushin/miconjs

2

Für ES6 habe ich diesen Container entwickelt https://github.com/zazoomauro/node-dependency-injection

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

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

Dann können Sie zum Beispiel die Wahl des Transports im Container festlegen:

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

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

Diese Klasse ist jetzt viel flexibler, da Sie die Auswahl des Transports von der Implementierung in den Container getrennt haben.

Jetzt, da sich der Mailer-Dienst im Container befindet, können Sie ihn als Abhängigkeit von anderen Klassen einfügen. Wenn Sie eine NewsletterManager-Klasse wie diese haben:

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

export default NewsletterManager

Beim Definieren des newsletter_manager-Dienstes ist der Mailer-Dienst noch nicht vorhanden. Verwenden Sie die Reference-Klasse, um den Container anzuweisen, den Mailer-Service einzuschleusen, wenn der Newsletter-Manager initialisiert wird:

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'))

Sie können den Container auch mit Konfigurationsdateien wie Yaml-, Json- oder JS-Dateien einrichten

Der Service Container kann aus verschiedenen Gründen kompiliert werden. Zu diesen Gründen gehört die Überprüfung auf potenzielle Probleme wie z. B. Zirkelverweise und die Verbesserung der Effizienz des Containers.

container.compile()
2
Mauro

Dies hängt vom Design Ihrer Anwendung ab. Sie können natürlich eine Java wie eine Injection ausführen, bei der Sie ein Objekt einer Klasse mit der im Konstruktor übergebenen Abhängigkeit wie folgt erstellen.

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

var cache = new Cache(mysqlStore);

Wenn Sie nicht OOP in Javascript tun, können Sie eine Init-Funktion erstellen, die alles einrichtet.

Es gibt jedoch einen anderen Ansatz, der in einem ereignisbasierten System wie node.js üblicher ist. Wenn Sie Ihre Anwendung so modellieren können, dass sie nur (meistens) auf Ereignisse einwirkt, müssen Sie lediglich alles einrichten (was ich normalerweise durch Aufrufen einer Init-Funktion mache) und Ereignisse von einem Stub ausgeben. Dies erleichtert das Testen und macht es lesbar.

1
Satyam Shekhar

Googles di.js funktioniert auf nodejs (+ browser) (+ ES6)

1
Amit Portnoy

Werfen Sie einen Blick auf Dips (Ein einfaches, aber leistungsstarkes Framework für die Verwaltung von Abhängigkeiten und Entitäten (Dateien) für Node.js)

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

1
Mario

Ich habe kürzlich eine Bibliothek namens circuitbox erstellt, mit der Sie die Abhängigkeitsinjektion mit node.js verwenden können. Es macht echte Abhängigkeitsinjektion im Vergleich zu vielen der abhängigkeitsbasierten Bibliotheken, die ich gesehen habe. Circuitbox unterstützt auch asynchrone Erstellungs- und Initialisierungsroutinen. Unten ist ein Beispiel:

Angenommen, der folgende Code befindet sich in einer Datei mit dem Namen 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;

Angenommen, das Folgende befindet sich in der Datei 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');
});

Mit Circuitbox können Sie Ihre Komponenten definieren und deren Abhängigkeiten als Module deklarieren. Nach der Initialisierung können Sie eine Komponente abrufen. Circuitbox injiziert automatisch alle Komponenten, die die Zielkomponente benötigt, und gibt sie zur Verwendung an Sie weiter.

Das Projekt ist in Alpha-Version. Ihre Kommentare, Ideen und Rückmeldungen sind willkommen.

Ich hoffe es hilft!

0
oddjobsman

Es sollte so flexibel und einfach sein:

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


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

Ich habe einen Artikel über Dependency Injection in node.js geschrieben.

Ich hoffe es kann dir dabei helfen.

0
slava

Ich denke, andere Beiträge haben im Argument für die Verwendung von DI großartige Arbeit geleistet. Für mich sind die Gründe

  1. Injizieren Sie Abhängigkeiten, ohne ihre Pfade zu kennen. Dies bedeutet, dass Sie nicht jede Datei berühren müssen, die davon abhängt, wenn Sie einen Modulspeicherort auf der Festplatte ändern oder durch eine andere austauschen.

  2. Es macht es viel einfacher, Abhängigkeiten für Tests zu verspotten, ohne die globale Funktion require auf eine Weise außer Kraft zu setzen, die problemlos funktioniert.

  3. Es hilft Ihnen, Ihre Bewerbung als lose gekoppelte Module zu organisieren und zu begründen.

Es fiel mir jedoch sehr schwer, ein DI-Framework zu finden, das mein Team und ich problemlos übernehmen können. Also habe ich kürzlich ein Framework namens deppie basierend auf diesen Funktionen erstellt

  • Minimale API, die in wenigen Minuten erlernt werden kann
  • Kein zusätzlicher Code/Konfiguration/Anmerkungen erforderlich
  • Eins zu eins direkte Zuordnung zu require Modulen
  • Kann teilweise übernommen werden, um mit vorhandenem Code zu arbeiten
0
Gaafar

Ich habe lange mit .Net, PHP und Java gearbeitet, deshalb wollte ich auch eine praktische Abhängigkeitsinjektion in NodeJS haben in NodeJS ist genug, da wir es mit Module bekommen können. Aber es hat mich nicht zufriedengestellt. Ich wollte ein Modul nicht mehr als eine Klasse behalten. Außerdem wollte ich, dass das DI eine vollständige Unterstützung für das Modul-Lebenszyklus-Management hat ( singleton module, transient module etc.), aber mit Node module musste ich sehr oft manuellen Code schreiben. Schließlich wollte ich den Unit Test vereinfachen. Deshalb habe ich mir eine Dependency Injection erstellt .

Wenn Sie nach einem DI suchen, probieren Sie es aus. Es kann hier gefunden werden: https://github.com/robo-creative/nodejs-robo-container . Es ist vollständig dokumentiert. Es werden auch einige häufig auftretende Probleme mit DI behoben und wie man sie auf OOP) Weise löst. Ich hoffe, es hilft.

0
Robo

Ich habe diese Frage entdeckt, als als Antwort auf ein Problem auf meinem eigenen DI-Modul gefragt wurde, warum man jemals ein DI-System für die NodeJS-Programmierung benötigen würde.

Die Antwort ging eindeutig auf die Antworten in diesem Thread über: Es kommt darauf an. Es gibt Kompromisse für beide Ansätze, und wenn man die Antworten dieser Frage liest, erhält man eine gute Form davon.

Die eigentliche Antwort auf diese Frage sollte also sein, dass Sie in einigen Situationen ein DI-System verwenden, in anderen nicht.

Was Sie als Entwickler jedoch möchten, ist, dass Sie sich nicht wiederholen und Ihre Dienste in Ihren verschiedenen Anwendungen wiederverwenden.

Dies bedeutet, dass wir Dienste schreiben sollten, die bereit sind, im DI-System verwendet zu werden, aber nicht an DI-Bibliotheken gebunden sind. Für mich bedeutet das, dass wir Dienste wie diesen schreiben sollten:

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
}

Auf diese Weise funktioniert Ihr Dienst unabhängig davon, ob Sie ihn mit oder ohne DI-Tool verwenden.

0
nfroidure

Ich habe eine Bibliothek entwickelt, die die Abhängigkeitsinjektion auf einfache Weise handhabt und den Boilerplate-Code verringert. Jedes Modul ist durch einen eindeutigen Namen und eine Controller-Funktion definiert. Die Parameter der Steuerung spiegeln die Abhängigkeiten des Moduls wider.

Lesen Sie mehr auf KlarkJS

Kurzes Beispiel:

KlarkModule(module, 'myModuleName1', function($nodeModule1, myModuleName2) {
    return {
        log: function() { console.log('Hello from module myModuleName1') }
    };
});
  • myModuleName1 Ist der Name des Moduls.
  • $nodeModule1 Ist eine externe Bibliothek von node_module. Der Name wird in node-module1 Aufgelöst. Das Präfix $ Zeigt an, dass es sich um ein externes Modul handelt.
  • myModuleName2 Ist der Name eines internen Moduls.
  • Der Rückgabewert der Steuerung wird von den anderen internen Modulen verwendet, wenn diese den Parameter myModuleName1 Definieren.
0
Apostolidis

Node.js benötigt DI genauso wie jede andere Plattform. Wenn Sie etwas Großes erstellen, wird DI es einfacher machen, die Abhängigkeiten Ihres Codes zu verspotten und Ihren Code gründlich zu testen.

Beispielsweise sollten Ihre Datenbankschichtmodule nicht nur für Ihre Geschäftscodemodule erforderlich sein, da die Daos beim Testen dieser Geschäftscodemodule laden und eine Verbindung zur Datenbank herstellen.

Eine Lösung wäre, die Abhängigkeiten als Modulparameter zu übergeben:

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

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

Auf diese Weise können Abhängigkeiten auf einfache und natürliche Weise verspottet werden, und Sie können sich weiterhin auf das Testen Ihres Codes konzentrieren, ohne eine knifflige Bibliothek von Drittanbietern zu verwenden.

Es gibt andere Lösungen (Broadway, Architekt usw.), die Ihnen dabei helfen können. obwohl sie mehr können als Sie wollen oder mehr Unordnung verwenden.

0
user2468170