Slide 1

Slide 1 text

NestJS-tRPCと戦術的DDDのい いとこどりをしてBackend  Typescriptの設計をした話 by Ficilcom CEO YOSA

Slide 2

Slide 2 text

自己紹介 東京大学工学部システム創成学科卒業 日鉄ソリューションズ(SIer) :システムエンジニア bestat(松尾研系スタートアップ) :機械学習エンジニア ZOZO(自社サービス) :推薦エンジニア 独立、現在2個目のプロダクトを開発中(今日はここの話) ←Web DB Press vol.129 「レコメンドエンジン総実装」 ↓ https://zenn.dev/yosashusaku

Slide 3

Slide 3 text

祝!2年ぶりの開催 この2年でNestJSの(個人的に一番の)大きな変化といえば、、、

Slide 4

Slide 4 text

NestJS tRPCのローンチ(2024/06) https://www.nestjs-trpc.io/ tRPCの作者(KevinEdry) が開発 →Monorepo内のBackend  Typescriptとして有力 な 候補に

Slide 5

Slide 5 text

tRPCとは client/serverでTS の型を共有 Monorepo前提 →とにかく開発体 験がいい!!

Slide 6

Slide 6 text

前提条件:Turborepoを使った開発 https://turbo.build/repo/ Vercel製のTS/JS専用Monorepo管理ツール - リモートキャッシュ - タスクの依存解決 - テストやビルドの並列実行 Monorepo開発における強力な機能が多い (Full TS前提だと特におすすめ) pnpm workspaceの拡張で右図のような ディレクトリ構成になっている

Slide 7

Slide 7 text

NestJS tRPCの設定 apps/api/src/app.module.ts packages/trpc/@generatedに自動生成 →フロントから呼び出せる

Slide 8

Slide 8 text

NestJS tRPCの実装例 NestJS tRPC専用のデコレータ DTOの代わりに Zodでスキーマ定義 (InputとOutputに使う フロントと型を共有) このサンプルでは1ファイルに 書いているが、実際に Module内は Router / Schema / Service で構成される。

Slide 9

Slide 9 text

NestJS tRPCのその他のメリット Validationの違い:Zodになったことでフロントと共有できるテクニックが使える class-validator           → Zod

Slide 10

Slide 10 text

おまけ:tRPC-Panel Swagger UIのtRPC版 APIエンドポイントの Documentを自動生成

Slide 11

Slide 11 text

NestJSでもMonorepo構成を 活かしたtRPCによる開発体験を 得ることができるゾ さて、 NestJSを使った開発に入る前に

Slide 12

Slide 12 text

NestJSは巷で言われているように too much なフレームワークなのだろうか?

Slide 13

Slide 13 text

APIサーバの役割をDDDの層に分解すると

Slide 14

Slide 14 text

NestJSの機能を当てはめると Controller / Router DTO / Schema Service

Slide 15

Slide 15 text

Serviceファイルに詰め込み過ぎじゃない? Serviceファイルにドメイン層・ユースケース層・インフラ層の責務を持たせる (Fat Service問題)のは無謀ではないか →改修困難なコード、人によってバラツくコーディングルール NestJSがtoo muchというのは嘘だと思う ※むしろnot enoughに感じた

Slide 16

Slide 16 text

NestJSで足りない部分は戦術的DDDで補う DDDの部分はpackages/coreに切り出そう! →ドメイン層とユースケース層はapps/apiを離れて packages/coreに移動する。 apps/api:NestJS(DI、デコレータなど) packages/core:DDD(domain、usecase、error) それぞれの文脈で開発を言葉を分けられる

Slide 17

Slide 17 text

戦略的DDDの構成要素 ドメイン層 - ビジネスロジックをエンティティや値オブジェクトに閉じ込め、純粋なビジネスルール として実装します。 - entity / value-object / aggregate / domain-service / repository(I/F) ユースケース層 - ユースケースに沿った処理を実装し、ドメイン層のオブジェクトを組み合わせてビジ ネスプロセスを実現します。 インフラ層 - 永続化や外部サービスとの連携など、技術的な実装を担います。ここでは、Prisma などのORMが利用され、実際のデータアクセスを管理します。

Slide 18

Slide 18 text

ドメイン駆動設計における依存の方向 api:ライブラリや外部アクセス    (ORM等)に依存する core:純粋なビジネスロジックを表現  ライブラリ等に依存しない  PureなTypescriptで実装する apiはcoreに依存する。 それぞれ分離することで、TestableでSOLIDな構成になる。

Slide 19

Slide 19 text

Turborepoを使った依存関係の管理 packages/core/package.json 余計なライブラリを入れない apps/api/package.json dependenciesでcoreへの依存を定義 core → api の順にtestやbuildを実行 DDDの依存関係をpackageで管理できた

Slide 20

Slide 20 text

依存方向のディレクトリ構成対応 ① ② ③ ① ② ③ ※依存元→依存先

Slide 21

Slide 21 text

依存性逆転の法則(実例) インフラ層を リポジトリに注入 (=依存性逆転) Repository(I/F) を使ってusecase を定義

Slide 22

Slide 22 text

依存性逆転の法則(実例解説) 前ページの実装例では AuthJSを活用して認証を行っているため、api配下ではAuthというModuleに属してい る。 ところが、core配下ではAuthJSというライブラリには依存したくないため、 ”createUser” といったビジネスドメインでの定義をしている。 ↓ core配下をPureなTypescriptを使って(=外部ライブラリやサードパーティに依存せず) ビジネスドメインを表現している。

Slide 23

Slide 23 text

いつでもNestJSを剥がせる (↑NestJSのmeetupで言うことじゃない!)

Slide 24

Slide 24 text

まとめ Tureborepoの性質をフル活かすことで、 ①NestJSでもtRPCという極上の開発体験を得られる ②NestJSとTS DDDのいいとこどりをした開発ができる

Slide 25

Slide 25 text

告知①:エンジニア募集中 Typescript DDDだけじゃなくて おもしろい技術スタック使ってるよ! 募集職種:Typescript芸人  プロダクトエンジニア 建設業というドメイン (→DDDを使いたかった)

Slide 26

Slide 26 text

告知②:TSKaigi 2025開催

Slide 27

Slide 27 text

ご清聴ありがとうございました