2009-08-28 14 views
20

だから私は、アプリケーションでリポジトリパターンを実装すると、パターンの私の理解では、二つの「問題」に出くわしたんだ:リポジトリパターンのベストプラクティスが

  1. クエリ - 私は回答を読んだことのIQueryableはいけませんリポジトリを使用する場合に使用します。ただし、メソッドを呼び出すたびにオブジェクトの完全なリストを返さないようにしたいと思うことは明らかです。実装する必要がありますか?リストと呼ばれるIEnumerableメソッドがある場合、IQueryableの一般的な「ベストプラクティス」は何ですか?どのようなパラメータを持っていてもいけないのでしょうか?

  2. スカラー値 - レコード全体を返さずに単一のスカラー値を返すには、リポジトリパターンを使用するのに最適な方法は何ですか?パフォーマンスの観点からは、行全体にわたって単一のスカラー値を返すほうが効率的ではないでしょうか? 1について

答えて

30

厳密に言えば、リポジトリは、ドメインオブジェクトを取得/配置するためのコレクションのセマンティクスを提供します。ドメインオブジェクトのコンシューマがそれらの詳細から切り離されるように、マテリアライゼーション実装(ORM、ハンドロール、モック)の抽象化を提供します。実際には、リポジトリは通常、エンティティ、つまりアイデンティティを持つドメインオブジェクト、通常永続的なライフサイクル(DDDフレーバでは、リポジトリが集約ルートへのアクセスを提供する)へのアクセスを抽象化します。

void Add(T entity); 
void Remove(T entity); 
T GetById(object id); 
IEnumerable<T> Find(Specification spec); 

あなたが命名の違いとするsaveOrUpdate保存/セマンティクスの追加を参照してくださいよものの以上、「純粋な」という考えです:

リポジトリのための最小限のインターフェイスは、次の通りです。 ICollectionのAdd/Removeメンバーといくつかのファインダを取得します。あなたがのIQueryableを使用しない場合は、のようなリポジトリに検索メソッドが表示されます:

FindCustomersHavingOrders(); 
FindCustomersHavingPremiumStatus(); 

この文脈でのIQueryableを使用して2つの関連する問題があります。第1の問題は、ドメインオブジェクトの関係、すなわちデメテルの法則の違反の形で実装の詳細をクライアントにリークさせる可能性である。第2の理由は、リポジトリが、ドメインオブジェクトリポジトリに属していない可能性のある責任の発見、例えば関連するデータよりも要求されたドメインオブジェクトに関する予測の発見を取得することである。

さらに、IQueryableを使用すると、パターンが破損します。IQueryableを備えたリポジトリは、「ドメインオブジェクト」へのアクセスを提供する場合も提供しない場合もあります。 IQueryableは、クエリが最終的に実行されたときに実現されるものについて、クライアントに多くのオプションを提供します。これは、IQueryableの使用に関する主な論点です。

スカラー値に関しては、スカラー値を返すためにリポジトリを使用すべきではありません。スカラが必要な場合は、通常、エンティティ自体からスカラーを取得します。これが非効率的であると思われる場合、それはありますが、負荷特性/要件によっては気付かないかもしれません。パフォーマンス上の理由、または多数のドメインオブジェクトのデータをマージする必要があるため、ドメインオブジェクトの代替ビューが必要な場合は、2つの選択肢があります。

1)エンティティのリポジトリを使用して特定のエンティティを検索し、プロジェクト/マップをフラット化したビューにします。

2)必要な平坦化されたビューをカプセル化する新しいドメインタイプを返すための専用のファインダインターフェイスを作成します。 Collectionのセマンティクスは存在しないため、これはリポジトリではありませんが、カバーの下にある既存のリポジトリを使用する可能性があります。

永続エンティティにアクセスするために「純粋な」リポジトリを使用する場合、ORMの利点のいくつかを妥協することがあります。 「純粋な」実装では、クライアントはドメインオブジェクトの使用方法に関するコンテキストを提供することができないので、リポジトリには 'ちょっと、私はcustomer.Nameプロパティを変更するつもりですその熱心に読み込まれた参照を得るのは苦労します。反対に、問題はクライアントがそのことを知るべきかどうかということです。それは両刃の剣です。

IQueryableを使用する限り、ほとんどの人は、動的なクエリ構成の利点を得るために、特にページング/並べ替えのようなクライアントの責任のためにパターンを「破壊」するのに慣れているようです。

Add(T entity); 
Remove(T entity); 
T GetById(object id); 
IQueryable<T> Find(); 

を、あなたは、あなたのクエリの要件が成長するにつれて、実際にリポジトリを乱雑にすべてのそれらのカスタム検索メソッド、と離れて行うことができます。その場合、あなたが持っているかもしれません。

+1

今日、このトピックに関するjbogardの投稿がありました:http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/02/ddd-repository-implementation-patterns.aspx – pfries

+3

+1: " ..ほとんどの人は、動的なクエリ構成の利点を得るためにパターンを「破る」のが快適であるように思えます... " –

3

:私の知る限りはそれを見ることができるよう が、それは問題がリポジトリから返されるIQuerableそのものではありません。リポジトリのポイントは、すべてのデータを含むオブジェクトのように見えるということです。リポジトリにデータを問い合わせることができます。同じデータを必要とする複数のオブジェクトがある場合、リポジトリの仕事はデータをキャッシュすることで、リポジトリの2つのクライアントは同じインスタンスを取得します。したがって、一方のクライアントがプロパティを変更すると、彼らは同じインスタンスを指しているからです。

