CQRS状态:命令不应查询读取端。
好的。让我们来看下面的例子:
用户需要使用订单行创建订单,每个订单行都包含product_id
,price
和quantity
。
它将带有订单信息和订单行列表的请求发送到服务器。
服务器(命令处理程序)不应该信任客户端,并且需要验证提供的产品(product_ids)是否存在(否则会产生大量垃圾)。
由于命令处理程序不允许查询读取端,因此应该以某种方式在写入端上验证此信息。
我们在方面写的东西:存储库。就DDD而言,存储库只能与聚合根一起运行,该存储库只能按ID获取和保存。
在这种情况下,唯一的选择是一个接一个地加载所有产品集合(存储库只有GET BY ID方法)。
注意:事件源被用作持久性,因此一次加载多个聚合以避免避免对存储库的多个请求将是有问题的,并且效率不高。
这种情况下最好的解决方案是什么?
PS:一种解决方案是重新设计UI(更类似于基于任务的UI),例如:用户首先创建订单(带有常规信息),然后一个接一个地添加产品(每个添加单独的http请求),但是我仍然需要支持批量操作(以第三方应用程序的API为例)。
最佳答案
简短的答案:将域服务(请参阅Evans,第5章)与其他命令参数一起传递给集合。
这不是绝对的-在命令处理程序中包含查询时需要权衡取舍;这并不意味着您无法做到。
在domain-driven-design中,我们具有domain service
的概念,这是一种无状态机制,通过该机制,聚合可以从其自身的一致性边界之外的数据中学习信息。
因此,您可以定义一个服务来验证产品是否存在,并在添加商品时将该服务作为参数传递给聚合。计算产品是否存在的工作将在服务接口(interface)之后抽象化。
但是您需要记住的是:大概是在订单汇总之外定义了产品。这意味着它们可以与您的支票同时更改以验证product_id。从正确性的角度来看,在集合中,在应用程序的命令处理程序中或在客户端代码中,检查product_id的有效性之间没有真正的区别。在所有三个地方,您要验证的产品状态可能是陈旧的。
Udi Dahan shared几年前的兴趣观察
如果客户端在编写命令时已经在一百毫秒之前验证了数据,并且数据对它们有效,那么聚合的行为应该是什么?
考虑添加与该产品的订单同时组成的产品的命令-从业务角度来看,系统的正确性是否应该取决于这两个命令碰巧到达的顺序?
要记住的另一件事是,通过将此检查引入聚合中,您将更改聚合的功能与域服务的可用性结合在一起。如果域服务无法获取其所需的数据(因为读取模型已关闭或其他原因),应该怎么办。它会阻塞吗?抛出异常?做一个猜想?这个选择是否会波及到聚合的设计中,依此类推。
关于validation - CQRS DDD : How to validate products existence before adding them to order?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45007667/