こちらの記事は2021年度の「株式会社カケハシ x TypeScript」アドベントカレンダーの14日目の記事になります。
TypeScriptをバックエンドとし、コンテナ環境にAPIサーバーを構築するようなシステム構成を実現するためにフレームワークの導入を検討しました。ExpressやFastifyはHttpサーバーとしてのルーティング機能の最低限の構成が存在しています。個人的な印象ですが、簡易なアプリケーションの開発においてはこれらは十分な機構が揃っている印象があります。Http層(俗にController層)を薄く作って、FaaSを乗せるような場合に一定の妥当性がありそうです。フルスタックフレームワークのfrourioは一見機能面が多そうにですが、様々なライブラリを組み合わせるコーディネータにように見えました。
上記はドキュメントとソースコードを見た印象です。個人的にフレームワークに期待する機能が足りない印象でした。
実際に手を動かして検証を行ったわけではないので厳密には異を唱える方もいらっしゃるかもしれませんが、お手柔らかに。
フレームワークに備わって欲しかった機能
- ルーティング
- DIコンテナ
- 例外ハンドラ
- ライフライクル管理
- 各種ライブラリとのインテグレーション
- 型安全性(TypeScriptを利用する場合)
- バンドラ
上記の機能をバランスよく満たすフレームワークがNestJsでした。特に、DIコンテナは依存関係逆転の原則によるレイヤー構造の分割の実現のため優先度を高く設けていました。ある程度中規模以上のシステム規模になりそうだったことやドメインロジックの複雑さが予想できました。
また、GraphQLのインテグレーションのサポートと包括的なドキュメントも決めての1つでした。
NestJsはExpressやFastify、Apollo Serverのアダプタを導入することで動作していますが、NestJsがそれらの抽象レイヤを用意していて、抽象化されたモジュールを呼び出すことで具象技術への依存度を下げています。プラグインアーキテクチャ(Nest.jsがそう言っているかは分からない)で活用するユーザーは疎結合にライブラリを組み替えられるようにしています。
また、ユーザーコミュニティの大きさや包括的なドキュメントを考えると今後もしばらくメンテナンスがされることが期待できます。
意識的にフレームワークと向き合っていること
フレームワークと心中したくないので、フレームワークとなるべく疎結合に保つようにしています。なるべく依存関係はController(GraphQLの場合はResolver)とAppModule(アプリケーションの設定)に局所化する工夫をしています。ただ、DIコンテナだけは各モジュールで活用していますし、一部でNest.js Wayな開発が余儀なくされます。アーキテクチャ構築時点で潔癖になりすぎても得られることが少ないなので妥協点としていますが、InversifyJSなどスタンドアロンなDIコンテナライブラリは代替候補です。
NestJsの懸念事項
fastifyほどNestJsはスループットが出ないと言われていますが、 プロジェクトでは過度に即応性を求めるアーキテクチャ観点はなかったので気に留めるほどではありませんでした。nest-fastify
等の代替案はあります。
FYI: TechEmpowerによるベンチマーク
NestJsで使っていない機能
簡易なCRUD構成をコード生成する機構がありますが、 意図したモジュール構成にできないためそのままは利用していません。
モジュール構成をどうしたか?
ドメイン駆動設計の戦術的なモジュール構成としてクリーンアーキテクチャの構成を作りました。また別の機会にサンプルコードと共に紹介できればと思います。