リポジトリへまたはリポジトリへ


10

ドメインドリブンデザインについて初めて知ったとき、データベースに対する穴居人のようなSQLクエリを投げたクールな子供たちにとって、かつては一流であるように思われるリポジトリと作業単位パターンも紹介されました。EFNHibernateのようなORMは、作業単位とリポジトリの両方を1つのAPI(セッションまたはコンテキストと呼ばれる)に実装するため、そのトピックを深く掘り下げれ読むほど、それらは必要なくなったように見えます。

今、私は何をすべきかわかりません。リポジトリするかしないか。このようなリークの多い抽象化は複雑すぎてデータアクセスを簡略化するものは一切追加しないという主張を本当に理解していますが、アプリケーションのあらゆる側面をEntity Frameworkなどに結合するのは適切ではないと感じます。通常、私はいくつかの簡単なガイドラインに従います:

  1. ドメイン層は、エンティティ、サービス、リポジトリを含むシステムの中心です...
  2. インフラストラクチャ層は、ファイル、データベース、プロトコルなどのインフラストラクチャ関連のドメインインターフェイスの実装を提供します。
  3. アプリケーション層は、物事を結び付け、すべてをオーケストレーションするコンポジションルートをホストします。

私の解決策は通常次のようになります:

Domain.Module1
Domain.Module2
    IModule2Repo
    IModule2Service
    Module2
Infrastructure.Persistence
    Repositories
        EntityFrameworkRepositoryBase
MyApp
    Boostrapper
        -> inject EntityFrameworkRepositoryBase into IRepository etc.

私は、IRepository<'T>データへのアクセス方法を教えてくれる他のものに依存しないドメインの問題でもあるを使用して、ドメインレイヤーをクリーンに保ちます。IModule2Serviceデータアクセスを必要とする具体的な実装を行うときは、注入DbContextし、これによってインフラストラクチャレイヤーに直接結合する必要があります。(Visual Studioプロジェクトに入ると、循環依存関係のために、これは本当にトリッキーになる可能性があります!

さらに、作品の保管庫ファックトンの代わりになるものは何ですか?CQRS?純粋なインフラストラクチャフレームワークを抽象化するにはどうすればよいですか?


1
余談ですが、DDDで説明されているように「リポジトリ」について話している場合、EFとnHibernateはリポジトリではありません。確かに、それらはデータアクセスのメカニズムを部分的に抽象化しますが、リポジトリにはそれ以上のものがあります
エリックキング

回答:


4

エンジニアリングは妥協のすべてです。ソフトウェア開発も同様です。現在、もっと簡単なのはORMを直接操作することだけです。しかし、あなたが言ったように、それはあなたを特定の永続化フレームワークにロックするかもしれません。

したがって、「永続性からコードを分離する価値がある追加の複雑さですか」と自問する必要があります。持続性からコードを分離したいと人々が言うのを聞くたびに、「あなたのキャリアの中で、持続性フレームワークを何回変更しましたか?」

リポジトリに関して私が目にする問題は、それらが一般的な変更を困難にすることです。例えば。集計をクエリする新しい方法を追加します。また、一般的ではない変更を(おそらく)簡単にします。例えば。リポジトリの実装の変更。

次に、ユニットテストの引数もあります。「永続化フレームワークでは、データベースをメモリ内またはローカルでモックすることができない場合、フレームワークを使用する価値はまったくありません。」


1
リポジトリの目的は、アプリを永続化から切り離すことであり、複雑さはありません。また、永続化の詳細は少なくとも1回変更します。これは、dbアクセスがコード化する最後のものであるため、メモリリポジトリで使用しているその時点までです。さらに、永続性の詳細を直接使用している場合、非常に微妙な罠があります。ビジネスオブジェクトは、不可知論的ではなく、それと互換性があるように設計される傾向があります。そして、のことが豊かな、適切にカプセル化されたエンティティは、直接(醜い)回避策なし(メモリを除く)任意のストレージから復元することはできませんので
MikeSW

集約ルートまたは任意のエンティティをクエリする新しい方法の追加について、それがCQRSが登場した理由です。個人的には、ドメインの目的でリポジトリを保持し、実際のクエリにはクエリハンドラーを使用します。そして、それらのハンドラーは、dbと非常に密に結合されており、ofcはそれらが何をするのか非常に効率的です。
MikeSW 2014年

