概要
nodejs でちょっとしたアプリ(WebAPI や DB からデータを取得して、AWS S3に登録する)を作ることになりました。
小さなコードなので、typescript とか使うのも面倒だなと思い、ES6 で書いていました。
プロトタイプ的に一通り実装したあと、プロダクト用に仕上げるに当たって、やはり DI 必要だなと思い、調べていたところ、awilix が使いやすかったという話です。
そもそも何で DI が必要か
プロトタイプでは DAO を以下のように実装していました。
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);
}
};
使う側は以下のような感じです。
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 開発をしましょー。