リポジトリパターンの正しい定義を見つけようとしているのはすべてです。
私の最初の理解はこれでした(非常に唖然としました)
私は実際に2つの異なる実装を見てきましたが、オンラインで正式な例はありません。私が見たものは本に隠れています。
実装1:
public Interface IRepository<T>{
List<T> GetAll();
void Create(T p);
void Update(T p);
}
public interface IProductRepository: IRepository<Product> {
//Extension methods if needed
List<Product> GetProductsByCustomerID();
}
実装2:
public interface IProductRepository {
List<Product> GetAllProducts();
void CreateProduct(Product p);
void UpdateProduct(Product p);
List<Product> GetProductsByCustomerID();
}
1つ目は一般的なGet/Update/GetAllなどであり、2つ目は私が「DAO」を定義するようなものです。
どちらもデータエンティティからの抽出を共有します。私は好きですが、単純なDAOでも同じことができます。ただし、2番目の部分は、私が価値を見出しているアクセス操作を標準化します。この企業全体を実装すると、リポジトリのアクセス方法のセットを簡単に知ることができます。
データへのアクセスの標準化がこのパターンの不可欠な部分であると仮定するのは間違っていますか?両方が正しい場合、なぜ実装2を選択するのでしょうか。
私はodedによって引用されたファウラーの引用を2番目にしています。彼が「コレクション-like」インターフェースと言ったことを指摘したいと思います。インターフェースのようなコレクションをどのように実装するかは確かにあなた次第ですが、それがリモートデータソースを表すという事実を隠そうとすることはできませんし、そうすべきでもありません。したがって、リモートデータストアに変更をフラッシュする必要がないインメモリコレクションとは大きく異なります。 ORMまたは独自のロールソリューションの変更追跡メカニズムによって、これを発信者に対してどの程度透過的にすることができるかが決まります。通常、削除は明示的にマークする必要があり、挿入は検出可能(到達可能性による永続性)であり、更新も明示的にマークする必要がある場合があります。これを集約ルートの複雑な依存関係と組み合わせると、コレクションのようなものではないことがわかります。
「標準的なリポジトリの実装」のようなものはありません。
ジェネリックリポジトリの基本クラスの支持者と、各リポジトリを独自に実装することを好む人々の間には、絶え間ない戦いが続いています。一般的な実装は単純なシナリオでは魅力的ですが、非常にリークの多い抽象化であることがよくあります。たとえば、一部のアグリゲートはソフト削除(仮想メソッドのオーバーライドを介して切断可能)のみである場合がありますが、他のアグリゲートは削除操作をまったくサポートしていない場合があります。
どのルートを取るかを決める前に、各アプローチの意味を理解してください。 Greg Youngは、一般的なリポジトリのメリットについて優れた投稿をしています。
http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx
MartinFowlerの「PatternsofEnterprise Application Architecture」によると、リポジトリパターンの定義は次のとおりです。
ドメインオブジェクトにアクセスするためのコレクションのようなインターフェイスを使用して、ドメインとデータマッピングレイヤーの間を仲介します。
したがって、どちらのアプローチも正しいです。
私は汎用リポジトリパターンの大ファンですが、特に汎用インターフェイスのコードは定義できるものと同じであることが多いため、非常に大きな制限になる可能性があるため、インターフェイスから直接継承しないことを強く検討する必要があると思います。抽象基本クラスでは、クラス内に複数の汎用リポジトリを含めることはできなくなります。
IProductRepository実装者に委任を介してジェネリックIRepository<Product>
にアクセスさせ、コンストラクターを介してそれを注入することをお勧めします。これにより、おそらく多くのIRepositoryのクラスを構成し、意味のある方法で単一のインターフェイスの背後にグループ化できます。
私はこのトピックについてブログを書きましたが、NHibernateを具体的に参照していますが、このパターンはあらゆるタイプのリポジトリに適用できます: 一般的で拡張可能なNHiberateリポジトリバージョン2の作成
.NETにLINQが導入されたことで、一般的なリポジトリパターンの実現がはるかに簡単になりました。
public interface IRepository<T> : IQueryable<T>
{
void Add(T item);
void Remove(T item);
}
リポジトリとしての資格を得るには、基盤となるストア(IQueryable
によって簡単に提供されます)のデータにアクセスし、含まれているデータを変更できる必要があります。
ベースインターフェイスに拡張機能を提供して、エンティティ固有の動作(SQLベースのリポジトリのストアドプロシージャ呼び出しへのワイヤリングなど)のフックを提供できますが、操作の大部分は単純なインターフェイスで完了できます。
ジェネリックリポジトリインターフェイス(実装1)とロール固有のリポジトリのバリエーション(実装2)に加えて、ジェネリックメソッドリポジトリを検討することもできます。
public interface IRepository
{
void Save<ENTITY>(ENTITY entity) where ENTITY : DomainEntity;
ENTITY Load<ENTITY>(Guid id) where ENTITY : DomainEntity;
IQueryable<ENTITY> Query<ENTITY>() where ENTITY : DomainEntity;
IQueryable<ENTITY> Query<ENTITY>(IDomainQuery<ENTITY> whereQuery)
where ENTITY : DomainEntity;
}
この3番目のバージョンは、Jimmy Bogardによる this blogpost からのものであり、彼は汎用リポジトリインターフェイスの設定も表現しています。私は通常、このインターフェイスを実装する汎用リポジトリベースクラスを使用します。そうすれば、ドメインエンティティごとに異なるものを実装するだけで済みます。
リポジトリパターンは、ソフトウェア開発で最も使用されるパターンの1つです。あなたの質問への回答としてマークできる投稿はたくさんあります。私が強調したいのは、IoC(Autofac、Windsorなど)を使用すると、優れたリポジトリの実装が改善されるという事実です。私はずっと前に、いくつかのADO.NETベースのフレームワーク(LinqToSql、EF)とNHibernateで遊んでいました。 IoCを使用すると、一般的な実装から常にメリットを得ることができます。特定のリポジトリのインターフェースを定義し、特定のアクションが本当に必要なときに解決できます。
私は通常、継承ではなく構成を使用して汎用リポジトリを使用します。これにより、公開するメソッドを制御できる汎用実装の利点が得られます。
このようなもの:
public Interface IRepository<T>{
List<T> GetAll();
void Create(T p);
void Update(T p);
}
public interface IProductRepository {
//Extension methods if needed
List<Product> GetProductsByCustomerID();
List<T> GetAll();
void Create(T p);
//Let assume here you should not be able to update the products
}
public ProductRepository : IProductRepository {
private IRepository _repository;
public ProductRepository(IRepository repository) {
this._repository = repository;
}
List<T> GetAll()
{
_repository.GetAll();
}
void Create(T p)
{
_repository.Create(p);
}
List<Product> GetProductsByCustomerID()
{
//..implementation goes here
}
}