リポジトリが実際にLinq-providerそのものであった場合、それはまさにその通りです。しかし、ほとんどの人はLinq-to-SQLプロバイダのIQuerableを通過させて、リポジトリの責任を迂回します。したがって、リポジトリは、少なくとも私の理解とパターンの使用法に従って、リポジトリではありません。

2について: 当然、レコード全体よりもデータベースから単一の値を返す方がパフォーマンスが向上します。しかし、リポジトリー・パターンを使用すると、レコードを返すことはなく、ビジネス・オブジェクトを戻すことになります。したがって、アプリケーションロジックは、フィールドに関わるものではなく、ドメインオブジェクトに関係している必要があります。

しかし、完全なドメインオブジェクトに比べて単一の値を返す方が効果的ですか?データベーススキーマが合理的に定義されている場合は、おそらくその差を測定することはできません。

微妙なパフォーマンスの最適化ではなく、きれいでわかりやすいコードを持つことがずっと重要です。

7

@ lordinateurへの応答として、私はリポジトリインタフェースを指定する方法が本当に好きではありません。

解決策のインターフェイスでは、すべてのリポジトリの実装に少なくともAdd、Remove、GetByIdなどが必要であるため、リポジトリの特定のインスタンスを介して保存することは理にかなっていません。 NotImplementedExceptionなどで残りのメソッドを実装する必要があります。

私はそうのような私のリポジトリインターフェイス宣言を分割することを好む:

interface ICanAdd<T> 
{ 
    T Add(T entity); 
} 

interface ICanRemove<T> 
{ 
    bool Remove(T entity); 
} 

interface ICanGetById<T> 
{ 
    T Get(int id); 
} 

工assエンティティの特定のリポジトリの実装は、このように次のようになります。

interface ISomeRepository 
    : ICanAdd<SomeClass>, 
     ICanRemove<SomeClass> 
{ 
    SomeClass Add(SomeClass entity); 
    bool Remove(SomeClass entity); 
} 

のバックステップを見てみましょうし、なぜ私は、これがすべてのCRUDメソッドを1つの汎用インタフェースで実装するよりも優れた方法だと考えているかを見てみましょう。

いくつかのオブジェクトには、他のものより異なる 要件があります。顧客 オブジェクトは削除できません。 PurchaseOrderは更新できません。 ShoppingCartオブジェクトは のみ作成できます。一般的な IRepositoryインターフェイスを使用している場合、この は明らかに の実装で問題を引き起こします。

しばしば、彼らは サポートしないメソッドに対して の例外がスローされますその後、彼らの完全な インタフェースを実装しますアンチパターン を実装するもの。これとは別に、彼らはまた、サポートされており、さらに にそれらを実装している かどうかを指定したオブジェクトのためのそれのメソッド を置く作業を開始しない限り、効果的 その IRepository抽象化を使用することができるという 彼らの希望を壊す 多数OO原則に不同意から。

この問題に共通する問題を回避するには、 などなどICanDelete、ICanUpdate、 ICanCreateこのような、より粒状のインターフェイスに移動する ある はOO 原則の観点から湧いてきた問題 の周りに多くの仕事をしながらまた、 コードの再利用量を大幅に減らすことがあります。 は、ほとんどの場合、 の具体的なインスタンスをリポジトリに使用することはできません。

私たちの誰も同じコード を何度も繰り返し書きたくない。しかし、 という契約はアーキテクチャの継ぎ目であるため、 は、より一般的なものにするために 契約を広げるための間違った場所です。

これらの発言は、this postからshamelesly取られています。このコメントは、コメントでさらに議論を読むことができます。

+0

リポジトリは、あなたが「保存」したようなものはどうですか?追加/削除部分をドロップすると、Finderインターフェースだけが表示されます。あなたがしたいことがすべて物事を見つけるものであれば、リポジトリは必要なく、むしろICustomerFinderの実装が必要です。それは言った、あなたのリポジトリは、ファインダーを実現することができます。 インタフェースICustomerRepository:作業単位のいくつかの種類の責任だろうリポジトリインタフェースを通じてIRepository 、ICustomerFinder だけ明確にする、あなたが本当に「保存」を実装。このようなセッションは、リポジトリの外部で管理するのが最適です。 – pfries

+0

グレッグの構図は良いです。つまり、内部の汎用リポジトリを使用してドメインオブジェクトリポジトリを実装できます。これは、NHibernate(汎用リポジトリの代わりになる)のようなORMの周りのICustomerRepositoryのようなインタフェースを使用するとき、実際にどのように動作するかです。彼の主なポイントは、アプリケーションの継ぎ目で汎用契約を使用しないことです。それはかなり妥当です。あなたの契約がIRepositoryではなくICustomerRepositoryである場合、あなたは彼が記述した問題を避けます。私は意図を表しているにもかかわらず、すべてのファインダーを愛していません。クエリオブジェクトはどうですか? – pfries

+0

froghammr:すべての調査の後、私はQuery Objectsで解決します。私はMonoDroid/C#でモバイルアプリを開発しており、LINQ、Entity、またはHibernateを持っていないので、すべてをゼロから書く必要があります。それにもかかわらず、Finder/Specificationのオーバーヘッドは深刻です。 – samosaris

関連する問題