Expressからの...NestJS

続き。何がしたいかというと、REST-fulなAPIをテストしやすく拡張しやすい手法で作りたいだけ。しかしAWSサーバレスの上でTypeScriptにと縛っていくとなかなか答えにたどり着けなかったのです。

NestJS

nestjs.com

サーバサイドのフレームワークとしてLoopBack 4の代わりを探していたら検索チェーンの果てで突き当たる。aws-serverless-expressに載せようとしてる中で、おそらくそれら用途で Expressのインスタンスを触れる方法 が用意されていました。さほどExpress感は無いが確かにExpressだったのと、バリバリTypeScript前提な作り。流行りなのかCLIツールも備えてますがまずは無視してフルスクラッチに行きます。LoopBack 4では初期ドキュメントを追っかける限りではCLIを無視できなかったので、これは良い。

// lambda.ts
import { createServer, proxy } from 'aws-serverless-express';
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
import { NestFactory } from '@nestjs/core';
import { Server } from 'http';
import * as express from 'express';
import module from './module';
// 追記(後述):実はこの無名クラス取り込みはダメ!!!
// 変更例としては「import { UserModule } from './module';」 

let server: Server;
export default function (event: APIGatewayProxyEvent, context: Context) {
    if (server) {
        return proxy(server, event, context);
    }
    (async () => {
        const app = express();
        const nest = await NestFactory.create(module, app);
        await nest.init();
        server = createServer(app);
        proxy(server, event, context);
    })();
}

NestFactory.createおよびinitが非同期なのでめんどくさい。コールドスタートした時にハンドラが先に動かないようにしないと。一方でホットスタートした時にまた非同期の初期化作業はさせたくなくこんな作り。もっと上手い書き方ありそうなので考え続けてみます。ExpressもLambdaも出てくるのはここまで。これから先はほとんど全てNestJSの上だけで作っていくことが可能。

// /module/user.ts
import * as nest from '@nestjs/common';
import * as express from 'express';

@nest.Controller('user')
export default class {
// 追記(後述):実はこの無名クラスはダメ!!!変更例「export class UserController {」
    // 非同期もPromiseを返すだけでOK。簡単に対応できる
    @nest.Get()
    async findAll(): Promise<string[]> {
        return Promise.resolve(['a', 'b', 'c', 'd', 'e']);
    }

    @nest.Get('ping')
    ping(@nest.Response() res: express.Response) {
        res.send('PING!');
    }

    @nest.Get(':id')
    findOne(@nest.Param('id') id: string): string {
        return `This action returns a #${id} user`;
    }
}

デコレータが出てきちゃいました。LoopBack 4でも出てきましたがこいつは10年以上前のJavaフレームワーク繚乱時代を思い出させます。そこではクラスに付加情報としてJavaではアノテーションと呼ばれていた言語機能を活用してフレームワークIDEによる介入を行なっていました。上記では@Controllerと@Getで実はルーティングを表現できちゃってる。すなわちfindAllメソッドは、/userで呼び出され、findOneメソッドは、/user/:id で。

pingメソッドでは引数に@Responseデコレータを用いてExpress由来のオブジェクトをDependency Injectionしています。まさにJavaでよくやられた懐かしい手法です。

// /module/index.ts
import * as nest from '@nestjs/common';
import user from './user';

@nest.Module({
    controllers: [user],
})
export default class {
// 追記(後述):実はこの無名クラスはダメ!!!変更例、「export class UserModule {」とすべき。
}

NestJS、シンプルにとてもいいんじゃないですかね。LoopBack 4よりは洗練されている気がする。ただ、モジュール間の依存性をDIで解決と言ってるのだけど、特にインターフェイス疎結合にして実装を分離するのではなく、実クラスをそのままとり回してるのだけどそういうものなのか?Angular由来というDIが昔のSpringやSeasarで馴染んでたDIと狙いというか概念がちょっと違う気がして戸惑っている。まあ、DIの根幹としてコンストラクターを直で呼ばせずにフレームワークインスタンス生成して、それがちゃんとシングルトンで管理されていれば良いってことかな。

無名クラスを複数個並べた場合の問題

@Module({imports})や@Module({controllers})の値が配列をとるので当然モジュールがグラフ状に広がる作りが可能なはずです。しかし、ここまで私の手元ではそれぞれ一つのモジュールに一つのコントローラーでサンプル程度を作ってる時には動いていましたが、二つ目のモジュールやコントローラを追加すると期待した通りには動かない、DIがきちんと動かないことがありました。

当初、非Lambdaブートストラップ版を書いたり、それをwebpackでビルドして動かしてみたり、公式のサンプルをいくつか持ってきたりして実験していましたが、どうにもうまく動いたり動かなかったり。。。と、しばらくしてトランスパイルされた結果を眺めていて突然閃く。

上記記事で書いた私のコードではモジュールやコントローラを無名クラスで書いてexport defaultしていたのです。これがダメ。NestJSのサンプルもドキュメントも全てそうは書かれてなかったのですが、私のいつもの好む書き癖から無名クラスで書いちゃってました。で、これがNestJSのDIの仕組みの中で一つ目のdefault exportな無名クラスに「default_1」という名前がつけられるのは良いとして、二つ目の無名クラスも同じ名前で被らせてしまうみたいなのです。おそらくここで複数ソースコードに渡って管理されていて「default_2」とでも添字をインクリメントするか、importでつけた名前に書き換えてくれれば問題ないんですけどね。これはトランスパイラーの仕様由来なのかな?現象の再現方法がわかったのでNestJSのコードを追って原因究明とフィードバックをいつかの課題としておきたいと思います。

