Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Node.jsアプリの開発をモダン化するために取り組んできたこと

5,570 views

Published on

Node学園祭 2018 登壇資料

Published in: Engineering
  • Be the first to comment

Node.jsアプリの開発をモダン化するために取り組んできたこと

  1. 1. ビットバンク株式会社 Node.jsアプリの開発をモダン化するために取り組んできたこと @d-yokoi 東京Node学園祭2018
  2. 2. Copyright © bitbank, inc. 自己紹介 ❏ ビットバンクでサーバーサイドを担当 ❏ Node.js, TypeScript ❏ 前職ではモバイルゲームを開発 ❏ C++, C#, PHP, etc. Daiki Yokoi
  3. 3. Copyright © bitbank, inc. テーマとアジェンダ 各所をモダン化して組織の拡大に耐える ❏ ソースコード編 ❏ TypeScript + Nestフレームワークの採用 ❏ ユニットテスト環境編 ❏ Docker + モックツールの活用 ❏ インフラ(AWS)編 ❏ Infrastructure as Code による サーバーレス環境の構築
  4. 4. Copyright © bitbank, inc. テーマとアジェンダ 各所をモダン化して組織の拡大に耐える ❏ ソースコード編 ❏ TypeScript + Nestフレームワークの採用 ❏ ユニットテスト環境編 ❏ Docker + モックツールの活用 ❏ インフラ(AWS)編 ❏ Infrastructure as Code による サーバーレス環境の構築
  5. 5. Copyright © bitbank, inc. ❏ 既存のアプリは Node.js (v6) + Express という構成 ❏ データ構造がわかりにくく、可読性が低い ❏ 新規メンバーのキャッチアップコストが高い ❏ TypeScript化のメリット ❏ コンパイラによる静的なチェック ❏ IDEによる強力なサポート ❏ フロントエンドチームとの言語共通化 素のNode.jsからTypeScriptへ ソースコード編 ▶ 概要
  6. 6. Copyright © bitbank, inc. TypeScript化と同時により効率的な開発手法を模索 ❏ DIの導入 ❏ 依存関係を強く意識してよりテスタブルな構成に ❏ ルーティング効率化 ❏ ハンドラの登録を自動化できないか ❏ APIドキュメント ❏ スプレッドシートでの手動管理から脱却 ❏ なるべくコストをかけずSwaggerUIに繋ぎこみたい ソースコード編 ▶ 概要
  7. 7. Copyright © bitbank, inc. ❏ InversifyJS [1] ❏ IoCコンテナ ❏ routing-controllers [2] ❏ デコレータでハンドラを登録できる ❏ tsoa [3] ❏ APIドキュメントの自動生成 [1] http://inversify.io/ [2] https://github.com/typestack/routing-controllers [3] https://github.com/lukeautry/tsoa いくつかのツールを試してみた ソースコード編 ▶ 概要
  8. 8. Copyright © bitbank, inc. これらが統合されたフレームワークが欲しくなった ❏ 各ツールの守備範囲が微妙に噛み合わない ❏ ツールごとの前提条件や制約もあり、まとめ上げるのが少し面倒 ソースコード編 ▶ 概要
  9. 9. Copyright © bitbank, inc. Nestを使い始めるまで そこで見つけたのがNest
  10. 10. Copyright © bitbank, inc. Nestを使ってみることに ❏ もともと欲しかった機能が一通り揃っていた ❏ オリジナルのDIコンテナ ❏ デコレータでのハンドラ登録 ❏ APIドキュメントの生成 ❏ ポピュラーになりそう ❏ 日本語情報が全くない中、GitHubのスターは5000超え (2018/3時点) ❏ Angularのカンファレンスにも登場 ソースコード編 ▶ 概要
  11. 11. Copyright © bitbank, inc. ❏ 簡単に言うとクラスの集まり ❏ 例)UserModule = UserController + UserService + … ❏ 関係性の強いクラスでまとめるべき ❏ 複数のモジュールがツリー状になって一つのアプリを構成 ❏ 頂点のモジュールは ApplicationModule と命名されるのが一般的 フレームワーク独自のモジュールという概念がある ソースコード編 ▶ Nest入門
  12. 12. Copyright © bitbank, inc. モジュールツリーのイメージ ソースコード編 ▶ Nest入門
  13. 13. Copyright © bitbank, inc. ❏ コントローラー ❏ 各APIのリクエストハンドラを持つクラス ❏ リクエストとレスポンスを扱い、ロジックは別クラスに任せる ❏ プロバイダ ❏ コントローラー以外のクラス ❏ ロジックを持ち、コントローラーや別のプロバイダから呼び出される モジュール内の各クラスは以下の2つに分類される ソースコード編 ▶ Nest入門
  14. 14. Copyright © bitbank, inc. コントローラーではデコレータでハンドラを登録する @Controller(‘users’) class UserController { @Get(‘:id’) get(@Param(‘id’) id: string) { // GET /users/1 等をハンドリングする } @Post() create(@Body() body: CreateUserRequest) { // POST /users をハンドリングする } } ソースコード編 ▶ Nest入門
  15. 15. Copyright © bitbank, inc. プロバイダは各種ビジネスロジックを担当 UserModule UserController UserService UserDetailService SessionService Controllers Providers ソースコード編 ▶ Nest入門
  16. 16. Copyright © bitbank, inc. 依存関係はコンストラクタの実装を元に解決される @Controller(‘users’) class UserController { constructor(private readonly userService: UserService) {} // 略 } --- @Injectable() class UserService { constructor(private readonly userDetailService: UserDetailService) {} // 略 } --- class UserDetailService { ソースコード編 ▶ Nest入門
  17. 17. Copyright © bitbank, inc. ❏ ツリー状のモジュール群がアプリケーションを構成 ❏ トップのモジュールは Application Module と命名する ❏ モジュールは関係性の強いクラスの集合 ❏ コントローラークラス => リクエストハンドラ ❏ プロバイダクラス => ビジネスロジック ❏ クラス間の依存関係はコンストラクタの実装を元に解決される ❏ モジュールをまたぐクラス間の依存関係の解決手段もある ここまでのおさらい ソースコード編 ▶ Nest入門
  18. 18. Copyright © bitbank, inc. @Module({ imports: [UserModule] }) export class ApplicationModule {} --- import { NestFactory } from '@nestjs/core'; import { ApplicationModule } from './app.module.ts'; async function bootstrap() { const app = await NestFactory.create(ApplicationModule); await app.listen(3000); } bootstrap(); モジュールが作成できたらサーバーを起動できる ツリーのトップのモジュールを渡す ソースコード編 ▶ Nest入門
  19. 19. Copyright © bitbank, inc. ❏ コントローラの実装を解析してドキュメントを生成する ❏ 必要に応じてデコレータで追加の情報を与えられる APIドキュメントを自動で作成し、SwaggerUIに連携 ソースコード編 ▶ Nestレシピ集
  20. 20. Copyright © bitbank, inc. ロギングやレスポンスの整形に便利なインターセプタ ❏ ハンドラの実行前後に処理を差し込むことができる ❏ IDを振ってリクエスト開始時と終了時のログを紐づけることができる ❏ レスポンスにメタデータを追加したりするのにも便利 ❏ いわゆるアスペクト指向プログラミングをサポートしてくれる ソースコード編 ▶ Nestレシピ集
  21. 21. Copyright © bitbank, inc. @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(contexct: ExecutionContext, call$: Observable<any>): Observable<any> { const requestId = uuid.v4(); console.log(`[Before] requestId: ${requestId}`); return call$.pipe( tap(() => console.log(`[After] requestId: ${requestId}`) ), ); } } ロギングやレスポンスの整形に便利なインターセプタ 一つのメソッド内で 前後の処理を記述できる ソースコード編 ▶ Nestレシピ集
  22. 22. Copyright © bitbank, inc. リクエストパラメータと同時に定義するバリデーション class CreateUserRequest { @Length(10, 100) @IsEmail() readonly mail!: string; } @Controller(‘users’) class UserController { @Post() create(@Body() body: CreateUserRequest) { // 新規ユーザーを作成 } } この時点では以下が全て完了している - リクエストボティのパース - 指定したクラスへの変換 - バリデーションの実行 各バリデーションをデコレータで定義 - class-validatorが提供するもの - カスタマイズも可能 ソースコード編 ▶ Nestレシピ集
  23. 23. Copyright © bitbank, inc. ガードによってアクセスコントロールを実現 ❏ ガードは不正なリクエストを弾くための仕組み ❏ canActivateというインターフェースが用意されている ❏ リクエストデータを受けてboolean値を返すメソッドを実装するだけ ❏ 登録されたハンドラの実行前に評価される ❏ ビットバンクでの事例 ❏ 起動時にNestの全モジュールをスキャンしてAPI一覧を動的に取得 ❏ これを最新のマスターデータとして各ユーザーに紐付ける ❏ ガード内で呼び出そうとしているAPIの権限があるかをチェック ソースコード編 ▶ Nestレシピ集
  24. 24. Copyright © bitbank, inc. ❏ TypeORMはポピュラーな O/R Mapper ❏ 基本的なユースケースはほぼカバーされており開発効率が上がる ❏ トランザクションやパーティショニングが絡むと少し手間だが許容範囲 ❏ どうにもならなければSQLを直接書くこともできる ❏ マイグレーションの仕組みも用意されている ❏ 開発コミュニティも活発 TypeORMでのデータベースアクセスをサポート ソースコード編 ▶ Nestレシピ集
  25. 25. Copyright © bitbank, inc. ❏ @nestjs/typeorm パッケージ ❏ TypeORMのリポジトリクラス等を他のクラスと同じように簡単にDI できる TypeORMでのデータベースアクセスをサポート ソースコード編 ▶ Nestレシピ集
  26. 26. Copyright © bitbank, inc. https://tech.bitbank.cc/typeorm-entity-guideline/ ソースコード編 ▶ Nestレシピ集
  27. 27. Copyright © bitbank, inc. その他 ❏ @nestjs/testing パッケージ ❏ モジュール内の一部のクラスをモックに差し替えることができる ❏ 依存先が多いクラスをテストする際に便利 ❏ コントローラー部分はREST以外にも対応可能 ❏ GraphQL, WebSocket, gRPC, etc. ❏ MVCにも対応可能 ソースコード編 ▶ Nestレシピ集
  28. 28. Copyright © bitbank, inc. TypeScript + Nest + TypeORM で開発を加速 ソースコード編 ▶ まとめ
  29. 29. Copyright © bitbank, inc. テーマとアジェンダ 各所をモダン化して組織の拡大に耐える ❏ ソースコード編 ❏ TypeScript + Nestフレームワークの採用 ❏ ユニットテスト環境編 ❏ Docker + モックツールの活用 ❏ インフラ(AWS)編 ❏ Infrastructure as Code によるサーバーレス環境の構築
  30. 30. Copyright © bitbank, inc. 当時のユニットテスト環境と課題 ❏ ローカルにインストールしたMySQLやRedisを利用 ❏ バージョンや設定が人によって異なる ❏ 人によってユニットテストが通ったり失敗したりする ❏ git管理されていない内容なのでコミュニケーションコストが発生する ❏ AWSの認証情報を使って直接AWSのサービスを利用 ❏ 認証情報を全員が持たないといけない ❏ 他の人が認証情報を更新するとユニットテストが失敗するようになる ❏ 専用のS3バケットやKinesisストリームを用意する必要がある ユニットテスト環境編 ▶ 概要
  31. 31. Copyright © bitbank, inc. 再現性が高く外部に依存しない構成へ ❏ 可能なものはコンテナを使用 ❏ MySQL ❏ Redis ❏ LocalStack(AWS S3, Kinesis, etc.) ❏ その他はテストツールでモッキング ❏ Jest ❏ MockDate ユニットテスト環境編 ▶ 概要
  32. 32. Copyright © bitbank, inc. ユニットテストでのDockerの利用 ❏ Docker Compose ❏ 単一ホスト上でのオーケストレーションツール ❏ 使用したいコンテナの情報をYAMLで記述 ❏ Dockerに馴染みがない場合でもキャッチアップしやすい ❏ CI連携 ❏ Dockerコンテナをテストに利用できるサービスが多く親和性が高い ユニットテスト環境編 ▶ Docker
  33. 33. Copyright © bitbank, inc. docker-compose.yml サンプル ユニットテスト環境編 ▶ Docker
  34. 34. Copyright © bitbank, inc. テストツールも刷新し、モックを積極活用 ユニットテスト環境編 ▶ モックツール ❏ Jest ❏ Facebook製のテスティングフレームワーク ❏ デフォルトでマルチプロセスで処理するので高速 ❏ チェック用のメソッドやモック機能が豊富 ❏ MockDate ❏ Dateをモック化するための軽量パッケージ
  35. 35. Copyright © bitbank, inc. ❏ AWS StepFunctions の実行ステータスチェックをモック化 インスタンスのメソッドをモック化するサンプル ユニットテスト環境編 ▶ モックツール
  36. 36. Copyright © bitbank, inc. ❏ Axios によるGETリクエストの呼び出しをモック化 依存モジュールをモック化するサンプル ユニットテスト環境編 ▶ モックツール
  37. 37. Copyright © bitbank, inc. Dateをモック化するサンプル ユニットテスト環境編 ▶ モックツール
  38. 38. Copyright © bitbank, inc. Docker + モックツールでユニットテストも効率化 ユニットテスト環境編 ▶ まとめ
  39. 39. Copyright © bitbank, inc. テーマとアジェンダ 各所をモダン化して組織の拡大に耐える ❏ ソースコード編 ❏ TypeScript + Nestフレームワークの採用 ❏ ユニットテスト環境編 ❏ Docker + モックツールの活用 ❏ インフラ(AWS)編 ❏ Infrastructure as Code によるサーバーレス環境の構築
  40. 40. Copyright © bitbank, inc. ポイント インフラ(AWS)編 ▶ 概要 ❏ サーバーレス環境の探求 ❏ Elastic Beanstalk から ECS Fargate への移行 ❏ Infrastructure as Code の実践 ❏ インフラ構成を CloudFormation / SAM で積極的にコード化
  41. 41. Copyright © bitbank, inc. 当初は Elastic Beanstalk + Docker での運用がメイン インフラ(AWS)編 ▶ サーバーレス環境の探求
  42. 42. Copyright © bitbank, inc. Elastic Beanstalk の運用 ❏ Good ❏ とにかく簡単にアプリケーションの実行環境を用意できる ❏ イミュータブルなデプロイが可能 ❏ EC2インスタンス削除時のオートリカバリー ❏ Bad ❏ EC2の管理コスト ❏ 色んなことを裏側でやってくれる反面、柔軟にコントロールできない ❏ アプリのアーカイブが1000件を越えるとデプロイに失敗する インフラ(AWS)編 ▶ サーバーレス環境の探求
  43. 43. Copyright © bitbank, inc. Elastic Beanstalk の運用 ❏ 合うケース ❏ インフラに割くことができるリソースが少ない ❏ あれこれ設定するよりも、AWSが提案する型に乗っかりたい ❏ 合わないケース ❏ インフラを重点的に見れる人がいる ❏ より柔軟にインフラ構築していきたい インフラ(AWS)編 ▶ サーバーレス環境の探求
  44. 44. Copyright © bitbank, inc.
  45. 45. Copyright © bitbank, inc. Fargate 概要 ❏ フルマネージド ❏ EC2(OS, Docker Agent, ECS Agent, etc.)の管理から脱却 ❏ スケーラブル ❏ シームレスにスケールアップ・アウト可能 ❏ 使用したリソースに対してのみ課金(秒単位) ❏ 他のAWSサービスとのインテグレーションが容易 ❏ VPC, ELB, IAM, Cloud Watch Logs, etc. インフラ(AWS)編 ▶ サーバーレス環境の探求
  46. 46. Copyright © bitbank, inc. ❏ OSや各エージェントにパッチを当てる ❏ ディスクの逼迫を防ぐためのログローテーション コンテナ環境でも引き続きEC2環境の管理が必要 インフラ(AWS)編 ▶ サーバーレス環境の探求
  47. 47. Copyright © bitbank, inc. インフラ(AWS)編 ▶ サーバーレス環境の探求 ❏ 開発者はアプリケーションとコンテナを作ることに集中 ❏ インフラの管理は全面的にAWSにお任せする Fargateでより効果的な役割分担が可能に
  48. 48. Copyright © bitbank, inc. ECSの構成要素について インフラ(AWS)編 ▶ サーバーレス環境の探求 ❏ ECS Task ❏ アプリケーションの実行単位 ❏ 1つ又は複数のコンテナで構成される ❏ ECS Service ❏ 紐づけられた1つの ECS Task の実行数を制御 ❏ ECS Cluster ❏ インフラやセキュリティの分割単位 ❏ 1つ又は複数の ECS Service で構成される
  49. 49. Copyright © bitbank, inc. Fargateのスケーリング インフラ(AWS)編 ▶ サーバーレス環境の探求 ❏ ECS Task ❏ 必要なコンピューティングリソース(CPU/Memory)を設定 ❏ コンテナが複数存在する場合は上記がシェアされる ❏ リソースが枯渇した場合は即座に停止されるのでバッファが必要 ❏ ECS Service ❏ Taskの実行数を設定してロードバランサーと連携
  50. 50. Copyright © bitbank, inc. Fargateのスケーリング インフラ(AWS)編 ▶ サーバーレス環境の探求
  51. 51. Copyright © bitbank, inc. Elastic Beanstalk からエンドポイント単位で移行 インフラ(AWS)編 ▶ サーバーレス環境の探求
  52. 52. Copyright © bitbank, inc. ❏ ドキュメンテーションコストの増加 ❏ 手動で構築するとより詳細なドキュメントを残す必要がある ❏ アプリケーション数に比例してドキュメンテーションコストも増加 ❏ 権限管理を厳格化した副作用 ❏ 権限を持った人が手動で構築するのを待つ必要がある AWSコンソールのみでの運用の限界 インフラ(AWS)編 ▶ Infrastructure as Code の実践
  53. 53. Copyright © bitbank, inc. ❏ CloudFormation ❏ Serverless Application Model (SAM) ❏ CloudFormation の拡張命令セット ❏ Lambda とその周辺コンポーネントをより簡潔に表現できる AWSのインフラをコード化する インフラ(AWS)編 ▶ Infrastructure as Code の実践
  54. 54. Copyright © bitbank, inc. ❏ コンポーネントの構成を視覚化できる ❏ ドラッグ&ドロップによるリソース追加や依存関係の構築 ❏ 各リソースのプロパティ名をオートコンプリート (Ctrl + Space) Tip1: CloudFormation Designer を使ってみる インフラ(AWS)編 ▶ Infrastructure as Code の実践
  55. 55. Copyright © bitbank, inc. ❏ 一度に大量に更新すると、失敗した際のロールバックが長い ❏ git でのバージョン管理と相性が良くなる Tip2: 少しずつ記述してデプロイする インフラ(AWS)編 ▶ Infrastructure as Code の実践
  56. 56. Copyright © bitbank, inc. ❏ そのリソースはどんなプロパティを持つのか ❏ コンソールでは意識せずに使ってきたプロパティもこの機会に確認 ❏ そのリソースにはどんなアクションが紐付くのか ❏ 最小限の IAM Policy を作成する際に必要になる ❏ そのリソースに対する Ref 組み込み関数が何を返すのか ❏ ARN の場合もあれば ID の場合もある ❏ 必要に応じて GetAtt 組み込み関数と使い分ける ❏ リソース間に依存関係を持たせる場合に確認する必要がある Tip3: リソースを見る観点を理解する インフラ(AWS)編 ▶ Infrastructure as Code の実践
  57. 57. Copyright © bitbank, inc. ❏ 再作成されるリソースの有無は特に注意して確認する ❏ CLIの deploy コマンド実行前に ChangeSet を作成する ❏ --no-execute-change-set オプションをつける ❏ もしくは create-change-set コマンドで代替 ❏ describe-change-set コマンドで差分を表示する Tip4: デプロイ前に変更内容を確認する インフラ(AWS)編 ▶ Infrastructure as Code の実践
  58. 58. Copyright © bitbank, inc. Serverless + IaC でインフラ構築も高速化 インフラ(AWS)編 ▶ まとめ
  59. 59. Copyright © bitbank, inc. テーマとアジェンダ 各所をモダン化して組織の拡大に耐える ❏ ソースコード編 ❏ TypeScript + Nestフレームワークの採用 ❏ ユニットテスト環境編 ❏ Docker + モックツールの活用 ❏ インフラ(AWS)編 ❏ Infrastructure as Code による Serverless 環境の構築
  60. 60. Copyright © bitbank, inc. 最後に ご静聴ありがとうございました ( ´∀`)

×