【发布时间】:2017-12-13 22:18:30
【问题描述】:
CQRS 状态:命令不应查询读取端。
好的。举个例子:
用户需要创建带有订单行的订单,每个订单行包含product_id、price、quantity。
它向服务器发送带有订单信息和订单行列表的请求。
服务器(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