Domain driven design 如何在DDD中实现CQRS的查询端?

Domain driven design 如何在DDD中实现CQRS的查询端?,domain-driven-design,cqrs,Domain Driven Design,Cqrs,我已经使用域模型和存储库实现了DDD的命令端,但是如何实现查询端呢 我是否为UI创建了一个全新的域模型,它保存在项目结构中的什么地方?在域层、UI层等等 另外,我应该使用什么作为我的查询机制,我应该专门为UI域对象创建新的存储库,而不是存储库,还是其他什么?根据我对CQR的理解,您可以创建一组DTO,以满足用户界面屏幕或可能需要使用它们的应用程序的要求 这在项目中的位置是基于需求的,因为这取决于您是否打算通过web服务公开这些DTO。 在这种情况下,我不会把它放在Web层,而是放在应用层或专用的

我已经使用域模型和存储库实现了DDD的命令端,但是如何实现查询端呢

我是否为UI创建了一个全新的域模型,它保存在项目结构中的什么地方?在域层、UI层等等


另外,我应该使用什么作为我的查询机制,我应该专门为UI域对象创建新的存储库,而不是存储库,还是其他什么?

根据我对CQR的理解,您可以创建一组DTO,以满足用户界面屏幕或可能需要使用它们的应用程序的要求

这在项目中的位置是基于需求的,因为这取决于您是否打算通过web服务公开这些DTO。 在这种情况下,我不会把它放在Web层,而是放在应用层或专用的Façade层

然后您将拥有一个只读存储库或数据访问层,直接填充DTO。我认为查询端应该针对读取性能进行优化,在这种情况下,对数据库视图或表和SqlDataReader的直接查询/存储过程将在这里做得最好。但是,将这种访问抽象到接口后面肯定是值得的,这样您就可以在以后添加缓存实现

如果您使用的是ORM,并且希望从域实体映射到DTO,那么您可以拥有一个通用的QueryRepository,其中的方法采用ISpecification或类似的构造来定义查询,然后是一个DTOAsembler对象,用于从域对象创建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; 
        }
    }
公共接口规范
{
表达式谓词{get;}
}
公共类ActiveCustomerSpecification:ISpecification
{
私有表达式谓词;
公共ActiveCustomerSpecification()
{
谓词=c=>c.IsActive;
}
#地区指定成员
公共表达式谓词
{
获取{返回谓词;}
}
#端区
}
公共接口存储库
{
IQueryable GetQuery(ISpecification规范);
IEnumerable FindAllBy(ISpecification规范);
}
公共类CustomerToAssembler
{
公共CustomerTo AssembleFrom(客户)
{
var customerDto=新customerDto
{
Id=customer.Id
};
退回客户;
}
}
我认为willbt给了你一个非常好的机会

<>我想补充一点,如果你选择继续使用ORM作为查询的数据访问策略,你最好考虑定义一个适合你期望访问的数据的获取策略(顺便说一下,我在这里特别考虑NHiBiNess)。这意味着您可以决定是延迟加载还是急切加载与特定对象关联的对象和集合

Ritesh Rao提供了一个优秀的(正在进行的工作)演示,演示了如何为不同的目的定义不同的抓取策略

在他的博客里写得很好

请继续查看源代码:

  • 这是
  • 这表明
在测试“Repository\u For\u使用\u注册的\u获取\u策略”中,调用

NHRepository<Order>().For<NHRepositoryTests>()
NHRepository().For())

…导致使用针对NHRepositoryTests类注册的获取策略,因此OrderItems和Products将在不干扰NHibernate映射配置的情况下立即加载。

我刚刚看到Jak Charlton的一篇博文,他描述了他决定根本不使用DDD进行查询的原因:“CQS的查询端不需要强类型实体,也不需要强类型DTO,因为维护这些实体主要是临时数据,DTO将为DataTable能够充分处理的事情消耗不成比例的开发时间“这是一个正确的观点。然而,只有当您将实体返回到Facade层,然后映射到dto时,它才真正适用。我的建议是使用返回的IQueryable并将其直接投影到DTO中。这样你就避免了任何策略。您甚至可以在NHibernate映射级别进行投影。当然!是的,谢谢。我仍然在考虑NHibernate.Linq是如何改变游戏的,但是如果你看看Ritesh是如何接受IQueryable的,你会发现他确实“理解”了,并且围绕着IQueryable构建了一些好东西(例如,在构造函数中采用表达式的规范)。我发现这很有趣,你能澄清一件事吗?查询存储库和规范接口,这两者显然都与ORM技术相结合,不是吗?在这种情况下,我猜是实体框架。它不应该是抽象的吗?