web-dev-qa-db-ja.com

NodeJSに依存性注入が必要ですか、それともどう対処するのですか?

現在、nodejsでいくつかの実験プロジェクトを作成しています。 Springを使用してJava EE Webアプリケーションを多数プログラミングし、依存関係の注入が容易であることを高く評価しました。

今、私は興味があります:ノードで依存性注入を行うにはどうすればよいですか?または:それも必要ですか?プログラミングスタイルが異なるため、代替の概念はありますか?

これまで、データベース接続オブジェクトの共有などの簡単なことについて話していましたが、満足できるソリューションは見つかりませんでした。

197
Erik

つまり、C#/ Javaの場合のように、依存関係注入コンテナーまたはサービスロケーターは必要ありません。 Node.jsはmodule patternを活用するため、コンストラクターまたはプロパティインジェクションを実行する必要はありません。まだできますが。

JSの素晴らしいところは、あなたが望むものを達成するためにほとんど何でも変更できることです。これは、テストに関して便利です。

私の非常に不自然な不自然な例を見てください。

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

MyClassfsモジュールにどのように依存しているかに注目してください。 @ShatyemShekharが言及したように、他の言語と同様に、実際にコンストラクターまたはプロパティーの注入を行うことができます。ただし、Javascriptでは必要ありません。

この場合、2つのことができます。

fs.readdirSyncメソッドをスタブするか、requireを呼び出すときにまったく異なるモジュールを返すことができます。

方法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;

方法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);

}

重要なのは、Node.jsとJavascriptの力を活用することです。私はCoffeeScriptの男なので、私のJS構文はどこか間違っているかもしれません。また、これが最良の方法であると言っているわけではありませんが、それは方法です。 Javascriptの達人は、他のソリューションと仲良くすることができるかもしれません。

更新:

これにより、データベース接続に関する特定の質問に対処できます。データベース接続ロジックをカプセル化するための別のモジュールを作成します。このようなもの:

MyDbConnection.js:(より適切な名前を選択してください)

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    
}

次に、データベース接続を必要とするモジュールには、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. 

この例をそのまま逐語的に実行しないでください。 moduleパターンを利用して依存関係を管理していることを伝えようとするのは不十分な例です。これがもう少し役立つことを願っています。

101
JP Richardson

requireは、Node.jsで依存関係を管理する方法であり、確かに直感的で効果的ですが、制限もあります。

私のアドバイスは、Node.jsで現在利用できるDependency Injectionコンテナのいくつかを見て、長所/短所を把握することです。それらのいくつかは次のとおりです。

ほんの数例を挙げます。

本当の問題は、単純なrequireと比較して、Node.js DIコンテナーで何を達成できるかということです。

長所:

  • テスト容易性の向上:モジュールは依存関係を入力として受け入れます
  • 制御の反転:アプリケーションのメインコードに触れることなくモジュールを配線する方法を決定します。
  • モジュールを解決するためのカスタマイズ可能なアルゴリズム:依存関係には「仮想」識別子があり、通常はファイルシステム上のパスにバインドされていません。
  • 拡張性の向上:IoCおよび「仮想」識別子によって有効になります。
  • 可能な他の派手なもの:
    • 非同期初期化
    • モジュールのライフサイクル管理
    • DIコンテナ自体の拡張性
    • より高いレベルの抽象化(AOPなど)を簡単に実装できます

短所:

  • Node.jsの「エクスペリエンス」とは異なります。requireを使用しないことは、Nodeの考え方から逸脱しているように感じられます。
  • 依存関係とその実装との関係は、必ずしも明示的ではありません。依存関係は実行時に解決され、さまざまなパラメーターの影響を受ける場合があります。コードの理解とデバッグがより難しくなります
  • 起動時間が遅い
  • 成熟度(現時点):現在のソリューションはどれも本当に人気がないため、あまり多くのチュートリアルもエコシステムも戦闘テストもされていません。
  • 一部のDIコンテナは、BrowserifyやWebpackなどのモジュールバンドラーとうまく機能しません。

ソフトウェア開発に関連するものと同様に、DIまたはrequireのどちらを選択するかは、要件、システムの複雑さ、プログラミングスタイルによって異なります。

67
Mario

