Domain driven design CQRS命令和查询。就DDD而言,它们属于应用程序级还是域级?

Domain driven design CQRS命令和查询。就DDD而言,它们属于应用程序级还是域级?,domain-driven-design,cqrs,Domain Driven Design,Cqrs,在阅读不同的DDD文献时,我偶然发现了一个理论问题。问题是我是否应该在应用程序或域级别放置命令和查询 所以,一些作者,比如Scott Wlaschin在他的书《领域建模》中说 如果该命令成功,它将启动一个工作流,然后 将创建相应的域事件 因此,例如PlaceOrder命令和DomainEventOrder命令之间存在对应关系。这让我相信,我应该把命令和事件放在一个层次上,并像这样组织: \Model \Message \Command Plac

在阅读不同的DDD文献时,我偶然发现了一个理论问题。问题是我是否应该在应用程序或域级别放置命令和查询

所以,一些作者,比如Scott Wlaschin在他的书《领域建模》中说

如果该命令成功,它将启动一个工作流,然后 将创建相应的域事件

因此,例如PlaceOrder命令和DomainEventOrder命令之间存在对应关系。这让我相信,我应该把命令和事件放在一个层次上,并像这样组织:

\Model
    \Message
         \Command
             PlaceOrder.lang
         \Event
             OrderPlaced.lang
因此,我将所有命令放在域级别,而应用程序服务调用这些命令并将它们包装在事务中

然而,还有另一种观点,在Scott Millett的《领域驱动设计的模式、原则和实践》一书中表达。引文说:

命令是一项业务任务,是一个系统的用例,并且是有效的 在应用层中。您应该使用业务语言编写命令

由于这一矛盾,我不太确定处理命令和查询的最规范的方法是什么。在现实世界中,他们是生活在域级还是应用程序级

由于这一矛盾,我不太确定处理命令和查询的最规范的方法是什么。在现实世界中,他们是生活在域级还是应用程序级

文学在这一点上有点混乱,主要是因为历史的原因

当我们谈论消息时,也就是说我们的应用程序/服务的API;这些属于域模型之外

根本问题在于,消息模式是应用程序和与其对话的客户端之间的契约定义的一部分。合同,尤其是那些跨越组织边界的合同,需要保持稳定,因为变更的成本很高

与此相比,域模型的内存表示形式纯粹是一个实现细节,可以随时更改。你的数据模型在中间的某个地方——客户端不关心你的信息在持久存储中的样子,但是你的未来应用程序需要能够读取前任留下的信息。 当说服务共享契约和模式,而不是类或类型时,他描述的是正在交换的消息。客户机不需要关心我们在域模型的实现中使用什么类或类型

现在,公平地说,客户机也不需要关心我们在应用程序实现中使用的类或类型。您获取PlaceOrder消息(由网络上发送给您的字节序列表示的语义),并将其表示为类型化内存引用的内存内安排,这是您自己的事情

这里我们要注意的是,负责解释字节的代码属于负责与其他事物通信的代码部分,而不是管理业务内存中抽象的代码部分

由于这一矛盾,我不太确定处理命令和查询的最规范的方法是什么。在现实世界中,他们是生活在域级还是应用程序级

文学在这一点上有点混乱,主要是因为历史的原因

当我们谈论消息时,也就是说我们的应用程序/服务的API;这些属于域模型之外

根本问题在于,消息模式是应用程序和与其对话的客户端之间的契约定义的一部分。合同,尤其是那些跨越组织边界的合同,需要保持稳定,因为变更的成本很高

与此相比,域模型的内存表示形式纯粹是一个实现细节,可以随时更改。你的数据模型在中间的某个地方——客户端不关心你的信息在持久存储中的样子,但是你的未来应用程序需要能够读取前任留下的信息。 当说服务共享契约和模式,而不是类或类型时,他描述的是正在交换的消息。客户机不需要关心我们在域模型的实现中使用什么类或类型

现在,公平地说,客户机也不需要关心我们在应用程序实现中使用的类或类型。您获取PlaceOrder消息(由网络上发送给您的字节序列表示的语义),并将其表示为类型化内存引用的内存内安排,这是您自己的事情

