プラットフォーム部の dascarlet です。 今回は、NestJS で Prisma やトランザクションを考慮しながらドメイン駆動設計をする方法について紹介します。
進行中のプロジェクト内の話にあたるので具体的なコードはご紹介できませんが参考になれば幸いです。
NestJS とドメイン駆動設計、そして Prisma のトランザクション
弊社では NestJS とドメイン駆動開発を用いてバックエンドの開発が進行中です。RDB へのアクセスは Prisma を利用しています。そんな中で問題となったのは「ドメイン駆動設計を維持しながら Prisma を用いてトランザクションを RDB へ走らせる」ということをどのように実現するか、でした。
Prisma の $transaction API からは二種類の書き方が提供されています。一見すると事足りるように見えるのですがドメイン駆動設計やリポジトリパターンと共に使用しようとすると、複数のドメインを扱ったトランザクションを実行するときに Prisma の表現力の都合上どうしても不都合が出てくる部分があります。
これを解決するために Node.js API の AsyncLocalStorage をラップしている NestJS CLS というライブラリを利用し、更に TransactionModule
を NestJS
上に用意しました。以下が簡単な図になります。
説明
例えばある ApplicationServiceModule
の Provider に登録されている XXXApplicationService
が YYYRepository
と ZZZRepository
を用いてトランザクションを張りながらCRUD操作を行いたいとします。その場合 TransactionModule
が提供する inTransaction()
という関数に YYYRepository,ZZZRepository
の関数を実行するコールバック関数を渡せば、その後は PrismaServiceModule
がトランザクションを考慮しながら上手くコールバック関数を実行してくれるという仕組みになっています。
その際、各 Repository
からは「トランザクション専用で同一な Prisma の Client インスタンス」が利用されなければなりません。上記で紹介した NestJS CLS
をラップした RequestContextModule
がその部分を担当しています。
YYYRepository
と ZZZRepository
のドメインを合体させた YYZZRepository
などを作るのも手ですが、様々なドメインが追加される度に組み合わせがどんどん増えていってしまうのでその方法は見送りました。
短い記事にはなりましたが同じような問題に直面した方の参考になれば幸いです。