Closed3

NestJS + Fastify + Prisma ロードの考え方と突き進み方

waddy_uwaddy_u

これらを採用したい大目的(これを忘れないようにしたい)

  • パフォーマンスの向上
  • ソースコードへのドキュメンテーション能力を強化したい。なぜならメンテナンスやオンボーディングのコストを下げたいから。(正確には機能が追加されていくのでコストの増加を緩やかにしたい、というほうがいいかも)
    • TypeScript の型
    • フレームワークの思想にのる
    • ライブラリやツールを活用する

結論的な話

😋

  • TSの型を自動生成してくれるので開発者は型変換に気を使わなくていい
  • PrismaがもともとGraphQL前提だったこともあるのか、全体的に「ユースケースで全部解決する」方針を感じる
  • たとえば「コメントが追加されたら記事のコメント数をインクリメント」ではなくて「記事のコメントを作成するユースケースなのでコメントを作成しつつ記事のコメント数をインクリメントする」という感じ
  • TSの型がCRUDのインターフェース。なので裏側がDBでもAPIでもNoSQLでもアプリとしては同じようにJSONとして扱いを考えればいいのはメリットのひとつ(TypeORMだとRDBのEntityは特別、APIレスポンスはJSONというように扱いが別)

😬

  • 情報が少ない
  • Rails からの移行という観点で見ると不向き。フックや関連の更新がきつい。
  • フックや関連更新を全部ユースケースで実現するのは無理ゲー臭がする
  • 情報が少ない
waddy_uwaddy_u

アプリケーション におけるコード管理の方向性 - インスタンス作成とクラスorオブジェクト

つまりNestJSは:

  • 関数ベースで課題になりがちな「役割のまとまり」や「仕様上の意味」をもたせるべく、expressやfastifyをラップ
  • デベロッパーは「モジュール」単位で開発をすることで、これらの課題を解決できる
    • ので、できるだけモジュールのまとまりに名前をつけ、「imports」「exports」、つまりモジュールのインターフェースを明示的にできることが望ましい
  • DIは、Node.jsに優しいシングルトンを維持しつつこの仕組を実現する手段にすぎない(という感想)
    • ので、依存関係逆転であったり、実装の差し替えとかはぶっちゃけ気にしなくていいと思われる
  • インスンタンス化には可能な限りDIを使い、扱いをNestJSに任せる
    • これはクリーンアーキテクチャを使おうぜ、という意味ではなくて、NestJSの方針にできるだけ従う、という意味
waddy_uwaddy_u

TypeORM と Prisma にみるデータベースオブジェクトの扱いの違い

  • TypeORM:データベーステーブルと対応するEntityクラスを定義してね
  • Prisma:schema.prismaを定義してくれればこっちでJSONにするぜ!JS/TSの世界に集中してクレメンス

pure object + function ベース = わかりやすいが大規模になると大変

関数ベースのフレームワークなら(pure expressなど)、特に難しいことを考えずにPrismaオブジェクトを関数に引き渡していけば良い。

https://github.com/prisma/prisma-examples/blob/latest/javascript/rest-express/src/index.js

ただし、NestJSが解決したい課題「アプリケーションの機能をまとめる」「役割が名前をつける」は依然として難しくなっていくのでこれはこれで課題。

pure object + class ベース = モジュール化しやすいが pure object の扱いを決める必要がある

NestJS下でデータベースから受け取った pure object を扱うことの何が課題なのか?

NestJSのドキュメントを読んでいってもわかるとおり、NestJSの中ではできるだけクラスとして扱わせようとしているフシがある。

@Post()
@UsePipes(new JoiValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

リクエストから入ってきたJSONをわざわざ CreateCatDto クラスに変換しているし、そのための JoiValidationPipe もクラス。これは、デコレータをつけたりDIするにあたりクラスベースのほうが都合がいいからだと思われる。

クラスベースの世界に流れ込む Prisma の pure js

PrismaClientのレスポンスはJSON。

{
    "id": 1,
    "title": "hello",
    "published": false,
    "content": "prisma",
    "authorId": 1
}

schema.prisma を定義しておけばここまでやってくれる。個人開発ではこれをこのまま返せばはいAPI一個完成、ではあるがプロダクションではそうもいかない。

どうするか:

  • @Injectable なサービスで Prisma から取ってきた JSON をレスポンス可能なJSONに加工する(レスポンス作業は関数ベースですすめる)
    • わかりやすいが共通処理の切り出しで課題が出る。たとえば記事と商品で共通のSlugの生成をどうするか、など。
  • やっぱり一度 シリアライザクラスにマッピングしなおす。
  • 冗長に感じるが、NestJSのデコーレータが使えるのはやっぱり後々効いてくるかも。

このあたりは世の中でもまだ課題と思われる。

- まったく同じ疑問だけど残念ながら回答なし https://stackoverflow.com/questions/69166198/how-to-serialize-prisma-object-in-nestjs
- 「class-transformerでクラスにしてexposeとかしたよ」という人がいた。https://www.reddit.com/r/node/comments/lihfu8/how_do_you_serialize_prisma_objects_in_nestjs/
- prisma がDBデータをプレーンなJSONを扱うのはよくわかるけど、拡張性をもたせるためにJSONじゃなくてクラスがほしいという話 https://github.com/prisma/prisma/issues/5998
このスクラップは2021/12/01にクローズされました