CQRS DDD:如何在将产品添加到订单之前验证产品是否存在?

Tei*_*raz 2 validation domain-driven-design cqrs

CQRS声明:命令不应该查询读取端.

好.我们来看下面的例子:

用户需要与订单行创建订单,每个订单行包含product_id,price,quantity.

它通过订单信息和订单行列表向服务器发送请求.

服务器(命令处理程序)不应该信任客户端,并且需要验证提供的产品(product_ids)是否存在(否则,将会有大量垃圾).

由于不允许命令处理程序查询读取端,因此它应该以某种方式在写入端验证此信息.

我们在写作方面有什么:存储库.就DDD而言,存储库仅与Aggregate Roots一起运行,存储库只能使用GET BY ID和SAVE.

在这种情况下,唯一的选择是逐个加载所有产品聚合(存储库只有GET BY ID方法).

注意:事件源是用作持久性的,因此一次加载多个聚合以避免对存储库的多个请求会产生问题并且效率不高.

这种情况的最佳解决方案是什么?

PS:一个解决方案是重新设计UI(更像是基于任务的UI),例如:用户首先创建订单(使用常规信息),然后逐个添加产品(每个添加单独的http请求),但我仍然需要支持批量操作(以第三方应用程序为例).

Voi*_*son 6

简短的回答:将域服务(参见Evans,第5章)与其他命令参数一起传递给聚合.

CQRS声明:命令不应该查询读取端.

这不是绝对的 - 当您在命令处理程序中包含查询时,会涉及权衡; 这并不意味着你不能这样做.

,我们有a的概念domain service,它是一种无状态机制,通过该机制,聚合可以从其自身一致性边界之外的数据中学习信息.

因此,您可以定义验证产品是否存在的服务,并在添加项目时将该服务作为参数传递给聚合.计算产品是否存在的工作将在服务接口后面进行抽象.

但是你需要记住的是:产品,可能是在订单聚合之外定义的.这意味着它们可以与您的支票同时更改以验证product_id.从正确性的角度来看,在聚合中,或在应用程序的命令处理程序中或在客户端代码中检查product_id的有效性之间没有真正的区别.在这三个地方,您要验证的产品状态可能是陈旧的.

几年前Udi Dahan分享了一项兴趣观察

时间上的微秒差异不应对核心业务行为产生影响.

如果客户端在编写命令时已经在100毫秒前验证了数据,并且数据有效,那么聚合的行为应该是什么?

考虑一个命令,添加一个与同一产品的订单同时组成的产品 - 从业务角度来看,系统的正确性是否取决于这两个命令恰好到达的顺序?

另外要记住的是,通过将此检查引入聚合,您可以将聚合更改为域服务的可用性.如果域服务无法访问所需的数据(因为读取的模型已关闭,或者其他),应该会发生什么.它会阻止吗?抛出异常?做一个猜想?这个选择是否会回归到聚合的设计中,等等.