3

私はプロリポジトリですが、一般的なリポジトリパターンから離れました。代わりに、リポジトリをそれらが提供するビジネス機能に合わせます。リポジトリはORMを抽象化することを目的としていません。これは私が変更することを期待しているものではないため、リポジトリを細かくしすぎるのを避けます。(つまりCRUD)代わりに、私のリポジトリは2〜3つの主要な目的を果たします。

  • データ検索
  • データ作成
  • 完全削除

データを取得する場合、リポジトリは常にを返しますIQueryable<TEntity>。データ作成の場合、TEntityを返します。リポジトリーは、一時削除パターンを使用するシステムの許可「アクティブ」状態や、履歴データを使用するシステムの「現在」状態など、私の基本レベルのフィルタリングを処理します。データの作成は、必要な参照が解決されて関連付けられていること、およびエンティティーがセットアップされて準備ができていることを確認することのみを担当します。

データの更新は、問題のエンティティを操作するビジネスロジックの責任です。これには、検証ルールなどを含めることができます。私はそれをリポジトリメソッドにカプセル化しようとしません。

ほとんどのシステムでの削除はソフト削除であるため、データの更新に該当します。(IsActive = false)完全削除の場合、これはリポジトリの1行になります。

なぜリポジトリか?テスト能力。もちろん、DbContextはモックできますが、返されるクラスをモックする方が簡単です。IQueryable<TEntity>。それらはUoWパターンでもうまく機能します。個人的には、MehdimeのDbContextScopeパターンを使用して、必要なレベル(つまり、MVCのコントローラー)で作業単位をスコープアウトし、コントローラーとヘルパーサービスクラスが参照を渡さなくてもリポジトリを利用できるようにします。周りのUoW / dbContext。IQueryableを使用すると、リポジトリに多くのラッパーメソッドが不要になり、コードでデータの消費方法を最適化できます。たとえば、リポジトリは「Exists」や「Count」などのメソッドを公開したり、データのサブセットが必要な場合にエンティティを他のPOCOでラップしたりする必要はありません。彼らは、あなたが必要とするかもしれないし必要としないかもしれない関連するデータのための熱心な読み込みオプションを処理する必要さえありません。IQueryableを渡すことにより、呼び出しコードは次のことができます。

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

非常に柔軟性があり、テスト用のPoVから、私のモック化されたリポジトリは、入力されたエンティティオブジェクトのリストを返すだけで済みます。

汎用リポジトリーについては、テーブルごとにリポジトリーが作成されると、コントローラー/サービスが1つのビジネスアクションを実行するために複数のリポジトリーを参照することになるため、大部分はこれらから離れました。ほとんどの場合、これらのリポジトリの1つまたは2つだけが実際に書き込み操作を行っています(ナビゲーションプロパティを適切に使用している場合)。残りは読み取りでそれらをサポートしています。5つまたは6つの異なるリポジトリにアクセスするのではなく、注文の読み取りと作成、および関連するルックアップなど(軽量の顧客オブジェクト、製品など)の読み取りが可能な注文の作成時に参照できるOrdersRepositoryのようなものが欲しいです。これはDNRYの純粋主義者に違反する可能性がありますが、私の主張は、リポジトリの目的は、関連する参照を含む注文の作成を提供することであるということです。Repository<Product>注文の基礎となる製品を取得するには、フィールドがいくつかあるエンティティのみが必要です。私のOrderRepositoryには、アプリケーションのニーズのさまざまな領域を試行して処理するためのいくつかの「Get」メソッドや、複雑なパスインフィルタリング式が存在する.GetProducts()場合IQueryable<ProductSummary>よりも優れたメソッドが返される場合Repository<Product>があります。

追跡、テスト、調整が簡単な、より単純なコードを選びます。それは悪い方法で悪用される可能性がありますが、悪用できない方法でコードを「ロックダウン」して失敗し、それから何かであるよりも、悪用が簡単に見つけて修正できるようなものが欲しいです。最終的にクライアントが実行するためにクライアントが支払うものを実際に実行するための悪夢。:)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.