这里我们要注意的是,负责解释字节的代码属于t
代码中负责与其他事物通信的部分,而不是管理业务内存中抽象的部分。

至于我,DDD的主要思想是关于无处不在的语言、有界上下文和上下文映射。因此,如果我们关注这些概念,所有其他部分都将成为实现细节

15年前,当蓝皮书问世时,正是Java时代。现在有不同的范例来来去去

我喜欢将命令、请求、事件和响应分为不同的级别。主要问题是你很快就会缺少合适的词语。但这是一个众所周知的编程问题

实现的核心是词汇表。它不是一种普遍存在的语言,但可能是它的大部分。在那里,我对特定于域的类和结构进行建模,主要使用不可变的数据类型。你把工厂和不变量放在那里。词汇表不依赖于代码的其他部分

其次,业务核心有决策者,他们接受命令并产生事件或拒绝。命令旨在更改系统的状态。事件是关于更改系统状态的决策。两者都是不可变的,并用词汇表达。该命令包含制定决策所需的全面数据,因此决策者无需从某处检索其他数据。因此,决策者可以是一个纯函数

命令和事件是系统业务核心的内部部分。但是您可以获取请求并以一些有线格式发送响应,比如JSON、XML、ProtoBuf等等。您应该封送/解封它,添加额外的数据,如身份验证/标识、不同的跟踪ID,并将其转换为一些对象模型,而这些对象模型对导线格式一无所知。我将这些对象模型称为业务请求和业务响应。这属于应用层

光是请求和响应是不够的。除了它们的数据之外,您还需要从存储库和其他依赖项中检索额外的数据,并在做出状态更改决定后保持这些数据。因此,获取业务请求、从依赖项收集数据、构建命令、将其发送给决策者、获取事件或拒绝、执行此决策并将其存储到存储库等,以及生成业务响应——这些都是应用程序层的职责

我不描述读取模型,因为只要来自读取模型的响应到达用词汇表中的数据类型表示的应用程序层,它们就可以以许多不同的方式实现

因此,与业务请求/响应之间的连接格式和转换器属于基础架构适配器层。业务请求和响应及其与命令/事件/拒绝之间的转换属于应用程序层,也属于为决策和执行决策准备数据的任务。命令/事件/拒绝属于功能核心


像请求、响应、命令、事件这样的术语并没有太大帮助:它们太多了,而且有点讽刺的是,我们在非模糊的无处不在的语言中使用它们。但我还是希望这能有所帮助。

至于我,DDD的主要思想是关于无处不在的语言、有界上下文和上下文映射。因此,如果我们关注这些概念,所有其他部分都将成为实现细节

15年前,当蓝皮书问世时,正是Java时代。现在有不同的范例来来去去

我喜欢将命令、请求、事件和响应分为不同的级别。主要问题是你很快就会缺少合适的词语。但这是一个众所周知的编程问题

实现的核心是词汇表。它不是一种普遍存在的语言,但可能是它的大部分。在那里,我对特定于域的类和结构进行建模,主要使用不可变的数据类型。你把工厂和不变量放在那里。词汇表不依赖于代码的其他部分

其次,业务核心有决策者,他们接受命令并产生事件或拒绝。命令旨在更改系统的状态。事件是关于更改系统状态的决策。两者都是不可变的,并用词汇表达。该命令包含制定决策所需的全面数据,因此决策者无需从某处检索其他数据。因此,决策者可以是一个纯函数

命令和事件是系统业务核心的内部部分。但是您可以获取请求并以一些有线格式发送响应,比如JSON、XML、ProtoBuf等等。您应该封送/解封它,添加额外的数据,如身份验证/标识、不同的跟踪ID,并将其转换为一些对象模型,而这些对象模型对导线格式一无所知。我将这些对象模型称为业务请求和业务响应。这是欧恩 由应用程序层生成

光是请求和响应是不够的。除了它们的数据之外,您还需要从存储库和其他依赖项中检索额外的数据,并在做出状态更改决定后保持这些数据。因此,获取业务请求、从依赖项收集数据、构建命令、将其发送给决策者、获取事件或拒绝、执行此决策并将其存储到存储库等,以及生成业务响应——这些都是应用程序层的职责

我不描述读取模型,因为只要来自读取模型的响应到达用词汇表中的数据类型表示的应用程序层,它们就可以以许多不同的方式实现

