generated at
ORMで使用されるデザインパターンについてnpmパッケージを元に考える
概要
エンタープライズ アプリケーションアーキテクチャパターンを読み直してみたので、整理も兼ねて情報をまとめてみました

パターンについて
大抵のORMは、これらのうち複数のパターンを実装しています
DB上のテーブルへのアクセスを抽象化する
大抵のORMでこのパターンは使用されている
DBへのアクセスを抽象化することで、例えば、SQLを書かずにクエリが実行できたり、RDBMS間の差異を吸収することなどができます
prisma.ts
const user = await prisma.user.findUnique({ where: { id: 1234 }, }); await prisma.user.update({ where: { id: 1234 }, data: { name: "foobar" }, });

DBのレコードと1:1に紐づき、永続化ロジックを提供するオブジェクトを実装するパターン
javascript
const user = await User.find(1234); user.name = "foobar"; await user.save();

※少しややこしいですが、railsactiverecordデザインパターンの名前がそのままライブラリ名として採用されています。
Model(ここではリレーションにマッピングされるオブジェクトの意味)が永続化に関する知識を持つ
行データゲートウェイとの違いはドメインロジックの有無で、Active Record行データゲートウェイが備える永続化の役割に加えて、ドメインロジックも提供するのが特徴 (ドメインモデル行データゲートウェイの両方のパターンを実装したものがActive Recordのイメージ)
sequelize.js
const user = await User.findByPk(1234); // 永続化に関する知識を持つ user.changeName("foobar"); // ドメインロジック await user.save(); // 永続化に関する知識を持つ

Model(リレーションにマッピングされるオブジェクト)はドメインロジックを持つものの、永続化に関する知識は持たないのがActive Recordとの違い
ORMの目的であるインピーダンスミスマッチを解消するという点においては、ここで紹介した各パターンの中において、最も理想的なパターンではないかと思います
ただし、ドメインと永続化を完全に分離することは難しくて、大抵のORMではある程度妥協されている場合がほとんどだと思います
例えば、TypeORMでは、マッピング情報をデコレータまたはEntity Schemaのいずれかの方法で定義できます
このうちデコレータを使ってマッピングを定義した場合、Modelが永続化に関する知識(TypeORMに対する依存)を持つことになるため、完全にドメインと永続化のレイヤーを分離できているわけではなくなります
MikroORMの場合、 *:N 関係を表現するのに独自の Collection 型を使用する必要があり、ドメインモデルからORMへの依存ができる
永続化に関する責務は、ORMにもよりますが、 EntityManager Repository のような名前のクラスで提供されることが多い印象です (これらのクラスは、ほとんどの場合、テーブルデータゲートウェイパターンを実装しているはずです)
typeorm.ts
const userRepository = dataSource.getRepository(User); const user = await userRepository.findOneBy({ id: 1234 }); user.changeName("foobar"); await userRepository.save(user); // Modelは永続化に関する知識を持たない

MikroORMが実装しているパターン

各ORMが実装しているパターンについて
Prisma - テーブルデータゲートウェイ (※Prisma Client extensionsを活用すれば、Active Recordパターンなども実装可能)

結局どのパターンがいいの?
ビジネスロジックをどのように実装するかにもよる
特にPrismaはAPIもシンプルで学習コストも他のORMと比較しても低めだと思うため、生産性もかなり意識して作られていると思うため、とにかく動くものを素早く作りたいケースにおいては向いているはず
ドメインモデルパターンによって実装する場合は、Data MapperActive Recordが相性が良いでしょう
Active RecordData Mapperを比較した場合、関心の分離の観点からすると、Active RecordよりもData Mapperの方が理想的です
ただ、Rails製の巨大なサービスも世の中には数多くあるように、Active Recordパターンを採用したからといって、メンテナンス不可能になるわけではないです
きちんとテストが書かれて自動化されていれば、結局どちらのパターンを使用したとしてもメンテナンスは十分可能だと思います。
なので、この2つのパターンについては、対象ドメインの複雑度によってどちらを使うかを決めるとよいのではないかと思います
また、チーム内にRailsのスキルを持ったメンバーが多く在籍しているような場合は、Railsactiverecordを使って実装した方が、生産性やメンテナンス性の面では実は有利かもしれません (最近、何かと批判されがちな印象は感じるものの、Railsは十分な実績や使用率もあり、とても優れたフレームワークだと自分は思っています)
最終的にはどのパターンを採用するにせよ、保守性の観点からすると、結局はテストの自動化やCIとかの仕組みとかが重要なのではないかと思います

参考

関連ページ