JavaScript
Node.js
DependencyInjection

nodejs, ES6 で dependency injection with awilix

More than 1 year has passed since last update.

概要

nodejs でちょっとしたアプリ(WebAPI や DB からデータを取得して、AWS S3に登録する)を作ることになりました。
小さなコードなので、typescript とか使うのも面倒だなと思い、ES6 で書いていました。
プロトタイプ的に一通り実装したあと、プロダクト用に仕上げるに当たって、やはり DI 必要だなと思い、調べていたところ、awilix が使いやすかったという話です。

そもそも何で DI が必要か

プロトタイプでは DAO を以下のように実装していました。

dao.js
const webDAO = require("./net"); // WEB API 通信モジュール
const dbDAO = require("./db"); // DB 操作モジュール
const s3 = require("./s3"); // S3 操作モジュール

module.exports = {
    getData1: function(code){
        return webDAO.getData(code);
    },
    getData2: function(type){
        return dbDAO.getData(type);
    },
    putData: function(key, data){
        return s3.putObject(key, data);
    }
};

使う側は以下のような感じです。

userSide.js
const dao = require("./dao");

const data1 = dao.getData1("sample");
dao.putData("sampleKey", data1);

で、これだと困ることがいくつかあります。

  • ローカル環境では S3 じゃなくてファイルに出力したいこともある
  • ローカル環境ではデータを固定にしたいこともある
  • 一連の処理のテストコードを書きたいが、外部リソースに依存していては・・・

と言うわけで DI の出番です。

Awilix が良いと思った理由

JS の DI framework って決定版的なものがないですよね、きっと。
以前に typescript を使った時は inversifyJS を使ったのですが、JS対応はイマイチだったので、他を探していたところ、Awilixを見つけました。

シンプルで使い勝手が良さそうです。ES6 の Proxy を使っているらしいです。

DI 版に修正する

※サンプルなので諸々テキトーです。

DI なので dao をコンストラクタで受け取るようにします。

const awilix = require('awilix');

const container = awilix.createContainer();

// モックの DAO
function mock() {
    const fs = require("fs-extra");
    container.registerValue({
        dao: {
            getData1: function(code){
                return [];
            },
            getData2: function(type){
                return [];
            },
            putData: function(key, data){
                return fs.outputFile(`${key}.txt`, data);
            }
        }
    });
}

// プロダクト用の DAO
function prod() {
    const dao = require("./dao/dao");
    container.registerValue({dao: dao});
}

mock(); // 注:ここを環境によって切り替える。
// prod();


// 以下、使う側

class TestUser {
    constructor(opts) {
        this.dao = opts.dao; // モックまたはプロダクト用の DAO がインジェクトされる
    }

    test() {
        this.dao.getData1("sample");
    }
}

container.registerClass({
    testUser: TestUser
});

const testUser = container.resolve("testUser");
testUser.test();

サンプルなので諸々テキトーです。
Usageが分かりやすいので、そちらを参照してください。

まとめ

Awilix で testable な nodejs 開発をしましょー。