このスレッドはこの時点ではかなり古いことは知っていますが、これについての私の考えに耳を傾けると思いました。 TL; DRは、JavaScriptの型指定されていない動的な性質により、依存性注入(DI)パターンに頼ったり、DIフレームワークを使用したりせずに実際に多くのことを実行できることです。ただし、アプリケーションがより大きく複雑になるにつれて、DIはコードの保守性を確実に高めることができます。

C#のDI

JavaScriptでDIがそれほど必要ではない理由を理解するには、C#のような強く型付けされた言語を見ると役立ちます。 (C#を知らない人にはおAびしますが、従うのに十分簡単なはずです。)車とそのホーンを説明するアプリがあるとします。次の2つのクラスを定義します。

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

この方法でコードを作成する場合、問題はほとんどありません。

  1. Carクラスは、Hornクラスのホーンの特定の実装と密結合しています。車が使用するホーンのタイプを変更したい場合は、ホーンの使用法は変わらなくても、Carクラスを変更する必要があります。 Carクラスを依存関係Hornクラスから分離してテストすることはできないため、テストも困難になります。
  2. Carクラスは、Hornクラスのライフサイクルを担当します。このような単純な例では大きな問題ではありませんが、実際のアプリケーションでは依存関係に依存関係があり、依存関係などがあります。Carクラスは、依存関係のツリー全体を作成する必要があります。これは複雑で反復的なだけでなく、クラスの「単一の責任」に違反します。インスタンスを作成するのではなく、自動車であることを重視する必要があります。
  3. 同じ依存関係インスタンスを再利用する方法はありません。繰り返しますが、これはこの玩具アプリケーションでは重要ではありませんが、データベース接続を検討してください。通常、アプリケーション全体で共有される単一のインスタンスがあります。

それでは、これをリファクタリングして、依存性注入パターンを使用しましょう。

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

ここで2つの重要なことを行いました。最初に、Hornクラスが実装するインターフェイスを導入しました。これにより、特定の実装ではなくCarクラスをインターフェイスにコーディングできます。これで、コードはIHornを実装するものなら何でも使用できます。次に、Carからホーンのインスタンス化を取り出し、代わりに渡します。これにより、上記の問題が解決され、特定のインスタンスとそのライフサイクルを管理するアプリケーションのメイン機能に任せます。

これは、車がCarクラスに触れずに使用する新しいタイプのホーンを導入できることを意味します。

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

メインは、代わりにFrenchHornクラスのインスタンスを挿入するだけです。これにより、テストも大幅に簡素化されます。 MockHornクラスを作成してCarコンストラクターに注入することで、Carクラスのみを単独でテストできます。