因此,与业务请求/响应之间的连接格式和转换器属于基础架构适配器层。业务请求和响应及其与命令/事件/拒绝之间的转换属于应用程序层,也属于为决策和执行决策准备数据的任务。命令/事件/拒绝属于功能核心


像请求、响应、命令、事件这样的术语并没有太大帮助:它们太多了,而且有点讽刺的是,我们在非模糊的无处不在的语言中使用它们。但我还是希望这会有所帮助。

命令和查询肯定是在应用程序级别

它们正在调用应用程序服务方法,这些方法负责跨事务、高级日志记录和委托给业务对象


查询服务也位于应用程序级别,返回域的优化视图,通常不使用域存储库,而是直接访问数据。

命令和查询肯定位于应用程序级别

它们正在调用应用程序服务方法,这些方法负责跨事务、高级日志记录和委托给业务对象


查询服务也在应用程序级别,返回域的优化视图,通常不使用域存储库,而是直接访问数据。

顺便说一句,Millett在他的书中提供了一个例子,他将命令和事件视为消息。因此,创建的处理程序以相同的方式处理命令和事件。换句话说,事件和命令在体系结构方面处于同一级别。但是,如果命令位于应用层,而像OrderPlaced这样的事件显然位于域级别,那么我们如何将它们视为消息呢。我相信这里重要的是尊重依赖性的方向:基础设施依赖于域,而不是相反。顺便说一句,Millett在他的书中提供了一个例子,他将命令和事件视为消息。因此,创建的处理程序以相同的方式处理命令和事件。换句话说,事件和命令在体系结构方面处于同一级别。但是,如果命令位于应用层,而像OrderPlaced这样的事件显然位于域级别,那么我们如何将它们视为消息呢。我相信这里重要的是尊重依赖性的方向:基础设施依赖于领域,而不是反过来。我也倾向于这样想。但是,我相信命令和静默不会调用应用程序服务方法。它们本身就是服务方法,由应用程序服务调用。我认为,在命令或优化视图的情况下,它们是域模型或服务的直接客户端。这是一种有趣的建模方式,在DDD中,没有坏模型,只有有用的模型。沃恩·弗农(Vaughn Vernon)在他的书中决定将命令建模为对象,作为应用程序服务方法的参数传递。您使用哪种语言编写解决方案?如果是Java,我强烈建议您查看Vaughn Vernon的存储库,他是实现域驱动设计的作者:如何验证用户是否有权查看正在查询的数据,而无需通过域业务规则?例如,在不验证诸如post.owneraAccount.visibility==PUBLIC | | user.isfollowPost.oweneraAccount之类的不变量的情况下,如何查询给定用户在Instagram上看到的帖子。我的意思是,我知道你可以在底层的查询中封装这个逻辑,但它不会导致业务逻辑被嵌入到应用程序的细节中,例如,一个SQL查询,与其在它所属的域代码中,不如以ACL或用户名的形式对其进行投影,然后根据主体的身份对查询服务进行过滤。我也倾向于这样想。但是,我相信命令和静默不会调用应用程序服务方法。它们本身就是服务方法,卡尔
由应用服务部牵头。我认为,在命令或优化视图的情况下,它们是域模型或服务的直接客户端。这是一种有趣的建模方式,在DDD中,没有坏模型,只有有用的模型。沃恩·弗农(Vaughn Vernon)在他的书中决定将命令建模为对象,作为应用程序服务方法的参数传递。您使用哪种语言编写解决方案?如果是Java,我强烈建议您查看Vaughn Vernon的存储库,他是实现域驱动设计的作者:如何验证用户是否有权查看正在查询的数据,而无需通过域业务规则?例如,在不验证诸如post.owneraAccount.visibility==PUBLIC | | user.isfollowPost.oweneraAccount之类的不变量的情况下,如何查询给定用户在Instagram上看到的帖子。我的意思是,我知道你可以在底层的查询中封装这个逻辑,但它不会导致业务逻辑被嵌入到应用程序的细节中,例如,一个SQL查询,您可以以ACL或用户名的形式对其进行投影,然后根据主体的身份在查询服务上进行过滤,而不是位于它所属的域代码中。