Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TechPlay22_06_28_Full TypeScriptで新規プロダクトを立ち上げた際に実践した設計と反省

kakehashi
June 30, 2022
1.2k

TechPlay22_06_28_Full TypeScriptで新規プロダクトを立ち上げた際に実践した設計と反省

kakehashi

June 30, 2022
Tweet

More Decks by kakehashi

Transcript

  1. 小室雅春 (コムロ マサハル) • WEBソフトウェアエンジニア • 経歴 ◦ 某新聞社(2016 ~

    2022/05) ◦ 株式会社カケハシ(2021/06~) • TypeScript, SPA, Express, AWS CDK • 患者さんの服薬をサポートする新規プロダクトの立 上げを担当 自己紹介 @_hedrall hedrall https://blog.hedrall.work
  2. 1. 背景 2. 技術選定と設計 • 言語 • レポ管理 • アーキテクチャー

    • フロントエンド • バックエンド • テスト 3. まとめ アジェンダ
  3. • 21年6月入社した当時、新規プロダクトの立上げの話があった ◦ => 患者さんの服薬を薬局がサポートするための業務システム • 明確なDeadLineはないが、概ね3, 4ヶ月くらいでMVP版をリリースした い ◦

    その後、正式リリース => スケールしていく前提 • リソースは、私 + 兼業メンバー2, 3名のサポート 新規プロダクト開発の背景 スピーディにプロダクトを立ち上げて、 PMFに向けたサイクルを回したい!
  4. • 薬局がWindows Desc/Tabで利用するJamstackな業務WEBアプリ ◦ SPAアプリ (SSR不要) • 患者の個人情報が含まれる ◦ (クライアント証明書による制限が必要)

    • 薬局が患者をサポートした履歴など、書き込みが必要 • (ログインセッションは既存の別システムと共有化する(SSO)) • 将来的に実装されうる機能が特定しきれていない ◦ 既存にない業務、今までになかったプロダクト 構築するシステムの特性
  5. • 開発速度 ◦ 軽量な技術スタックでTryErrorのサイクルを短縮 ◦ 効果の高いツールは、新興ツールでも利用する ▪ 捨てられる事が前提 • シンプルさ

    ◦ 兼業のメンバーや、今後加わるメンバーが理解しやすい • 属人化しない ◦ 一般的な技術 or 可換な技術 or 使いやすい技術 ◦ 整理されつつ、複雑化しないアーキテクチャー 技術選定・設計方針
  6. • WEBフロントエンドを開発する以上必ず使う • 全部TypeScriptで無理なく開発可能 ◦ AWS CDK (2019 ~) により、IaC

    もTypeScriptで簡単に • 立ち上がり期、フルスタックなメンバーの多い環境 ◦ (周辺エコシステムを含む) コンテキストスイッチの最小化 ▪ 他の言語を導入する場合はコストを考慮する必要がある • (バックエンドに重い集計などはない) • TSの良さ ◦ 型によるドキュメンテーション効果の向上 ▪ 構造的部分型による人に優しい型 (情報量・可読性) 2.1 言語 => Full TypeScript!!
  7. • 最近のエディターは複数のtsconfig.jsonに対応 • フロント・バックが一体のシステム • 一部コードを共有したい ◦ Domain Model, DTO,

    コアロジック, 共通関数 ◦ FullTS環境では大きな利点 • cons ◦ コード共有は循環参照に気をつける ◦ precommit hook, eslintの設定などは工夫する 2.2 レポジトリ管理 => モノレポ Github Repository frontend backend infra package.json tsconfig.json ・・・ package.json tsconfig.json common
  8. • クリーンアーキテクチャー(CA)、DDDの手法を漸進的に導入する • CA ◦ ロジックの置き場を整理 ◦ コンポーネントを差し替えできるようにしておく ▪ β版からプロダクトのスケールの準備

    • DDD ◦ ドメインモデルを整備する ▪ ビジネス要件をコードに反映しやすくする ▪ ドキュメンテーションとしての価値 • 週2回 (+ 都度)、PdMと要件を整理 2.3 アーキテクチャー
  9. バックエンド(API) フロントエンド(SPA) Controller UseCase Repository DB MySQL HTTP Request Domain

    Model DAO DTO TypeORM (DynamoDBなら省略可能? ) ドメインモデルの改修サイクルで APIに破壊的な変更が入る事があ る 変換 変換
  10. Domain Model DAO TypeORM (DynamoDBなら省略可能? ) ドメインモデルの改修サイクルで APIに破壊的な変更が入る事があ る バックエンド(API)

    フロントエンド(SPA) Controller UseCase Repository DB MySQL HTTP Request リリース時の ダウンタイムを 許容するか? 変換 変換 DTO
  11. • フレームワーク(Next.js, Angularなど)に依存したくない ◦ 素早くプロダクトを立ち上げる為に身軽でいたい ◦ たち上がりは早い? ▪ 当初知識がなくても利用しやすいが、将来的な学習コストが高い ◦

    不必要なツールに依存する ▪ ビルドに時間がかかる ▪ TryErrorサイクルに影響 ▪ 手を加えるのが大変 • 判断のポイント ◦ toCかtoBかで非機能要件が大きく異なる ▪ SSR(SEO)、パフォーマンス、負荷対策 • 大まかなトレンドは The State of JS などを参考 2.4 フロントエンド 前提
  12. • React ◦ 内部が全てJS ◦ TSによるドキュメンテーション効果が高い => 属人化しない ◦ ビルド開発環境が選びやすい

    => DX改善がしやすい • ビルド ◦ esbuild <= Webpackの100倍早い !! ▪ 軽量かつ可換 ▪ 話題のesbuildをさっくりと調査してみた ◦ ローカルは、expressとbrowsersyncでホスト ▪ Advent Calender: esbuild + React(TS) で実現する超軽量な開発環境 • パッケージ: pnpm <= npm よりも高速でエコ • Inversify.js => DIコンテナ ◦ APIクライアントをstabと差替し、standaloneで起動 2.4 フロントエンド React
  13. • 開発速度の向上!ドキュメント作成! • とはいえ、活用方法は十人十色 • TSOAを採用 ◦ CodeFirstでOpenAPIスペックを生成 ◦ 更にOpenAPI-generatorでAPIクライアントを生成

    • => 手間なく、フロントとバックの整合性を担保 • => ボイラープレート部分を自動生成 詳細な活用方法 ▼ Code Firstで疲弊しないOpenAPI活用方法 2.5 OpenAPIの活用 OpenAPI
  14. バックエンド コントローラを記述 import express from 'express'; import { Controller ,

    Get, OperationId , Body, Request, Route, Security } from 'tsoa'; @Route('user') export class UserController extends Controller { constructor (private service: IUserService) { super();} /** * ユーザ一覧取得 */ @Get('list') @Security(SECURITIES.COGNITO) @OperationId ('list-user' ) public async get( @Request() request: express.Request, @Body() params: InputDto ): Promise<OutputDto> { const res = await this .service.list( request. user, apiDtoConverter(params) ); return apiDtoConverters.user.list.serviceToOutput (res); } } Controller 後続の処理 変換 HTTP Request
  15. import express from 'express'; import { Controller , Get, OperationId

    , Body, Request, Route, Security } from 'tsoa'; @Route('user') export class UserController extends Controller { constructor (private service: IUserService) { super();} /** * ユーザ一覧取得 */ @Get('list') @Security(SECURITIES.COGNITO) @OperationId ('list-user' ) public async get( @Request () request: express.Request, @Body() params: InputDto ): Promise<OutputDto> { const res = await this .service.list( request. user, apiDtoConverter(params) ); return apiDtoConverters.user.list.serviceToOutput (res); } } Controller 後続の処理 変換 バックエンド コントローラを記述 GET: /user/listへのリクエストを処理 HTTP Request
  16. Controller 後続の処理 バックエンド OpenAPI Spec  ボイラープレート フロントエンド(SPA) Component (View) Repository

    Controller (Presenter) 変換 API Client 生成 生成 生成 • schemaでのvalidationなど • Auth Middlewareの差し込み 型の効いた API Request ! DB HTTP Request
  17. • Testing Trophyを念頭に • 静的解析を頼る • Unitを減らして、Unitに縛られない • 統合テストはDBをモックしない ◦

    https://github.com/testjavascript/nodejs-integration-tests- best-practices • Github Actions で実行 2.6 Testing 出典: https://twitter.com/kentcdodds/status/960723172591992832?lang=zh-Hant
  18. • Goods ◦ 機能全体のテストができるので、安心感が ある ◦ 障害が発生した場合に、修正以降の動作 を保証できる • Bads

    ◦ テストデータの作成が大変 ▪ JSONで記述するSeeds ▪ APIによるMutationを利用 ◦ テストケースが網羅できない場合がある ▪ 細かな文言の違い ▪ 局所的に複雑なロジック ▪ => UnitTestで担保できないか? • Jestの起動が遅い? ◦ Jestのテストを高速実行する自作ツール 既 存のJestで書かれたテストを爆速実行するツール、uvu-jest を作りました 2.5 Testing 出典: https://twitter.com/kentcdodds/status/960723172591992832?lang=zh-Hant