おそらく私以外にもハマる人いるはず!いないかな?NestJSのインジェクション対象(自分でコンストラクター呼び出しでインスタンスを作らないもの)は無名クラスはダメ。お気をつけください。

supertestとnock

github.com

自然な流れで、supertest。結構軽量な作りでhttp.Serverベースのアプリケーションを直接テスト内でアクセスするアサーションライブラリ。

github.com

こちらのnockは外部APIをモック化するライブラリ。

常用のjestにてsupertestとnockを組み合わせたら便利じゃないかなと想像している。まだ必要になるところまで届いていないので、想像だけ、備忘録としてだけ。

サーバレスでExpressと再会

eslint-typescript (様子見)

qiita.com

tslintとtslint-config-airbnbを常用していますが、中の人による上記記事ではいずれ世の中はtslintからeslintに移行されていくってことが予見されています。eslintの方が設定をpackage.jsonの中に書けるってのが好き。IDEやビルドツール群との連携は元々eslintの方が強いので、ルールのTypeScript対応が成熟してくれば。現在困ってないので今はちょっと待ちで、近い将来に移行してみようと思う。過渡的にeslintでtslintのルールを使うブリッジが提供されているけど特に狙いもないので冗長なので、eslintネイティブにいい感じになるのを様子見ながら待ち。

serverless-webpack

github.com

今まで使ってたserverless-plugin-typescripitでは、コードのコンパイルについてゼロ設定で対応してくれるけど、node_modulesはそのままクラウドへ持ち上げるのでデプロイの際に無駄に大きなパッケージを作ります。Jestとか@types以下全部とか、明らかに不要なものが含まれています。webpackでまとめて小さくするととてもコンパクトになり、結果としてデプロイ速度もスピンアップも早くなりとても有用です。

SPAを作る時にはクソめんどくさいwebpack設定も、この用途ではTypeScriptをコンパイルするだけなのでシンプルです。特にプロジェクト毎に変わるものでは無いので一度作ったらしばらく使い回しできる。

// webpack.config.js
const path = require('path');
const serverlessWebpack = require('serverless-webpack');
const webpackNodeExternals = require('webpack-node-externals');

module.exports = {
    mode: serverlessWebpack.lib.webpack.isLocal ? "development" : "production",
    entry: serverlessWebpack.lib.entries,
    resolve: {
        extensions: [
            '.js',
            '.json',
            '.ts'
        ]
    },
    output: {
        libraryTarget: 'commonjs',
        path: path.join(__dirname, '.webpack'),
        filename: '[name].js'
    },
    target: 'node',
    externals: [webpackNodeExternals()],
    module: {
        rules: [
            {
                enforce: 'pre',
                test: /\.ts$/,
                use: [
                    {
                        loader: 'tslint-loader',
                        options: {
                            typeCheck: true,
                            emitErrors: true,
                        },
                    },
                ],
            },
            {
                test: /\.ts$/,
                use: [
                    {
                        loader: 'ts-loader'
                    }
                ],
            }
        ]
    }
};

さらに、serverless.ymlではプラグイン登録と、ちょっと設定が必要。

# serverless.ymlの該当部分
plugins:
    - serverless-webpack

custom:
    webpack:
        includeModules:
            forceExclude:
                - aws-sdk
        packager: 'yarn'

webpack設定の方でnode_modulesに入るものを取り除き、serverless設定の方でパッケージに追加しています。custom.webpack.includeModulesはtrueを設定すると依存パッケージを全部取り込みます。aws-sdkは入れなくてもAWS環境側で用意されてますので、forceExcludeにホワイトリスト登録するとパッケージから省く&ワーニングが無いとのこと。

パッケージマネージャーは惰性でyarnを使ってますが、その後の機能追随と速度向上および元々のデフォルト感から、折見てnpmへ戻して良いかもと思っています。

Express

Reactアプリの対となるAPIを作る際に、引き続きAWSのサーバーレス製品群でやろうと思っています。今回の手法としてはServerless Frameworkとaws-serverless-expressでExpressアプリをLambdaへ載せてみてます。

github.com

今まではLambda上でExpressを使うことはなんとなく遅くてダメなんじゃないかなと思い込んでたけど、よく考えたらExpressのミドルウェアによってちょっとぐらいコールスタックが増えてもパワフルな現代のコンピューターの前にはなんの影響もないだろうし、一個のLambdaでエンドポイントだけ面倒見てもらってそれ以下はExpressのコードで書いた方が見通しもよく、変更にも強く、テストもしやすく、他クラウドへの移植性もよいんじゃないかなと。さらにはServerless Framworkで管理対象が巨大になってくると、背後のCloudFormationの由来にてリソース上限数制限200個が来るから、Lambdaが一個になってれば当然制限を避けられる。

社内でちょっと話してた時に指摘されたのは、多数Lambaがそれぞれ立ち上がるよりはコールドスタートが掛かりにくいってメリットはあるかもと。そしてLambdaなのにExpressで書いて良いってことになればExpressの既存知識や膨大な世間のコード資産が有効活用できそう。

# serverless.yml
service: express

provider:
    name: aws
    runtime: nodejs8.10
    stage: dev
    region: ap-northeast-1

plugins:
    - serverless-webpack

custom:
    webpack:
        includeModules:
            forceExclude:
                - aws-sdk
        packager: 'yarn'

functions:
    app:
        handler: lambda.default
        events:
            - http:
                  method: ANY
                  path: '{proxy+}'
                  cors: true