上記の例は、手動の依存性注入を示しています。通常、DIはフレームワークで行われます(C#の世界では nity または Ninject )。これらのフレームワークは、依存関係グラフを調べて必要に応じてインスタンスを作成することにより、すべての依存関係の配線を行います。

標準のNode.jsの方法

ここで、Node.jsの同じ例を見てみましょう。コードをおそらく3つのモジュールに分割します。

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

JavaScriptは型付けされていないため、以前とまったく同じ密結合はありません。 carモジュールは、honkモジュールがエクスポートするものに対してhornメソッドを呼び出そうとするだけなので、インターフェイスは必要ありません(存在しません)。

さらに、Nodeのrequireはすべてをキャッシュするため、モジュールは基本的にコンテナに格納されたシングルトンです。 requireモジュールでhornを実行する他のモジュールは、まったく同じインスタンスを取得します。これにより、データベース接続などのシングルトンオブジェクトの共有が非常に簡単になります。

carモジュールがそれ自体の依存関係hornをフェッチする責任があるという問題がまだあります。車のホーンに別のモジュールを使用する場合は、requireモジュールのcarステートメントを変更する必要があります。これは一般的なことではありませんが、テストで問題が発生します。

テストの問題を処理する通常の方法は、 proxyquire です。 JavaScriptの動的な性質により、proxyquireは必要な呼び出しをインターセプトし、代わりに提供するスタブ/モックを返します。

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

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

// Now make test assertions on car...

ほとんどのアプリケーションではこれで十分です。アプリで機能する場合は、そのまま使用します。しかし、私の経験では、アプリケーションがより大きく複雑になるにつれて、このようなコードを維持することは難しくなります。

JavaScriptのDI

Node.jsは非常に柔軟です。上記の方法に満足できない場合は、依存性注入パターンを使用してモジュールを作成できます。このパターンでは、すべてのモジュールがファクトリー関数(またはクラスコンストラクター)をエクスポートします。

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

これは、index.jsモジュールがインスタンスのライフサイクルと配線を担当するという点で、以前のC#メソッドに非常によく似ています。モック/スタブを関数に渡すだけでよいため、単体テストは非常に簡単です。繰り返しますが、これがあなたのアプリケーションにとって十分であるなら、それを使います。

ボーラスDIフレームワーク

C#とは異なり、依存関係管理を支援する確立された標準DIフレームワークはありません。 npmレジストリには多くのフレームワークがありますが、広く採用されているフレームワークはありません。これらのオプションの多くは、他の回答ですでに引用されています。

使用可能なオプションに特に満足していなかったため、 bolus と呼ばれる独自のオプションを作成しました。 Bolusは、上記のDIスタイルで記述されたコードで動作するように設計されており、非常に DRY であり、非常にシンプルにしようとします。上記のまったく同じcar.jsおよびhorn.jsモジュールを使用すると、index.jsモジュールをボーラスで次のように書き換えることができます。

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

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

基本的な考え方は、インジェクターを作成することです。すべてのモジュールをインジェクターに登録します。その後、単に必要なものを解決します。ボーラスは、依存関係グラフを調べて、必要に応じて依存関係を作成および注入します。このようなおもちゃの例ではあまり節約しませんが、複雑な依存関係ツリーを持つ大規模なアプリケーションでは、節約量は膨大です。

Bolusは、オプションの依存関係やテストグローバルなど、数多くの気の利いた機能をサポートしていますが、標準のNode.jsアプローチに比べて、2つの重要な利点があります。最初に、同様のアプリケーションが多数ある場合は、ベース用のプライベートnpmモジュールを作成して、インジェクターを作成し、有用なオブジェクトを登録できます。その後、特定のアプリは AngularJS's インジェクターの動作と同じように、必要に応じて追加、オーバーライド、解決できます。次に、ボーラスを使用して、依存関係のさまざまなコンテキストを管理できます。たとえば、ミドルウェアを使用して、要求ごとに子インジェクターを作成し、ユーザーID、セッションID、ロガーなどを、それらに依存するモジュールとともにインジェクターに登録できます。次に、リクエストを処理するために必要なものを解決します。これにより、リクエストごとにモジュールのインスタンスが提供され、すべてのモジュール関数呼び出しにロガーなどを渡す必要がなくなります。

44
Dave Johnson

これを実現するモジュールも作成しました。これは rewire と呼ばれます。 npm install rewireを使用してから:

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

Nathan MacInnesのインジェクター に触発されましたが、別のアプローチを使用しました。テストモジュールを評価するためにvmを使用しません。実際、ノード独自のrequireを使用します。このように、モジュールはrequire()を使用するのとまったく同じように動作します(変更を除く)。デバッグも完全にサポートされています。

37
Johannes Ewald

私は 電解質 をこの目的のために構築しました。そこにある他の依存性注入ソリューションは、私の好みにはあまりにも侵襲的でした。グローバルrequireをいじることは、私の特定の不満です。

Electrolyteには、特にConnect/Expressミドルウェアで見られるような「セットアップ」機能をエクスポートするモジュールが含まれます。基本的に、これらのタイプのモジュールは、返されるオブジェクトの単なるファクトリーです。

たとえば、データベース接続を作成するモジュール:

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

一番下に表示されるのは注釈です。これは、電解液が依存関係をインスタンス化して注入し、アプリケーションのコンポーネントを自動的に接続するために使用する追加のメタデータです。

データベース接続を作成するには:

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

Electrolyteは、@require 'd依存関係を推移的に走査し、エクスポートされた関数への引数としてインスタンスを注入します。

重要なのは、これが低侵襲であることです。このモジュールは完全に使用可能で、電解質自体とは無関係です。つまり、単体テストでは、テスト対象モジュールのみをテストでき、内部を再配線するための追加の依存関係を必要とせずにモックオブジェクトを渡すことができます。

完全なアプリケーションを実行すると、電解質はモジュール間レベルで介入し、グローバル、シングルトン、または過度の配管を必要とせずに物事を相互に接続します。

17
Jared Hanson

私は自分でこれを調べました。私は、モジュールのインポートをハイジャックするメカニズムを提供する魔法の依存関係ユーティリティライブラリの導入を嫌います。その代わりに、モジュール内にファクトリ関数エクスポートを導入することで、どの依存関係をモックできるかを明確に述べるために、チーム用の「設計ガイドライン」を思いつきました。

ボイラープレートを回避し、名前付きの依存関係オーバーライドメカニズムを提供するために、パラメーターと構造化にES6機能を広範囲に使用します。

以下に例を示します。

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

そして、それはその使用例です

import { factory } from './bar';

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

ES6の構文に慣れていない人にはすみません。

9
ctrlplusb

私は最近、OPとほぼ同じ理由でこのスレッドをチェックしました-私が遭遇したほとんどのライブラリは、一時的にrequireステートメントを書き換えます。私はこの方法でさまざまな成功を収めてきたので、次のアプローチを使用することになりました。

エクスプレスアプリケーションのコンテキストで-app.jsを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();

ローダーに渡されるオブジェクトマップは次のようになります。

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

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

次に、直接requireを呼び出すのではなく...

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

ローダーにエイリアスが見つからない場合は、デフォルトの通常のrequireになります。これには2つの利点があります:クラスのどのバージョンでもスワップでき、アプリケーション全体で相対パス名を使用する必要がなくなります(したがって、現在のファイルの下または上にカスタムlibが必要な場合は、トラバースする必要はありません、およびrequireは同じキーに対してモジュールをキャッシュします)。また、即時テストスイートではなく、アプリの任意の時点でモックを指定できます。

便宜上、npmモジュールを少し公開しました。

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

5
sunwukung

JavaScriptは本当に動的なプログラミング言語であり、実行時にほとんどすべてを変更できるため、実際にはIoCコンテナーなしでnode.jsをテストできます。

以下を考慮してください。

import UserRepository from "./dal/user_repository";

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

export default UserController;

そのため、実行時にコンポーネント間の結合をオーバーライドできます。 JavaScriptモジュールを分離することを目指すべきだと思います。

実際のデカップリングを実現する唯一の方法は、UserRepositoryへの参照を削除することです。

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

export default UserController;

これは、他のどこかでオブジェクトの構成を行う必要があることを意味します。

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

export default new UserController(new UserRepository());

オブジェクトの構成をIoCコンテナーに委任するというアイデアが好きです。このアイデアの詳細については、記事 JavaScriptでの依存関係の反転の現在の状態 をご覧ください。この記事では、「JavaScript IoCコンテナーの神話」をいくつか明らかにしようとしています。

神話1:JavaScriptにはIoCコンテナーの場所がない

神話2:IoCコンテナーは必要ありません。すでにモジュールローダーがあります!

神話3:依存関係の反転===依存関係の注入

IoCコンテナーを使用するというアイデアも気に入った場合は、InversifyJSをご覧ください。最新リリース(2.0.0)は、多くのユースケースをサポートしています。

  • カーネルモジュール
  • カーネルミドルウェア
  • クラス、文字列リテラル、またはシンボルを依存識別子として使用します
  • 定数値の注入
  • クラスコンストラクターの注入
  • 工場の注入
  • 自動車工場
  • プロバイダーのインジェクション(非同期ファクトリー)
  • アクティベーションハンドラー(プロキシの注入に使用)
  • マルチインジェクション
  • タグ付きバインディング
  • カスタムタグデコレータ
  • 名前付きバインディング
  • コンテキストバインディング
  • わかりやすい例外(循環依存関係など)

詳しくは InversifyJS をご覧ください。

3
Remo H. Jansen

Nodejsの依存性注入は、サービス間の依存性を緩和し、アプリケーションを明確にするため、まだ必要だと思います。

Spring Framework から着想を得て、Nodejsでの依存性注入をサポートする独自のモジュールも実装しています。私のモジュールは、アプリケーションを再起動せずに、code changesおよびauto reloadサービスを検出することもできます。

私のプロジェクトにアクセスしてください: Buncha-IoC container

ありがとうございました!

2
Tho

IoCコンセプトのシンプルさがいつも好きでした-「環境について何も知る必要はありません。必要なときに誰かから呼び出されます」

しかし、私が見たすべてのIoC実装はまったく逆のことを行いました。コードがない場合よりもさらに多くのものでコードが乱雑になります。だから、私はそれが望むように動作する独自のIoCを作成しました-それは時間の90%隠されて見えないままです.

MonoJS Webフレームワークで使用されています http://monojs.org

これまで、データベース接続オブジェクトの共有などの簡単なことについて話していましたが、満足できるソリューションは見つかりませんでした。

これは次のように行われます-構成にコンポーネントを一度登録します。

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

そして、どこでもそれを使用します

app.db.findSomething()

完全なコンポーネント定義コード(DB接続およびその他のコンポーネントを含む)は、こちらで確認できます https://github.com/sinizinairina/mono/blob/master/mono.coffee

これは、IoCに何をすべきかを伝えなければならない唯一の場所です。その後、これらすべてのコンポーネントが自動的に作成および配線され、アプリケーションでIoC固有のコードを表示する必要がなくなります。

IoC自体 https://github.com/alexeypetrushin/miconjs

2

ES6の場合、このコンテナを開発しました https://github.com/zazoomauro/node-dependency-injection

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

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

次に、たとえば、コンテナ内のトランスポートの選択を設定できます。

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

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

実装からコンテナへのトランスポートの選択を分離したため、このクラスはより柔軟になりました。

メーラーサービスがコンテナ内にあるので、他のクラスの依存関係として注入できます。このようなNewsletterManagerクラスがある場合:

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

export default NewsletterManager

Newsletter_managerサービスを定義するとき、メーラーサービスはまだ存在しません。 Referenceクラスを使用して、ニュースレターマネージャーを初期化するときにメーラーサービスを挿入するようにコンテナーに指示します。

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

Yaml、Json、JSファイルなどの構成ファイルを使用してコンテナーをセットアップすることもできます

サービスコンテナはさまざまな理由でコンパイルできます。これらの理由には、循環参照などの潜在的な問題のチェックや、コンテナの効率化が含まれます。

container.compile()
2
Mauro

アプリケーションの設計に依存します。このようなコンストラクターで渡された依存関係を持つクラスのオブジェクトを作成するインジェクションのようなJavaを明らかに行うことができます。

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

var cache = new Cache(mysqlStore);

JavaScriptでOOPを実行していない場合は、すべてを設定するinit関数を作成できます。

ただし、node.jsなどのイベントベースのシステムではより一般的な、使用できる別のアプローチがあります。 (ほとんどの場合)イベントのみを処理するようにアプリケーションをモデル化できる場合は、すべてをセットアップし(通常はinit関数を呼び出すことで)、スタブからイベントを発行するだけです。これにより、テストがかなり簡単で読みやすくなります。

1
Satyam Shekhar

Googleの di.js はnodejsで動作します(+ブラウザー)(+ ES6)

1
Amit Portnoy

ディップをご覧ください(Node.js向けのシンプルかつ強力な依存性注入およびエンティティ(ファイル)管理フレームワーク)

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

1
Mario

最近、 circuitbox というライブラリを作成しました。これにより、node.jsで依存関係注入を使用できます。私が見た依存関係検索ベースのライブラリの多くに対して、真の依存関係注入を行います。 Circuitboxは、非同期の作成および初期化ルーチンもサポートしています。以下に例を示します。

次のコードが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;

ファイル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を使用すると、コンポーネントを定義し、その依存関係をモジュールとして宣言できます。初期化されると、コンポーネントを取得できます。 Circuitboxは、ターゲットコンポーネントに必要なすべてのコンポーネントを自動的に注入し、使用するために提供します。

プロジェクトはアルファ版です。あなたのコメント、アイデア、フィードバックを歓迎します。

それが役に立てば幸い!

0
oddjobsman

次のように、柔軟でシンプルでなければなりません。

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


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

Node.jsのDependency Injectionに関する記事を書きました。

これがあなたのお役に立てば幸いです。

0
slava

DIを使用するという議論において、他の投稿が素晴らしい仕事をしたと思います。私にとっての理由は

  1. パスを知らずに依存関係を注入します。つまり、ディスク上のモジュールの場所を変更したり、別の場所と交換したりする場合、それに依存するすべてのファイルに触れる必要はありません。

  2. 問題なく動作するようにグローバルrequire関数をオーバーライドすることなく、テストの依存関係を簡単にモックできます。

  3. 疎結合モジュールとしてアプリケーションを整理し、推論するのに役立ちます。

しかし、チームと私が簡単に採用できるDIフレームワークを見つけるのは本当に大変でした。だから私は最近 deppieというフレームワークを構築した これらの機能に基づいて

  • 数分で習得できる最小限のAPI
  • 追加のコード/構成/注釈は必要ありません
  • requireモジュールへの1対1の直接マッピング
  • 既存のコードで動作するように部分的に採用できます
0
Gaafar

私は.Net、PHPおよびJavaで長い間働いていたので、NodeJSでも便利な依存関係の注入が必要でした。 NodeJSに組み込まれているDIは、Moduleで入手できるので十分だと人々は言いました。しかし、それは私を満足させませんでした。モジュールをクラス以上にしたくありませんでした。さらに、DIがモジュールライフサイクル管理(シングルトンモジュール、一時モジュールなど)を完全にサポートすることを望んでいましたが、Nodeモジュールでは、非常に頻繁に手動コードを書く必要がありました。最後に、ユニットテストを簡単にしたいと考えました。それが私が自分のために依存性注入を作成した理由です。

DIを探している場合は、試してみてください。ここにあります: https://github.com/robo-creative/nodejs-robo-container 。完全に文書化されています。また、DIの一般的な問題とOOPの方法でそれらを解決する方法についても説明します。それが役に立てば幸い。

0
Robo

問題への回答 NodeJSプログラミングにDIシステムが必要になる理由を尋ねる自分のDIモジュールでこの質問を発見しました。

答えは明らかにこのスレッドで与えられたものに依存していました:それは依存します。両方のアプローチにはトレードオフがあり、この質問の回答を読むと、それらの良い形がわかります。

したがって、この質問に対する本当の答えは、ある状況ではDIシステムを使用し、別の状況では使用しないということです。

そうは言っても、開発者としてあなたが望むのは、自分自身を繰り返さずに、さまざまなアプリケーションでサービスを再利用することです。

これは、DIシステムで使用する準備ができているが、DIライブラリに結び付けられていないサービスを記述する必要があることを意味します。私にとっては、次のようなサービスを作成する必要があるということです。

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
}

