新規プロジェクトを任されることになり技術選定から一人で行いました。
新しい技術には常に関心があったので、理想とするモダンな構成を以前から検討していました。直近 3 年間では Node.js と Ruby をメインに使ってきましたが、動的言語の欠点としてよく言われるのが「結局のところテストケースで型チェックのコードが大量に必要になるため、実装コード量は静的言語よりも多くなってしまい、プロダクトコードを書くには全然楽じゃない」というものです。常々感じていました。
また Ruby はパフォーマンスがさほど高くなく、経験者が多いという以外では 2018 年に選択する理由が思い浮かびません。
TypeScript の評判の良さは前から知っていましたが、実際に使ってみると型定義ができるというだけで JavaScript 特有の暗黙の型変換を未然に防ぐことができ、テストも本質的なコードを書くだけでよくなりました。副次的に IDE のコード補完精度があがり、気持ちよく開発できるようになりました。
Web フレームワークには Nest.js、データベースの O/R Mapper には TypeORM を採用しました。日本語情報はほぼありませんが、開発規模が大きく、特に Nest.js はフルタイムで開発している方がいて Issue をあげたときのレスポンスも早かったので信頼できると判断しました。
フルスタックフレームワークなので学習コストは高かったのですが、新規プロジェクトでは WebSocket や gRPC を使いたいという話もあがっていたので、そのどちらのモジュールも存在する Nest.js にすることで、拡張性と次期プロジェクトを見据えた将来性の面で理がありました。
前プロジェクトから Node.js は使われていましたが、基盤と呼べるようなコードはなく、適切に動いてテストがあれば良いという状態でしたので、新プロジェクトでは基盤化することにも注力しました。
まず後から参加してくるエンジニアが迷わないように参考となるコードを複数機能にわたって充分に実装し、テストでは Fixture を読み込んだり、リセットする仕組み。DI 部分をモック化する仕組み。DB マイグレーションの仕組み。同時にドキュメント化もしました。
さらにプログラムは Docker でコンテナベースにし、GitHub に push するだけで、AWS CodePipeline と CodeBuild を通じて ECS (Fargate) に自動デプロイする仕組みを作りました。
インフラについても Terraform で IaC 化して 3 分で作成・破棄ができる状態になっています。モジュール化しているため、次期プロジェクトでも同様の構成であればそのまま使うことができます。AWS コンソールから手作業で行うとひとつの環境を作るのに 3〜4 時間かかっていたので、かなりの効率化が達成できました。
プロジェクト後期になってチームに追加メンバーが入ってからは一部機能をマイクロサービス化することが決まり、現在はメインの API サーバとマイクロサービス間で gRPC でインターナル通信して機能を呼び出す形になっています。Node.js で gRPC を使う例はあまり聞きませんが、これも仕組みを整え、Protobuf のコード生成スクリプトを用意したりと、別のマイクロサービスを別のメンバーが作ることになっても「このプロジェクトをベースに作ればいいです」と言える状態まで揃えました。
サービスの実装と基盤化には概ね 3 ヶ月くらいでした。そしてメンバーが増え、さらに1ヶ月後に完成となり全世界リリースしました。