serverless.ymlの方では、methodが「ANY」で、pathが「{proxy+}」を設定します。これはAWSのドキュメントではプロキシ統合と呼んでいる。これでAPI GatewayではルーティングをせずにExpressの方に全てが回って来ます。

// lambda.ts
import { createServer, proxy } from 'aws-serverless-express';
import { eventContext } from 'aws-serverless-express/middleware';
import { APIGatewayProxyEvent, Context } from 'aws-lambda';
import * as express from 'express';

// 普通にExpressのインスタンスを作る
const app = express();

// aws-serverless-express/middlewareでAPI Gatewayのイベントを搭載してみる
// http.Requestですでにアプリを作るのに十分な情報があるので、実戦だったらいらない機能かなと思います。
app.use(eventContext());
type Event = {
    apiGateway: {
        event: any;
    };
};
const dumpEvent: express.Handler = (req, res) => {
    res.json((req as unknown as Event).apiGateway.event);
};

// ここでは例なのでワイルドカードによる全部受けハンドラ登録をExpressに対して行っている。
// 実戦では、app.get('/dump', dumpEvent); というようにメソッドとパスの組み合わせを具体的に設定する。
app.all('*', dumpEvent);

// aws-serverless-expressでLambdaと直結する。
const server = createServer(app);
export default function (event: APIGatewayProxyEvent, context: Context) {
    proxy(server, event, context);
}

上記ではとりあえず例としてExpressでもワイルドカード利用でリクエスト全部受けのコード書いてますが、実戦ではExpressのルーティング作法で細かく普通にやればよく、そうすれば想定外パスへのリクエストはExpressの機能として404が返ります。

Serverless Components (様子見)

github.com

Serverless Frameworkが定義ファイルからCloudFormationテンプレートを生成してクラウド操作するのではなく、定義ファイルからコンポーネントインスタンスが生成されてそれらがAPIを直接操作する仕組みになっていた。これは良いアイディアで良いアーキテクチャだと思うけど、実装として今はまだ未成熟。Serverless Frameworkの先としてうまく融合 or 進化してほしい。

CLIだけでなく コードでデプロイなどの操作を行う機能 も素敵。そのほかそここことなく、Gulpのようなものを目指してるのかなと思った。

Amplify (お蔵入り)

amplify-cliのfunctionやapi機能がTypeScriptにまだ対応していない。また吐き出すScaffoldの構造がちょっと好きじゃないかな。そもそもにExpressでいいじゃないかと思ったのは、AmplifyでAPIを定義する際にCLIが表示する選択肢の中にExpress利用を見つけたからなので、もうちょいTypeScript対応が進めば個人的な見え方は変わると思います。でも今日ではなかった。

LoopBack (お蔵入り)

5年ぐらい前に、自分的には第1期としてNodeに触れていた時期に、チューンドNodeバイナリを提供していたStrongLoopを見つけました。StrongLoopは当時住んでた近所のSan Mateoに会社が所在していたので、そこで開催されたNodeのMeetupに参加するのにオフィスに入ったこともあります。連想的に彼らが作ってたNode上でRESTful-APIを作るCLI/フレームワークとしてLoopBackを触ったことがありましたが当時APIはGo言語で書くことになったのと、モノとして時期尚早感があったので使うことはありませんでした。ニューストピックとしてその後すぐにStrongLoopがIBMに買収されたことまで追いましたが、しばらく忘れていました。Expressを検討する中で懐かしい名前としてStrongLoopとLoopBackがまた出てきた。

ちょうど新しいバージョン4を出す前夜ぐらいなタイミングで、しかもその4はそれまでのコードベースを捨ててTypeScriptで書き直しているということで、俄然興味がでてきます。

v4.loopback.io

CLIで吐き出すのはTypeScriptオンリーでそれは素敵なのですが、フレームワークの作法としてクラスを書いてデコレータでDIするなど昔のJavaフレームワークかのような。製品WEBでGraphQLとかも語ってたのでこれまでの実績捨ててゼロから作り直した動機はこの辺かと期待しましたが。。。分厚い。。。スタックが深いよ、これは。また4はそれまでと異なりExpressベースではなくなっていました。そしてさすがan IBM Company。IBMクラウドを基本に考えているので、サーバレスにはちょっと背を向けてる風な感じ。各クラウドベンダーの独自なマネージドサービスを多用するサーバレスは現時点での競争負け組であるIBMの戦略には合わないみたい。そして、4が出てくるこのタイミングでわざわざExpressベースとはいえ古い3を使うかというと、なんか気が進まない。よってまたLoopBackはそっと閉じて心の隅にしまう。

Tech-on MeetUp#04「APIでつなぐ・つながるFinTecher」

Tech-on MeetUp#04「APIでつなぐ・つながるFinTecher」というイベントに行ってきた。

f:id:masataka_k:20190116005629p:plain
Stripeセッション

KDDIの金融な人たちのホストで、じぶん銀行au WALLETと連携するカブドットコム証券のセッションに、二本立てのようにゲストStripeで、それぞれFintechの入口のところを事例中心に関係者掛け合いの体で盛りだくさんでした。それぞれのコマが30分程度なので掘り下げ浅めで聴きやすくはありましたが、もっと時間をかけて実装的なことも語られるともっとよかった。 じぶん銀行API(Open API+独自API)はとても興味深いが、法人が気合い入れて取り組まねば利用のためのハードルは超えられなさそうという。。。以下は聴きながら取ってたメモの流し込み。

