【问题标题】:CQRS DDD: How to validate products existence before adding them to order?CQRS DDD:如何在将产品添加到订单之前验证产品是否存在?
【发布时间】:2017-12-13 22:18:30
【问题描述】:

CQRS 状态:命令不应查询读取端。

好的。举个例子:

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

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

服务器(command handler)不应信任客户端,需要验证提供的产品(product_ids)是否存在(否则会产生大量垃圾)。

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

我们在编写方面拥有的东西:存储库。在 DDD 方面,repositories 只能使用 Aggregate Roots 操作,repository 只能 GET BY ID 和 SAVE。

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

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

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

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

【问题讨论】:

  • 甚至更好:您不仅需要验证产品是否存在,还需要确保在应用代码验证和将其保存到数据库之间,该产品没有从数据库中删除之后(成功验证后):-)。这就是为什么 CQRS 是否是一件好事的问题的原因之一。此外,还有更多考虑因素可以正确解决这个问题。一个是:您的产品添加应该是原子的,还是应该添加那些存在并以某种方式为那些不存在的产品生成错误?您的数据库可能是这里最好的选择。
  • 如果您想要原子操作,请将所有内容放入数据库事务中,将 ORders 表中的 ItemProductId 设置为 Products 表中的 ForeignKey 并在您的代码中检查是否存在 ForeignKeyViolation 异常(实现取决于您的应用程序和数据库平台)。
  • 您为什么不考虑将行项目封装为客户请求的对象。从客户那里获得 CustomerRequest 后,您可以创建订单并根据请求添加其行,始终使用产品存储库来获取现有产品。
  • @SebastianOliveri,我在问题中提到过,这不是一个好的选择,因为我需要加载多个聚合根,(对存储库的多个请求以检查每个提供的产品)。更重要的是,存储库是作为事件溯源实现的,因此加载许多 AR 效率不高。 (嗯,也许没有那么低效?)
  • @Teimuraz 我也面临这个问题。但是为什么服务器不应该信任客户端。我认为 CQRS 的客户端意味着我们的应用程序服务器不是它。为什么我们不应该在访问命令处理程序之前验证事物。

标签: validation domain-driven-design cqrs


【解决方案1】:

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

CQRS 状态:命令不应查询读取端。

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

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

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

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

Udi Dahan shared多年前的一次兴趣观察

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

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

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

要记住的另一件事是,通过将此检查引入您的聚合,您将更改聚合的能力与域服务的可用性相结合。如果域服务无法获得所需的数据(因为读取模型已关闭,或其他原因),应该发生什么。它会阻塞吗?抛出异常?做一个猜想?这种选择是否会影响到聚合的设计,等等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    • 2013-01-06
    • 1970-01-01
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多