■イベント TypeScriptを活用した型安全なチーム開発 https://sansan.connpass.com/event/292695/
■登壇概要 タイトル:Prisma を活⽤した TypeScript チーム開発 登壇者:技術本部 Digitization部 データ化グループ 湯村 直樹
■Digitization部 エンジニア 採用情報 https://media.sansan-engineering.com/digitization
Prisma を活⽤したTypeScript チーム開発技術本部 Digitization部 データ化グループ湯村 直樹
View Slide
写真が入ります2019年 Sansan株式会社に新卒⼊社。名刺データ化システムなど複数のシステムの開発を経て、現在はデータ⼊⼒システム⽤の認証基盤の開発に従事。湯村 直樹Sansan株式会社Digitization部 データ化グループ
データ化システムの認証基盤- Digitization部では複数のデータ化システムを開発している。- データ化システムを利⽤する⽅のアカウント管理、⼊⼒システムへのSSO を可能にする認証基盤を開発している。- 認証基盤では TypeScript と Prisma を採⽤している。
Prisma とは?- Prisma は ORM- Prisma のスキーマを single source of truth として、アプリケーションのモデルの型とデータベースのマイグレーションファイルを⽣成できる。
Prisma とは?アプリケーションのモデルDB のスキーマ
Prisma は next-generation ORM- Prisma は従来の ORM とは異なるデータマッパー (※)- アプリケーションのモデルと DB のスキーマのマッピングは、マッピング⽤のクラスではなく、Schema から⽣成された Prisma のクライアントが⾏う。- アプリケーションのモデルはクラスで定義していたのに対して、Prismaでは TypeScript の型が⽣成される。- これによって型安全性を得ることができる。(※) Is Prisma an ORM?https://www.prisma.io/docs/concepts/overview/prisma-in-your-stack/is-prisma-an-orm
実際に Prisma を使う- 素直に使うとデータの完全性を守るのが難しい。Email のドメインは会社に登録されたものである必要があった
データの完全性をどう守るのか
Active Record パターンでの実装- DB のテーブルに対応したクラスに、データアクセスのロジックとビジネスロジックを持たせる。- データの完全性はレコード追加時に担保できればいい。- インスタンスのメソッドを利⽤するために、Prisma Client が返すオブジェクトからインスタンスを⽣成する。
レコード追加時に完全性を担保する
Data Mapper パターンでの実装- データアクセスの役割を持つクラスを分ける。- データの完全性をモデルクラスで担保する。- モデルクラスのインスタンスを完全なデータとして信頼する。- DB から取得した値は完全なデータとして扱う。
インスタンス⽣成時に完全性を担保する完全なデータとして信頼する
改めて Prisma が従来の ORM とどう違うのか- Prisma は従来の ORM とは異なるデータマッパー (※)- アプリケーションのモデルと DB のスキーマのマッピングは、マッピング⽤のクラスではなく、Schema から⽣成された Prisma のクライアントが⾏う。- アプリケーションのモデルはクラスで定義していたのに対して、Prismaでは TypeScript の型が⽣成される。- Prisma によって⽣成された型と Prisma Client の関数を再実装していることになるのでは?(※) Is Prisma an ORM?https://www.prisma.io/docs/concepts/overview/prisma-in-your-stack/is-prisma-an-orm
プレーンなオブジェクトを使った実装- プレーンな JavaScript のオブジェクトをそのまま扱う。
プレーンなオブジェクトを使った実装- クラスに変換する必要がない。- クラスで実現できていたカプセル化の実現が困難。- モジュールが肥⼤化しやすい。
Prisma Client Extensions- model- アプリケーションのモデルにメソッドを定義できる。- client- Prisma Client にメソッドを定義できる。- query- Prisma Client のクエリを加⼯できる。- result- Prisma Client の返すオブジェクトにメソッドを定義できる。
Prisma Client Extensionsresultclientmodelquery
model の拡張- モデルにレコードを追加するメソッドを追加する。
query の拡張- create の実⾏時に値を検証する。
result の拡張- Prisma Client が返すオブジェクトにメソッドを追加する。
Prisma Client Extensions まとめ- データの変換処理を⾏わず、型安全にビジネスロジックを実装できる。- $allModels と $allOperations を利⽤して、モデルやオペレーションをまたいだ共通処理も実装できる。- 複数のモデルが関連する処理を書きにくい。- 関連するモデルを都度 DB から引き直す。- Prisma.defineExtension を返す関数の引数に関連するモデルを渡す。- Prisma 特有の知識が求められる。
認証基盤で採⽤したアプローチ- クラスを利⽤した Data Mapper のような考え⽅でプレーンなオブジェクトを利⽤する。- クラスに変換する必要がないので、データの取得は直接 Prisma Clientを利⽤する。- データの完全性をビジネスロジックを扱うモジュールで担保する。- ビジネスロジックを扱うモジュールで⽣成されたオブジェクトを完全なデータとして信頼する。- 状態ごとに型を分けると、状態ごとのロジックを持たせやすい。- コンパイル時に意図しない状態に気づくことができる。
不正な値を渡すとコンパイルに失敗する
認証基盤で採⽤したアプローチ- 型と関数を組み合わせることで状態に特化したロジックを実装しやすい。- データの完全性をコンパイル時に担保しやすい。- 型が増えるとそれらの関係を把握するのが難しくなる。- コントローラーから関数を呼び出す側の実装が増えるケースがある。
[補⾜] 共通のクエリを実装するには?- satisfies (※)- ビルダーパターン(※) How TypeScript 4.9 `satisfies` Your Prisma Workflowshttps://www.prisma.io/blog/satisfies-operator-ur8ys8ccq7zb
[補⾜] satisfies を利⽤した共通のクエリ- 型の Widening を防ぎながらクエリを記述できる。
- Fluent な API を提供できる。[補⾜] ビルダーパターンを利⽤した共通のクエリ
まとめ- 継続的にチーム開発を⾏うためにはデータの完全性を保つ仕組みが必要。- TypeScript と Prisma では様々なアプローチが可能。- 重要なのはどのパターンを採⽤するかではなく、重要な原則を守るための仕組み、共通認識を持つこと。