Node.js on AWS Lambda の初期化処理とDIとコード分割について
2020/08/30 #AWS #AWS_Lambda #Node.js
これまである程度ベストプラクティスが公式に提示されたフレームワークの上で書いてきたので、いざハンドラの中身は自由にどうぞと言われるとなかなか難しい。
フレームワークを導入するほどではないが真面目にメンテしていきたいので妥当なコード分割で良い感じにシュッと書きたい。
以下の2つが満たされていると良い。
コールドスタート時の初期化処理を1つの関数にまとめる (トップレベルに処理を書かない)
メインハンドラ全体をユニットテスト可能にする (ちゃんとDIしたい)
で、最低限こんな感じにすると良いのではと思う。
code:init.js
export async function init() {
// 初期化
return { hoge, fuga }
}
code:main.js
export async function main(event, context, { hoge, fuga }) {
// メインの処理
}
code:index.js
import { init } from './init'
import { main } from './main'
let deps;
export async function handler(event, context) {
if (!deps) {
deps = await init();
}
return await main(event, context, deps);
}
コールドスタート時の初期化処理を1つの関数にまとめる
初期化処理をトップレベルに直接書くのは避ける。主な理由は
非同期処理のawaitが難しい (現時点でAWS LambdaのNode.jsランタイムはv12系でtop-level awaitに非対応)
初期化処理のどこかが失敗した場合に次のウォームスタートで再実行できない
import時に動く初期化処理がテストの邪魔になる
メインハンドラ全体をテスト可能にしておく
最低限のテストとして入力イベントに対する最終的な出力を担保したいので、主にmain.jsに対してテストを書く。
インフラに依存した処理は全部モックする必要があるのでファイルごと分離する。例えばAWS.DynamoDBのようなSDKのサービスクライアントの初期化はinit関数に書く。カプセル化したインターフェースまたは関数を返して、最終的にmain関数の引数へ渡す。
要するにちゃんとDependency Injectionすればテストも書きやすいよねという話で、メインの処理に対してどこでDependencyを初期化してどうInjectionするかを決める。あとは他のフレームワークと変わらない考え方で書けそう。
参考リンク
AWS Lambda 関数を使用する際のベストプラクティス - AWS Lambda
Lambda を使用する際のベストプラクティス - Amazon DynamoDB
Async Initialisation of a Lambda Handler | Serverless First