リポジトリパターンを実装する最良の方法は?
-
05-07-2019 - |
質問
私はBDD / DDDを調査してきましたが、その結果、リポジトリパターンの適切な実装を考え出そうとしています。これまでのところ、これを実装する最善の方法についてコンセンサスを見つけるのは困難でした。私はそれを次のバリエーションに要約しようとしましたが、どれが最良のアプローチかわかりません。
参考のため、NHibernateをバックエンドとして使用してASP.MVCアプリケーションを構築しています。
public interface IRepository<T> {
// 1) Thin facade over LINQ
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Remove(T entity);
IQueryable<T> Find();
// or possibly even
T Get(Expression<Func<T, bool>> query);
List<T> Find(Expression<Func<T, bool>> query);
}
public interface IRepository<T> {
// 2) Custom methods for each query
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Remove(T entity);
IList<T> FindAll();
IList<T> FindBySku(string sku);
IList<T> FindByName(string name);
IList<T> FindByPrice(decimal price);
// ... and so on
}
public interface IRepository<T> {
// 3) Wrap NHibernate Criteria in Spec pattern
void Add(T entity);
void Update(T entity);
void Remove(T entity);
IList<T> FindAll();
IList<T> FindBySpec(ISpecification<T> specification);
T GetById(int id);
}
public interface IRepository<T> {
// 4) Expose NHibernate Criteria directly
T GetById(int id);
void Add(T entity);
void Update(T entity);
void Remove(T entity);
IList<T> FindAll();
IList<T> Find(ICriteria criteria);
// .. or possibly
IList<T> Find(HQL stuff);
}
最初に考えたことは
1)は効率の観点からは素晴らしいですが、事態が複雑になると問題が発生する可能性があります。
2)非常に退屈に思われ、非常に混雑したクラスになる可能性がありますが、それ以外の場合は、ドメインロジックと私の好きなデータレイヤーを高度に分離できます。
3)前もって難しく、クエリを書くのに多くの作業があるように見えますが、相互汚染は仕様層のみに制限されます。
4)一番好きではないが、おそらく最も直接的な実装であり、おそらく複雑なクエリに対して最も効率的なデータベースですが、呼び出し元のコードには多くの責任があります。
解決
これらはすべて良い選択肢だと思います(nhibernateに結び付けたくない場合は4を除く)。あなたの現在の努力に基づいてあなた自身で決定を下すために長所と短所をよく分析しているようです。 これであまりにも強く打ち負かさないでください。
現在、2〜3の混合物に取り組んでいます:
public interface IRepository<T>
{
...
IList<T> FindAll();
IList<T> FindBySpec(ISpecification<T> specification);
T GetById(int id);
}
public interface ISpecificRepository : IRepository<Specific>
{
...
IList<Specific> FindBySku(string sku);
IList<Specific> FindByName(string name);
IList<Specific> FindByPrice(decimal price);
}
また、(Tの)リポジトリベースクラスもあります。
他のヒント
<!> quot;上記のどれでもないための良い引数もあります<!> quot;アプローチ。
汎用リポジトリの問題は、システム内のすべてのオブジェクトが4つのCRUD操作(作成、読み取り、更新、削除)をすべてサポートすると仮定していることです。ただし、複雑なシステムでは、いくつかの操作のみをサポートするオブジェクトが存在する可能性があります。たとえば、読み取り専用のオブジェクトや、作成されたが更新されていないオブジェクトがあります。
IRepositoryインターフェースを、読み取り、削除などのために小さなインターフェースに分割することもできますが、それはすぐに面倒になります。
Gregory Youngは、(DDD /ソフトウェアレイヤリングの観点から)各リポジトリが、操作しているドメインオブジェクトまたは集約に固有の操作のみをサポートするべきであるという良い議論をしています。 ジェネリックリポジトリ。
別のビューについては、このAyende ブログ投稿。
私たちがしていることの1つは、すべてのリポジトリのニーズが異なるため、インターフェイスのコレクションを作成していることです:
public interface IReadOnlyRepository<T,V>
{
V Find(T);
}
この例では、読み取り専用リポジトリはデータベースから取得するだけです。 T、Vの理由は、Vがリポジトリーによって返されるものを表し、Tが渡されるものを表すため、次のようにすることができることです。
public class CustomerRepository:IReadOnlyRepository<int, Customer>, IReadOnlyRepository<string, Customer>
{
public Customer Find(int customerId)
{
}
public Customer Find(string customerName)
{
}
}
また、追加、更新、削除用に個別のインターフェイスを作成することもできます。このように、リポジトリが動作を必要としない場合、インターフェースを実装しません。
IQueryable <!> lt; <!> gt;に適用できるよりもフィルターとページング拡張メソッドを作成できるため、私は1のbifファンです。 Findメソッドの戻り値。拡張メソッドをデータレイヤーに保持し、ビジネスレイヤーでその場で構築します。 (確かに完全に純粋ではありません。)
もちろん、システムが安定したら、同じ拡張メソッドを使用して特定のFindメソッドを作成し、Func <!> lt; <!> gt;を使用して最適化するオプションがあります。
NHをLinqで使用する場合、リポジトリは次のようになります。
session.Linq<Entity>()
仕様は次のものを扱うものです:
IQueryable<Entity>
必要に応じてファサードをすべて外すことができますが、抽象化を抽象化するための多くの日常的な作業です。
シンプルは良い。ああ、NHはデータベースを使用しますが、さらに多くのパターンを提供します。 DAL以外がNHに依存していることは、罪にはほど遠い。
それはあなたのニーズ次第だと思います。使用することを検討している他の設計パターンと併せて、リポジトリについて考えることが重要です。最後に、リポジトリに何を期待するか(それを使用する主な理由)に大きく依存します。
厳密なレイヤーを作成する必要がありますか(たとえば、将来的にNHibernateをEntity Frameworkに置き換える必要があります)?特にリポジトリメソッドにテストを書きますか?
リポジトリを作成する最良の方法はありません。いくつかの方法があり、それは間違いなくあなた次第です。あなたのニーズにとって最も実用的なものです。
リポジトリなし
LosTechiesのJimmy Bogardによる関連記事
また、バージョン2を示唆するコメントを含む別の簡単な記事は、実際にはDOAパターンであり、リポジトリではありません。
http://fabiomaulo.blogspot.com/2009/06 /linq-and-repository.html