このように、DIツールの有無にかかわらずサービスを使用しても、サービスは問題になりません。

0
nfroidure

単純な方法で依存性注入を処理するライブラリを開発しました。これにより、定型コードが削減されます。各モジュールは、一意の名前とコントローラー関数によって定義されます。コントローラーのパラメーターは、モジュールの依存関係を反映しています。

KlarkJS の詳細を読む

簡単な例:

KlarkModule(module, 'myModuleName1', function($nodeModule1, myModuleName2) {
    return {
        log: function() { console.log('Hello from module myModuleName1') }
    };
});
  • myModuleName1はモジュールの名前です。
  • $nodeModule1node_moduleの外部ライブラリです。名前はnode-module1に解決されます。接頭辞$は、外部モジュールであることを示します。
  • myModuleName2は、内部モジュールの名前です。
  • コントローラーの戻り値は、他の内部モジュールがパラメーターmyModuleName1を定義するときに使用されます。
0
Apostolidis

Node.jsには、他のプラットフォームと同様にDIが必要です。大きなものを構築する場合、DIを使用すると、コードの依存関係を簡単にモックし、コードを徹底的にテストできます。

たとえば、データベースレイヤーモジュールは、ビジネスコードモジュールで必要になるだけではありません。これらのビジネスコードモジュールを単体テストするときに、daosがデータベースに読み込まれて接続するからです。

解決策の1つは、依存関係をモジュールパラメーターとして渡すことです。

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

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

この方法により、依存関係を簡単かつ自然にモックでき、トリッキーなサードパーティライブラリを使用せずにコードのテストに集中できます。

これに役立つ他のソリューション(ブロードウェイ、建築家など)があります。彼らはあなたが望む以上のことをするかもしれませんが、もっと混乱させるかもしれません。

0
user2468170