カブドットコム証券、KDDI

  • 株取引関連のAPIを提供し、ARコンセプトモデルを提供した
    • 巷にある商品をスマホカメラで撮影すると、メーカーを識別してそこから株情報を表示する
    • リコノミカル株式会社:17年創業で、AI+AR+IoT
      • 安く、早く。APIを利用して作る
      • 金融 x ARで提案している
    • 実際の商品を起点として株取引が始まるのが実は新しいのではないかと着目した
  • 課題
    • 金融APIがオープンではない
      • イノベーションを起こすような機能利用ケースが想像しにくい
      • 法制上の制限。銀行情報に触れるためには業登録が必要。。。
      • 証券APIは上記どちらもないため、株のAPIは直接的な制限がない
    • インフラとデバイスの問題
      • ARでサービスを作ってもスマートグラスが普及してない、容易にしそうにない
      • UXの進化
    • クラウド
      • それまでのWEB化してフル機能を提供する時代が過ぎる。
      • 機能を細分化して提供する時代、API化、サードパーティによるソリューション提供
        • 金融のコア機能は一つか二つで、その他の内容を組み合わせてサービス創造

じぶん銀行KDDI

  • Open API
    • 17年の改正銀行法。OpenAPIの提供を努力義務...やらねばならない
      • 2年以内に体制を整備する努力義務
    • 改正前はスクレイピングで各種情報を取得する努力を各社が行なっていた
      • 電子決済代行業者
        • 金融機関との契約が重い、やってない。セキュリティなど利用者保護はどうなってるのか?
        • Open APIの登場、しかし審査のある金融機関との契約が必要。果たしてこれでオープンなのか?
          • 反社勢力排除などの必要があった
  • じぶん銀行
    • モバイル特化で330万口座を持つ。KDDIMUFGの協業
    • モバイルでフルバンクサービスを提供している
    • 2018年9月にOpen API提供スタート
      • 残高照会
      • 入出金明細照会
      • AI外貨予測
      • totoキャリーオーバー額照会
      • オンライン口座振込
    • Alexaやマネーフォワード
  • 独自APIを利用したサービス
    • au WALLETとカブドットコムの連携
    • au WALLETプリペイドカード
      • 2000枚以上発行済み
      • 機能:決済・チャージ・参照・送金/払出・口座開設
      • 朝と昼にコンビニで利用される、夕方にスーパーで利用。それぞれ実店舗であり、ECは少ない
      • プリペイドなのに残高不足の心配がない
        • 残高が一定額を下回ると、じぶん銀行の口座から自動チャージ(非同期にチャージ)
        • 決済時に不足金額をリアルタイムチャージする。デビットカードに近い使い勝手になる
          • 同期でチャージと決済を行うので、応答時間が課題。現在は1秒以下で機能提供できている
          • 現在のところは*Payサービスはさほどトランザクションを発生させていない
    • これからのサービス
      • QRコード決済を19年4月に提供開始
      • キャッシュレス化を推し進めようとする日本で、チャージのトランザクションが増加するだろう

Stripe、600株式会社

  • Stripe:クレジットカード周辺の機能をワンストップ&ペーパーレスでAPIサービス提供する
    • Stripeを用いるとクレジットカード情報を直接取り扱わないので、結果としてセキュア
  • 600:無人コンビニ、AWSとStripeを利用している
    • 17年6月創業、18年6月から一般的にサービス展開
      • 冷蔵庫にタブレットが貼ってある
        • クレジットカードをスワイプすると冷蔵庫のドアが開く
        • 欲しいものを取り出すとタブレットに自動集計する。
        • タブレットの購入ボタンを押して、決済完了
    • ユーザーの身近で商品提供する(50m、2分の距離)
  • Fintech/キャッシュレスをビジネスマンとしてどう見るか
    • テクノロジーだけでなく、マインドが必要。しかし最近はマインドが変化してきた
      • 5年前であれば実現できない雰囲気だっただろう
      • 日本はキャッシュレスについて後進国ということはないし、伸び代もある
      • コンプライアンス課題、現金を取り扱うリスクからキャッシュレスへの流れ
      • キャッシュレスの先には決済を意識しなくても良くなっていく。
        • 定額課金など。
        • あらゆるシーンから「決済」は透明化の流れ
      • 銀行以外が付加価値を持つ潮流も、一旦不況下では信頼を担保する銀行が強いか?
  • ハードウェアビジネス
    • ソフトウェアと違って、デプロイサイクルが長く重い
      • 不具合の検出の課題
      • 見つけた不具合を更新する際の課題
    • オンラインとオフラインの境界が曖昧になってきているように、ハードウェアとソフトウェアも融合
      • 自分の専門領域を持ちながら隣接領域にも詳しくなると有利。ハードウェアがその一つだと貴重

年末年始

あけましておめでとうございます。

年末年始の10連休でコード書こうかと思ってましたが、Diablo3 for SwitchNFL game passと積んでた「NEVER LOST AGAIN グーグルマップ誕生 (世界を変えた地図)」を読んで(以上、割合順)過ごしました。残る明日はNFLワイルドカードゲームの現地土曜開催分が未明と午前中にLive放送なのと、17pmまでスプラトゥーン2のフェスなのでほとんど非生産的なまま私の休暇は終わりです。

休み中ちょっとだけReactキャッチアップ。v16.3からContextが変更されてReduxいらないじゃん議論を目にしました。私は新しいContextも含めて以下のように3つ併用した方がキレイだと思います。無理にReduxでなんでもやろうとするとか、Reduxを全く使わないとか、極端にすると余計にテクニックを要して複雑かなと、先入観。

  • Redux: SPA背後のドメインモデルを持つ
  • Context: 複数ビューを必然的にまたぐルーティングとテーマだけを持つか、使わない
  • State: 単一ビューに閉じる画面状態だけを持つ。特にフォーム画面ではフォームの情報を取り扱う。前にライブラリ書いた

