我已经使用域模型和存储库实现了 DDD 的命令端,但是如何实现查询端呢?
我是否为 UI 创建了一个全新的域模型,它保存在项目结构中的什么地方……在域层、UI 层等中?
另外,我使用什么作为我的查询机制,我是专门为 UI 域对象创建新的存储库,而不是存储库,还是其他什么?
我已经使用域模型和存储库实现了 DDD 的命令端,但是如何实现查询端呢?
我是否为 UI 创建了一个全新的域模型,它保存在项目结构中的什么地方……在域层、UI 层等中?
另外,我使用什么作为我的查询机制,我是专门为 UI 域对象创建新的存储库,而不是存储库,还是其他什么?
根据我对 CQRS 的理解,您将创建一组 DTO,以满足用户界面屏幕或可能需要使用它们的应用程序的要求。
项目中存在的位置取决于要求,因为这取决于您是否要通过 Web 服务公开这些 DTO。在这种情况下,我不会将它放在 Web 层,而是放在应用程序层或专用的 Façade 层。
然后,您将拥有一个直接填充 DTO 的只读存储库或数据访问层。我认为查询方面应该针对读取性能进行优化,在这种情况下,数据库视图或表上的直接查询/存储过程和 SqlDataReaders 会在这里做得最好。但是绝对值得将这种访问抽象化到接口后面,这样您就可以稍后添加一个缓存的实现。
如果您正在使用 ORM 并希望从您的域实体映射到 DTO,那么您可以拥有一个通用的 QueryRepository,它的方法采用 ISpecification 或类似构造来定义您的查询,然后是一个 DtoAssembler 对象,用于从您的域创建 Dto对象。然后让一个实现为您要执行的每个查询都有一个第一类对象。
这是一个相当人为的例子,但我希望它能给你一个想法。
public interface ISpecification<T>
{
Expression<Func<T, bool>> Predicate { get; }
}
public class ActiveCustomersSpecification : ISpecification<Customer>
{
private Expression<Func<Customer, bool>> predicate;
public ActiveCustomersSpecification()
{
predicate = c => c.IsActive;
}
#region ISpecicfication<Customer> Members
public Expression<Func<Customer, bool>> Predicate
{
get { return predicate; }
}
#endregion
}
public interface IQueryRepository<T>
{
IQueryable<T> GetQuery(ISpecification<T> specification);
IEnumerable<T> FindAllBy(ISpecification<T> specification);
}
public class CustomerDtoAssembler
{
public CustomerDto AssembleFrom(Customer customer)
{
var customerDto = new CustomerDto
{
Id = customer.Id
};
return customerDto;
}
}
我认为willbt给了你一个很好的起点。
我要补充一点,如果您确实选择继续使用 ORM 作为查询的数据访问策略,那么建议您考虑定义一个针对您期望需要访问的数据量身定制的获取策略(我是顺便说一下,这里专门考虑 NHibernate)。这意味着您可以决定是延迟加载还是急切加载与特定聚合根对象关联的对象和集合。
Ritesh Rao的NCommon 项目提供了一个出色的(正在进行中的)演示,展示了如何为不同的目的定义不同的获取策略。
Ritesh在他的博客中很好地解释了这一点。
继续看看源代码:
在测试“Repository_For_Uses_Registered_Fetching_Strategies”中调用
NHRepository<Order>().For<NHRepositoryTests>()
...导致使用针对 NHRepositoryTests 类注册的获取策略,因此 OrderItems 和 Products 将被预先加载,而不会弄乱 NHibernate 映射配置。