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

时间:2017-07-10 08:54:48

标签: validation domain-driven-design cqrs

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

确定。让我们举个例子:

用户需要创建包含订单行的订单,每个订单行包含product_idpricequantity

它向服务器发送包含订单信息和订单行列表的请求。

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

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

我们在写入方面:存储库。就DDD而言,存储库仅使用聚合根进行操作,存储库只能使用GET BY ID和SAVE。

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

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

此案例的最佳解决方案是什么?

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

1 个答案:

答案 0 :(得分:6)

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

  

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

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

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

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

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

Udi Dahan shared多年前的兴趣观察

  

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

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

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

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