在DDD中使用IQueryable和存储库模式

时间:2014-01-15 09:29:05

标签: domain-driven-design repository-pattern ddd-repositories

我需要有关DDD(域驱动设计)的建议以及Repository模式和封装的实现。 据我所知,Repository模式使我能够将所有数据库访问逻辑放在一个地方,并从应用程序的其他部分抽象出该逻辑。 另一方面,有orm(Nhibernate,EntityFramework ...),它支持Linq和IQueryable。 我的强硬是: 1.如果我使用的是存储库,那么我不应该使用IQueryable作为我的返回类型,而是使用IEnumerable。因为如果我使用IQueryable,那么这将允许将数据库代码泄露到其他应用程序层(IE将允许其他开发人员在他们不属于的mvc控制器中进行查询)。 但是每个控件都使用IQueryable来访问数据并且这样做更容易。 如果我使用IQueryable作为我的存储库方法的返回类型,那么: - 我允许其他开发人员在其他应用程序层中进行数据库查询(我认为这不可能) - 它会将我的域实体(域模型)泄漏到其他应用层(即用户界面),但不应该使用,而应该使用DTO。

我的问题是,IQueryable是DDD的一个好习惯吗?

2 个答案:

答案 0 :(得分:3)

我想说这不是一个好习惯。 理想情况下,您应该在域之上使用某种应用程序层。如果直接或通过Query对象公开域对象,有人可能会在您的控件之外实际修改它。

我个人认为IQueryable是一个实现细节,理想情况下我的域不依赖它(如果我想切换我的存储技术,那可能是个问题)。 大多数情况下,我最终会在我的存储库实现内部使用IQueryable。我通常最终得到的是实现具有FindBySpecification方法的通用存储库,然后为从其继承的每个聚合根提供专用存储库。例如:

public interface IRepository<TEntity>        
{
    TEntity Get(Guid ID);
    void Add(TEntity entity);
    void Remove(TEntity entity);
    void Detach(TEntity entity);
    IEnumerable<TEntity> WithSpecification(ISpecification<TEntity> specification);
}

public interface IOrdersRepository : IRepository<Order>
{
    IEnumerable<Order> GetCompletedOrdersForAllPreferedCustomers(DateTime orderCompletedAfter);
    Order GetOrderBySomeOtherComplicatedMeans();
}

另一个方法是将您的应用程序设计为遵循CQRS原则。 然后,您可以让DomainModel在命令端执行它的魔术,并在查询端为您的客户端创建数据的只读模型。 根据您的要求,此设置可以变得非常精细,但它可以像映射到同一数据库的两个ORM模型一样简单(命令端的一个映射您的域实体,查询侧的映射到简单的DTO) 。

答案 1 :(得分:1)

就我个人而言,我不认为通过IQueryable在所有层中暴露EF实体必然是一件坏事。但这只是我自己的看法。其他人可能不同意,尤其是如果你在封装视角上看它。但一般来说,松散耦合的概念是复杂性和实际收益之间的权衡。通过封装IQueryable知道你会失去很多实际的收益,例如延迟加载的能力。

如果您的应用程序层直接位于存储库层之上,我投票使用IEnnumerable而不是IQueryable。但是如果你在中间有一个服务层(我个人更喜欢包含所有业务逻辑,因此存储库层可以专注于数据访问操作),那么我将让存储库返回IQueryable并让服务层在执行后返回IEnnumerable它来自存储库返回的IQueryable对象的业务逻辑。

这些只是我个人的规则:

  1. 如果需要封装EF,请仅将其封装在应用程序层之外。否则,在所有图层上根据需要使用它。 EF非常强大,将其封装在存储库层会让你失去很多力量
  2. 应用程序层应尽可能薄,不应对从其下面的层接收的数据执行任何进一步处理。它接收列表并呈现它,就是它。