しかーし。ReduxをContextに置き換えてみれば山ほどあるReduxお約束コードがシンプルになるメリットがあるかも。しかしここで考察が足りないのは自分的に未経験なGraphQLのSubscribeが入った場合にどうなるか。Redux続行なら多分イベント直結でストアにDispachさせればいいんじゃないなかと思ってるけど、Diable3やっててそこまで手をつけてない。

今日時点ではRedux続行派な私も、数日後には言うこと変わってるかもしれない。ドメインモデルが直感的に書けてテストしやすいままなら、最低限、依存ライブラリをバッサリ絞れるだけでもステキな予感はある。

2018年の振り返り

2018年は色々あった筈なのですが、全体的に印象に乏しい一年でした。米国駐在からの帰任があったのでそれなりにライフイベント満載な筈なのですが。。。単に自分の感受性の問題だろうか?書き出しながらなんとか思い出す。

1月〜4月

2017 to 2018の年越しは日本にいました。4年の駐在を終えて夏に帰任することが決まったために新春帰国してその後の準備を色々するはずだったのです。プライベートではその時期に長女の高校受験というのもありました。長女は12月初めに帰国させて行徳の実家に預け、その後は公立中学校に3学期だけ通わせました。早生まれの彼女は現地での9年生を終了しておらずそのままだと日本では一学年下がってしまうので、日本の中学校に帰国編入して卒業見込みを得ることが必要だったのです。しかもそれは1月に高校入学願書を出す時点で編入していないといけない。帰国子女枠での受験は海外に3年以上住み&帰国から一年以内に進学というのがよくある条件なので、有識者に聞くと夏休みに入ってすぐに母親と子供だけ先に帰って受験準備をするのがベストプラクティスだったようですが、その分せっかくの海外生活経験も短くなります。ウチは頑張ればどうにでもなるだろうとギリギリまで引っ張りました。

私は3ヶ月間都内ホテル住まいで、サイオステクノロジー社内で進めてたサービス開発に支援で関わってサーバーレス on AWSに触れていた他、その先の仕込みでコツコツ古いものを温めたり、新しいことの種まきしたりと。幸いに長女の受験は全勝してくれたので3月中旬には心安らかに米国へ。しかし諸般の事情でこの時点まで帰任先が明確にはなってなかったのです。なんとなく持株会社ではなく事業会社の方に行くことにはなりそうな雰囲気でしたが、それがサイオステクノロジーなのか(そうならばどの事業部なのか)グルージェントなのか、その他のグループ会社なのか決まってなかった。そんな中で暫定的にサービス開発に関わってましたが、結局はそのサービスが管掌される所の技術部門を担当することになりました。そのサービスとはSIOS Coatiで、技術部門はサイオステクノロジー株式会社第一事業部です。確か内示時期はこの長期出張から戻る直前だったような記憶です。

  • この時期に取り組んだコト
    • AWSのマネージドサービス群。CognitoとStepFunctionsが初物で、Lambda中心にDynamoDB、S3、API Gateway、SQS、CloudWatchなど。
    • Serverless Framework。これをキッカケにAWS Cloud Formationを調べたり戻ったり。とても便利でもっと栄えると良いと思う。
    • Vue.js。それまでReactに馴染んでた私でしたが、現場がVue.jsでやりたい声に溢れてたので何でもよかろうと採用。。。が、個人的には失敗だった。Reactの方が良かったと12月の今は心の底から思う。FLUX的なストア構造、ルーティング、コンポーネント毎での描画ユニットテスト、それぞれVue.jsでも論点あるのだと思うけど、Reactの方が一周以上先を走ってる。Vue.jsにはエンジン/フレームワークをTypeScriptで全部書き直すことも構想されているらしい次世代に期待。Anglarは結局食わず嫌いだった。
    • Hugo。Static Site Generatorを一通り評価しつつ、結果としてバイナリを一つ置くだけのシンプルな環境で、コンテンツもGo Templateでサクサク書けたHugoを選ぶ。多分私がGoは書くけどRubyは書かないってことが大きく影響していると思う。これも良いと思う。栄よ!
    • Stripe。必要になってから他の選択は考えもせずに一択。これまでもこれからもAPIが重要。

5月〜6月

サーバレス on AWSでの経験についてAWS Sumitで登壇することになり、プライベートでは不動産巡りも兼ねてまた日本出張二週間。私と長女の通勤通学の便利から目黒線沿線&大型犬飼育可能の条件で、幸いに奥沢に物件見つけて契約。出張から帰るともう米国は残り1ヶ月しかなく、その辺りは引越しの手配とフェアウェルパーティしか覚えてません。

  • この時期に取り組んだコト
    • bashUNIX系なコマンドライン操作で知識があやふやな所が多かったのを必要があって改めてた。forループ、リダイレクト、パイプ、sedなど。応用として正規表現も。シェルスクリプトを書くというよりはコンソールでの対話型に使えるbash知識で、クォートやカッコの使い分けとかこれまでなんとなくイディオムとして使ってたあたりがハッキリした。

7月〜8月

6月末に荷物を出してから船便が届くのに2ヶ月かかった。よって家具もなく床で寝てたしTVも無いし。かろうじて食卓だけは米国に持って行かずに実家に預けてたので、家族全員でほとんどの生活がダイニング。猛暑が続き、毎日家でビール飲んでました。ヤッホー・ブルーイング「インドの青鬼」専門、大好き。

