阅读不同的DDD文献时,我偶然发现了一个理论问题。问题是我应该在应用程序级别还是在域级别放置命令和查询。
所以,像Scott Wlaschin(在他的《领域建模使功能》一书中)这样的作者说
如果命令成功执行,它将启动工作流程,该工作流程依次 将创建相应的域事件
因此,例如“下订单”命令和域事件“下订单”之间存在对应关系。这使我相信,我应该将命令和事件放在一个级别上,并像这样进行组织:
\Model
\Message
\Command
PlaceOrder.lang
\Event
OrderPlaced.lang
因此,我将所有命令置于域级别,而应用程序服务调用这些命令并将其包装在例如事务中。
但是,在Scott Millett的书中(领域驱动设计的模式,原理和实践)表达了另一种观点。引言说:
命令是一项业务任务,是系统的用例,它存在 在应用程序层中。您应该使用业务语言编写命令。
由于这个矛盾,我不确定,处理命令(以及查询)的最规范的方法是什么。在现实世界中,它们生活在域还是应用程序级别?
答案 0 :(得分:2)
命令和查询肯定在应用程序级别。
他们正在调用应用程序服务方法,这些方法负责跨越事务,高级日志记录并委托给业务对象。
查询服务也在应用程序级别,通常不使用域存储库而是直接访问数据,从而返回域的优化视图。
答案 1 :(得分:1)
由于这个矛盾,我不确定,处理命令(以及查询)的最规范的方法是什么。在现实世界中,它们生活在域还是应用程序级别?
主要由于历史原因,文学在这一点上令人困惑。
当我们谈论消息时,也就是说我们的应用程序/服务的API;这些属于领域模型之外。
根本问题是消息架构是您的应用程序和与其交谈的客户端之间的合同定义的一部分。合同,尤其是那些跨越组织边界的合同,需要保持稳定,因为变更的成本很高。
将此与域模型的内存中表示形式进行对比,后者纯粹是实现细节,可以随时更改。您的数据模型位于中间位置–客户端不在乎持久存储中的信息,但是您将来的应用程序需要能够读取前任遗留的信息。
当Udi Dahan说服务“共享合同和模式,而不是类或类型”时,他描述的是正在交换的消息。不需要客户关心我们在域模型的实现中使用什么“类或类型”。
现在,公平地说,客户不需要关心我们在应用程序 的实现中使用的“类或类型”。您采用PlaceOrder message (由网络上发送给您的字节序列表示的语义)并将其表示为键入的内存引用的内存排列的事实是您自己的事。
我们在这里要注意的是,负责解释字节的代码属于 部分,并负责与其他事物进行通信,而不是管理业务内存中抽象的代码部分。
答案 2 :(得分:1)
对于我来说,DDD的主要思想是关于泛在语言,有界上下文和上下文映射。因此,如果我们专注于这些概念,那么所有其他部分都将成为实现细节。
15年前,当编写《蓝皮书》时,是Java的时代。如今,出现了不同的范例。
我喜欢将命令,请求,事件和响应分成不同的级别。主要问题是您很快就会缺少合适的单词。但这是编程中的一个众所周知的问题。
实施的核心是词汇。它不是全部无所不在的语言,但可能是其中的大部分。我在那里建模领域特定的类和结构,主要使用不可变的数据类型。在那里,您放置了工厂和不变式。词汇不依赖于代码的其他部分。
接下来,业务核心具有决策者,这些决策者接受命令并产生事件或拒绝。命令旨在更改系统状态。事件是有关更改系统状态的决策。两者都是不可变的,并使用词汇表述。 Command包含制定决策所需的全面数据,因此决策者无需从某个地方检索其他数据。因此,决策者可以是纯粹的职能。
命令和事件是系统业务核心的内部。但是您会收到请求并以某种有线格式(例如JSON,XML,ProtoBuf等)发送响应。您应该封送/取消封送,添加其他数据,例如身份验证/标识,不同的跟踪标识,然后将其转换为某种对导线格式一无所知的对象模型。我称这些对象模型为业务请求和业务响应。这归应用层所有。
仅请求和响应是不够的。除了它们的数据外,您还需要从存储库和其他依赖项中检索其他数据,并在做出状态更改决定后继续存在。因此,获得业务请求,从依赖项收集数据,构建命令,将其发送给决策者,获取事件或拒绝,执行此决策(存储到存储库等)并产生业务响应-这些都是责任一个应用程序层。
我没有描述读取模型,因为只要来自读取模型的响应到达使用词汇表中的数据类型表示的应用程序层,它们就可以通过许多不同的方式实现。
因此,到/来自业务请求/响应的有线格式和转换器属于基础结构适配器层。业务请求和响应以及它们与命令/事件/拒绝之间的转换属于应用程序层(以及为决策和执行决策准备数据的任务)。命令/事件/拒绝属于功能核心。
诸如请求,响应,命令,事件之类的术语不是很有用:它们非常重载,并且具有讽刺意味的是,我们在不模糊的普遍语言领域使用它们。但我仍然希望这会有所帮助。