1. sugiyasu-qr

    Posted

    sugiyasu-qr
Changes in title
+nodejs, ES6 を awilix で DI する
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,117 @@
+# 概要
+
+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 を使った時](http://qiita.com/sugiyasu-qr/items/cbf074ee17a9e119f783)は inversifyJS を使ったのですが、[JS対応はイマイチ](https://github.com/inversify/InversifyJS/blob/master/wiki/basic_js_example.md)だったので、他を探していたところ、[Awilix](https://github.com/jeffijoe/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](https://github.com/jeffijoe/awilix)が分かりやすいので、そちらを参照してください。
+
+# まとめ
+
+Awilix で testable な nodejs 開発をしましょー。