仕事としては、「オマエは技術部門の統括だ、以上!」としかJob Descriptionがない中で、社内観察を行いつつApplication Lifecycle Management(もしくはDevOps環境とでもいうべきか)の統合と刷新を始めています。

  • この時期に取り組んだコト
    • Chatツールの全般評価。Slack、ChatWorks、Yammer、Workplace by FB、Line、Typetalkなど。結果として初めからそうかなと思ってたけどSlackを採用。その後にSlack TeamとTeam PlusとEnterpriseGridを検討し、Team Plusを採用。
    • GitHub Enterpriseの評価。初めはGitHub Businessを評価してたけど、コイツだと事故るな、とすぐに思ってEnterprise。AWS上で何度かインスタンスを作っては壊し、その他コラボツールやSSOを検討していった。
    • SSO、つまりはIdPの全般評価。初めはAuth0で作ろうと思って組んで書いた。社内で面白がって手伝う雰囲気があったら本気でAuth0を使ったアプリケーションを作ってたかもしれない。が、とにかく面倒になってきて古巣にご存知Gluegent Gateがあったからそっちに転向。

9月〜10月

船便が届いてから行ったことはソファーの貼り替え。業者事例ページでは世田谷区を目黒区って間違ってる。結構なお値段するんで色々な素材&色々なカラーの見本を取り寄せては考え、最終的に白の合皮で貼り替え終わるまでたっぷり2ヶ月はかかった。決め手は犬でした。彼が土足で登るから汚れても掃除しやすい合皮で、掃除しやすい合皮だからこそ白にチャレンジできた。最近毎晩、彼が就寝用に占有していますがさほど手間をかけずともキレイに保てています。あとはたくさん本を読んでた気がする。シリーズで続いているものの新刊追ってなかったのをまとめて読んだり。大きな本屋に行って書棚を眺めていると何か見つかる。亡きトム=クランシーのジャック=ライアンものとか、漫画とかも。

