实现存储库模式的最佳方式?

时间:2009-09-11 01:41:40

标签: c# domain-driven-design repository-pattern bdd

我一直在探索BDD / DDD,因此试图找到Repository模式的正确实现。到目前为止,很难就实现这一点的最佳方式达成共识。我试图将其归结为以下变化,但我不确定哪种方法最好。

作为参考,我正在构建一个以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);
}

我最初的想法是

从效率的角度来看,这是非常好的,但随着事情变得更加复杂,我可能会遇到麻烦。

2)看起来非常繁琐,可能最终会出现一个非常拥挤的课程,但除此之外,我的域逻辑和数据层之间存在很大程度的分离。

3)预先看起来很困难,编写查询的工作也比较多,但是只限于Specs图层的交叉污染。

4)我最不喜欢的,但可能是最直接的实现,可能大多数数据库对复杂查询有效,尽管它对调用代码负有很多责任。

7 个答案:

答案 0 :(得分:22)

对于“以上都不是”的方法,也有一个很好的论据。

通用存储库的问题在于,您假设系统中的所有对象都支持所有四种CRUD操作:创建,读取,更新,删除。但在复杂系统中,您可能只有少数几个操作的对象。例如,您可能具有只读对象或已创建但从未更新的对象。

您可以将IRepository接口分解为小型接口,用于读取,删除等,但这很快就会变得混乱。

Gregory Young提出了一个很好的论据(从DDD /软件分层的角度来看),每个存储库应该只支持特定于域对象或您正在使用的聚合的操作。这是他关于generic repositories的文章。

对于另一种观点,请参阅此Ayende blog post

答案 1 :(得分:10)

我认为他们都是不错的选择(如果你不想把自己束缚于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);
}

还有一个Repository(of T)基类。

答案 2 :(得分:6)

我们正在做的一件事是我们所有的存储库都有不同的需求,所以我们正在创建一个接口集合:

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)
    {
    }
}

我还可以为Add,Update和Delete创建单独的界面。这样,如果我的存储库不需要该行为,那么它就不会实现该接口。

答案 3 :(得分:1)

我是1的粉丝,因为我可以创建过滤器和分页扩展方法,而不是我可以应用于IQueryable&lt;&gt;返回Find方法的值。我将扩展方法保留在数据层中,然后在业务层中即时构建。 (诚​​然,并非完全纯粹。)

当然,当系统稳定时,我可以选择使用相同的扩展方法制作特定的Find方法,并使用Func&lt;&gt;进行优化。

答案 4 :(得分:1)

在Linq中使用NH时,您的存储库可以是:

session.Linq<Entity>()

规范是涉及的事项:

IQueryable<Entity>

如果你愿意的话,你可以将它全部展开,但这是抽象抽象的很多平凡的工作。

简单就是好。是的,NH做数据库,但它提供了更多的模式。除了DAL之外,不依赖于NH远非罪恶。

答案 5 :(得分:1)

我认为这取决于您的需求。将存储库与您考虑使用的其他设计模式结合使用非常重要。最后,它取决于您对存储库的期望(使用它的主要原因是什么)。

您是否需要创建严格的图层(例如,您将来需要将NHibernate替换为Entity Framework)?您是否想要特别针对存储库方法编写测试?

没有最好的方法来创建存储库。只有几种方式,它绝对取决于您,最符合您需求的是什么。

答案 6 :(得分:0)

保存库

LosTechies的Jimmy Bogard的相关文章

http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/10/wither-the-repository.aspx

此外,另一篇快速文章,其中一些评论表明版本#2实际上是DOA模式,而不是存储库。

http://fabiomaulo.blogspot.com/2009/06/linq-and-repository.html