仕事面ではALMの展開で社内にSSOされたSlackとGitHub Entepriseの本番環境を作ってIDを段階的に増やしつつ、メインは中期経営計画(3年)とさらなる長期ビジョン(10年)の策定で社内ワークショップをファシリテーションしたり、内容をまとめたり。ITイベントに積極的に参加するようにし始めたのはこの頃で、理由は米国に行くしばらく前からイベントやコミュニティに参加しないようになってたので新しい人脈改めて広がれば良いなとか、業界どうなってるのかリサーチのため。

  • この時期に取り組んだコト
    • スクラム。現場課題にマイクロパッチを充てがう傍で、理屈としてスクラムのベストプラクティスについて書籍やイベント参加などで温め直し。
    • Business Model Canvas。派生のValue Proposition Canvasとともにビジネスモデルの考察と表現に採用するのに温め直し。ワークショップはワールドカフェにBMCやVPCを自己ブレンドしたやり方を試してみましたが、結果よかったんじゃないかなと思ってる。
    • Slackアプリ開発。Slackでまずはスラッシュアプリ(Slackのコメント欄に”/"から始めるコマンドを打つと定められたURLにアクセスして結果をコメント書き出しするアプリ)から。Hello Worldでは超簡単で世のブログでもそこまではよく見るのだけど、セキュアなサードパーティとしての振る舞いを、きちんとトークンを取得し補完するなどの手間を実装するのは、ベストプラクティスが何かは知られていない。で、試行錯誤。サーバーレスでトークンはDynamoDBに補完する仕組み組んでみたけど、多分これでいいんじゃないかなと思う。需要があれば内外に解説します。

11月〜12月

生活環境も流石に落ち着き、土日も特にどこか家族で行くこともなく淡々と。平日に外で飲むことも以前に比べると少なくなって自室で過ごすことが多くなってくる。9月に開幕したNFLで長年応援しているChicago Bearsが今年は強く、NFL GamepassというWEB視聴サービスを購入して熱心に観てます。1試合が2時間半ぐらいなんで、週に3試合から5試合ぐらい観てるとすぐ夜も更ける。特にBearsは冬に勝ち続けてしばらくぶりに地区優勝!大晦日のシーズン最終戦までプレイオフのシード権を巡って期待させてくれて目が離せない。しかも正月からプレイオフでドキドキさせてくれる。

仕事は日常の日米開発体制で少々イシューありつつも、中期で取り組むべき内容について。アウトプットが量的に停滞して来てたので空き時間はコード書くようにしてます。で、React。サーバー側はAWSサーバーレスで、使ったことないものを眺めながらグルグルウロウロしてます。

  • この時期に取り組んだコト
    • TDD。皆に押し付ける前にまず自分から。私が知ってる10年ぐらい前のJavaでのTDD文脈が今でも通じるのかを確かめている。今は当たり前なのかアップデートされた情報が少ない。クラウド時代のTDDとか、サーバーレスとTDDとか、色々ありそうなものだが。。。
    • React。TypeScriptで書くSPAを、create-react-appで。年末になって始めたばかりなのでなんとも言えないがエコシステムが見ない間に大きく進化していて元々の筋も良いと思う。今週はずっとredux-actionsでストアを作ってる。もちろんJestでTDDでね。

年末最後に、Switch版のDiablo3を買っちゃった。。。

2018年の総括

温故知新、ってことです。日本に戻ってそれまで断絶してた知識や関係をアップデートしている。

2019年の抱負

今の所日々の雑たることはたくさんあると思うけど、私が新しいことをやる予算の獲得はままならず手足縛られているので多分ヒマ予測。イベントやコミュニティに参加したりコード書いたりしてキャッチアップを進めることと、後半ぐらいには人前に出て自分の考えや知見を喋れたらと思う。社内需要次第ですが、勉強会やハッカソンなども考えます。手元では、もうTypeScriptでReactとAWSのサーバーレスで可能な範囲だけでいいやって感じ。他に色々手を広げる気力がない。気力あればMLaaSとかいよいよの時期なのだろうけど。コンテナでk8sなマイクロサービスとかも、AWS以外のクラウドも、社内に有識者いるのでそちらにオマカセ。

プライベートには、やはりダイエットが重点課題になるでしょう。

新年もどうぞよろしくお願いいたします。

Reactは15ヶ月前に比べて相当様子が違った

17年9月ごろを最後にReact書いてなかったので、15ヶ月ぐらいのブランクでしたが、再び寝たReactを起こしてみました。すると、なんとまあ、相当様子が違うのです。Cliツールで雛形を作るのはView.jsで経験していましたが、そもそもに元祖だったらしいReactのソレ(create-react-app)がいい感じに良く動く。複雑なWebpackの設定とか、ライブラリスタックのバージョン合わせなどを全くしなくてもOK。素敵。TypeScriptもちゃんと対応。

package.json

まずやるのは、scriptsから事故防止のためにejectを削ります。

その後にだいたい使いそうなライブラリを加えたりしてまずは以下の感じ。せっかくcreate-react-appで依存性をシンプルに見せられているのに@typesの連続で汚れる。TypeScriptの機能としてJSライブラリに型定義がなければ自動的に@typesみてくれるようになればいいのにな。

まだアプリケーションのコード一切書いてないのでライブラリは入れ出しするでしょう。material-ui関連ではバージョンがあがりすぎてて見当がつかないのでまずドキュメント読み直すところからです。最近のお気に入りはramda。関数型「風」に書くのが好きなのでlodashとかより適している。GraphQL with AppSyncは初挑戦。

{
    "scripts": {
        "start": "react-scripts-ts start",
        "build": "react-scripts-ts build",
        "test": "react-scripts-ts test --env=jsdom"
    },
    "dependencies": {
        "@material-ui/core": "^3.7.0",
        "@material-ui/icons": "^3.0.1",
        "aws-amplify": "^1.1.17",
        "aws-amplify-react": "^2.2.4",
        "ramda": "^0.26.1",
        "react": "^16.7.0",
        "react-dom": "^16.7.0",
        "react-redux": "^6.0.0",
        "react-router-dom": "^4.3.1",
        "redux": "^4.0.1",
        "redux-actions": "^2.6.4"
    },
    "devDependencies": {
        "@types/enzyme": "^3.1.15",
        "@types/enzyme-adapter-react-16": "^1.0.3",
        "@types/jest": "^23.3.10",
        "@types/nock": "^9.3.0",
        "@types/node": "^10.12.18",
        "@types/ramda": "^0.25.45",
        "@types/react": "^16.7.17",
        "@types/react-dom": "^16.0.11",
        "@types/react-redux": "^6.0.11",
        "@types/react-router-dom": "^4.3.1",
        "@types/redux-actions": "^2.3.1",
        "enzyme": "^3.8.0",
        "enzyme-adapter-react-16": "^1.7.1",
        "nock": "^10.0.4",
        "react-scripts-ts": "3.1.0",
        "tslint-config-airbnb": "^5.11.1",
        "typescript": "^3.2.2"
    }
}

ここで疑問なのは、react-scripts-tsが自動生成されたものではdependensiesに入ってたこと。これってdevDependenciesでいいんじゃないかなと思うので移しておいて様子見ます。

watchman

create-react-appはJestのテスト環境もすぐ作ってくれますが、テスト実行すると立ち上がったままで変更監視を始めます。すると「ファイルが多すぎ」エラーが出てしまいそのままでは動かなかった。調べるとファイル変更を監視するためにwatchmanというのをインストールしないとダメだとのこと。

$ HOMEBREW_PREFIX=$(brew --prefix) brew install watchman

HomeBrewで淹れますが、単に「$ brew install watchman」だと入れたwatchmanがアクセス権の問題で動きませんので、上記のようにPREFIX環境変数を設定します。これは長時間ハマりました。

テスト周辺

import * as React from 'react';
import { configure, shallow } from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';

import App from '../App';

describe('App', () => {
    beforeAll(() => {
        configure({ adapter: new Adapter() });
    });

    test('renders without crashing', () => {
        const wrapper = shallow(<App/>); // mountでもyarn testならOK。IDEからの直接起動はNG
        expect(wrapper.contains(<h1 className="App-title">Welcome to React</h1>)).toBeTruthy();
    });
});

デフォで書き出されてたテストをEnzymeバージョンにして動作確認。Dom不要のshallowはいつでも動くが、mountを使ったテストは単独でGoLand(IDEはWebStormもCLionもやめてGoLandにしました)から実行できない。

f:id:masataka_k:20181223223241p:plain
GoLand(他、JetBrainsのIDE)の個別Jest実行設定画面

こちらのように、Jestのオプション設定で「--env=jsdom」を書いてあげればOK。yarn testの実行時でもreact-scripts-ts testの引数として書かれてます。jestの設定で指定する方法をやってみると以下のエラーで、ejectしろと言ってくるので、とりあえず先のIDE設定で回避します。

Out of the box, Create React App only supports overriding these Jest options:

  • collectCoverageFrom
  • coverageReporters
  • coverageThreshold
  • snapshotSerializers
  • moduleNameMapper.

These options in your package.json Jest configuration are not currently supported by Create React App:

  • testEnvironment

If you wish to override other Jest options, you need to eject from the default setup. You can do so by running npm run eject but remember that this is a one-way operation. You may also file an issue with Create React App to discuss supporting more options out of the box.

しかしEnzymeは前からこんなに簡単に書けたかな?Reactノードを引数にとるのも、containsってのも記憶にない。。。これはView.jsに替えてReactを選ぶ強力な説得材料になるかも。jsx/tsxでテストが書けるのでDom断片を気軽に使える=ユニットテストが楽しい。nock使っての通信のところもフックすると、ユニットテストの延長線上でE2Eに近いことがさほど手数多く無く書けそう。

HMR環境

create-react-appのデフォではHMR環境が組まれてないのですが、まあ、無くてもいいかな。画面見ながら作るよりUIもTDDで書くようにしたら実際に動かす必要は常の事ではないので。トリッキーにアプリのブートストラップコードのところで細工しないといけないのが改善されていないのであればwebpackの設定をこじ開けるなど無理するのも嫌なのでHMRは入れなくていいや。

NoOps Meetup Tokyo #3に行ってきた

noops.connpass.com

さて、久しぶりにソフトバンク本社へ。サイオスビルの入口前にある古川橋バス停から新橋行きに乗って新橋五丁目で降りてちょっと歩く、G-Suiteでオシッコのひっかけ合いしてたときには何度も行き来して慣れた道のり約30分。今日の目的、NoOps Meetupに参加してきました。イベントには直前水曜日に登録したのではじめ補欠だったのだけど、今日になって朝からどんどん繰り上がって昼すぎには参加できるようになりました。某からリクエストもあったのでセッションのメモを貼っておきます。メモは今回も時系列ではなく聴きながら内容で上下整理しながらまとめたもの。

西谷さん from AWS

  • Opsは悪なのか?
    • Undifferentiated Heavy Lifting (本質ではない作業)
    • プロダクトを差別化するのはソフトウェア。その他のセットアップなどの作業をなくす
      • コードをなるべく書く環境を作る
    • 単一目的のサービス、HTTPSAPIのみによる通信、お互いをブラックボックス
      • -> Microservice/DevOps
  • Amazon流のサービス開発。Two-Pizza Teams
    • 2枚のピザで足りるような、少数精鋭チームが効果的
    • 作るもの全てに責任をもつ。
      • ロードマップの作成
      • 開発
      • 運用とカスタマーサポート
      • You Build it, you run it.
      • QAもオンコールもチームの中でやる
      • チームには権限が与えられていて、多くの自由が認められている
    • チームの水準を高く保つ
      • Hiringも各チームで行う。ヘッドカウントを持っている。
    • テストで大事なこと。
    • 運用で大事なこと。
      • Automate Everything! 自動化が重要。
      • 運用ゼロは幻想。NoOpsは難しくとも、LessOpsはできる。
      • LessOpsはServerlessでできる。
  • Our Leadership Principles、Amazonクレド
    • Customer Obsession
      • 全て、お客様から。単純な顧客第一主義には止まらない。
    • Ownership
      • 長期的な視野
      • DevOpsをやるのに大事なことがオーナーシップ。
    • Think Big
      • お客様に貢献するために、従来と異なる新たな視点を持つ。
    • Insist on the Highest Standard
      • 常に高い水準を目指す
      • Code Reviewをちゃんと行う
    • Deliver Result
      • 結果をだす。そのために重要なことにフォーカスする。
      • イテレーションを重視して、貯め込まずに早めにレビューを繰り返していく
      • Minimum Viable Product
      • お客様と一緒に製品を作る

牛尾さん from MS、Serverlessの自動化と自動回復(リトライ)

  • https://github.com/TsuyoshiUshio/MdTranslator
  • Serverlessとは
    • サーバー抽象化、イベント駆動、スケールアップ。
    • イベント -> トリガー -> インプットバインディング -> コードに書かれた内容 -> アウトプットバインディング
  • ユニットテストをとにかくちゃんとやる。
  • テスト可能な設計のためのTips
    • Functionにはインとアウトのバインディングのモックさえちゃんと作ればテストしやすい
      • ネットワークコールやストレージ呼び出しなどもモックで!
      • コラボレーターとの接点はモック、テスト対象がInterfaceを切ってればそれを自動で実装するライブラリがある。Interfaceを切ってなければWrapper経由の呼び出しにする
      • 他人のコードはテストしなくて良い
    • Functionの本体にはなるべくコードを書かない
      • テストがしやすいドメインオブジェクトにしてFunctionの外に出す
  • リトライ
    • APIの制限などで正常系がちゃんと動かないのが普通
    • 単一責務にすると、テストも単純になるし、自動リトライができるようになる
    • 冪等性を守るとリトライが楽にできる。
    • リトライ単位でのFunction設計が望ましい。

そのほか

  • NoOps = No Uncomfortable Ops
  • Toilを潰す
  • node-red: nodeで動くIoT向けフローベース。

感想

牛尾さんのユニットテストをしっかり書くというのは私も普段からの信条なのでとても参考になりました。テスト書くためにアーキテクチャを磨く、そしてテストTipsは、これからテスト書くときに勇気になる。これでいいんだって。

私がAWS Lambdaでポツポツ作る時は、Jestの高度なexpect機能とnockで手数なくできてる。特にnockが強烈。けど、DynamoDBのところはテストしてない。ここもServerless.comのオフラインプラグインを使うか、関数を引数に渡す&Jestモック機能か?

週末にちょっとトライしてみようと思う。