在 CQRS 中验证与其他域相关的命令

Validate Command in CQRS that related to other domain

我正在学习使用 DDD、CQRS 和 ES 开发微服务。它是 HTTP RESTful 服务。微服务是关于网上商店的。有几个域,如产品、订单、供应商、客户等。域内置在单独的服务中。如果命令有效负载涉及其他域,如何进行验证?

例如,这里是订单服务(命令端)中的 addOrderItemCommand 负载。

{
"customerId": "CUST111", 
"productId": "SKU222",
"orderId":"SO333"
}

如何验证上面的命令?如何知道客户是否真的存在于数据库中(查询端客户服务)并且仍然活跃?如何知道产品在数据库中存在并且产品状态已发布?如何知道客户是否有资格获得相关产品的促销价格?

是否可以直接调用 API(如点对点/ajax/请求承诺)来验证此有效负载以便命令端服务?但我认为,如果 API 直接调用只是为了验证,性能会变得更差。因为,我们在命令服务之外开发了一个事件处理器,用于侦听事件并将事件应用于物化视图。

谢谢。

一如既往,一切都取决于域的具体情况,但作为一般原则,跨域验证应通过读取模型完成。

在这种情况下,我会在每个微服务中维护一个读取模型以用于验证。当然,这带来了最终一致性的问题。 你如何处理它应该来自你对领域的理解。应考虑诸如与更新频率相比最终一致性的长度等因素。与最小化问题的开发成本相比,业务出错的成本。在许多情况下,仅记录出现问题的事实对于业务来说就足够了。

我有一个博客 post 专门用于验证,您可以在这里找到它:How To Validate Commands in a CQRS Application

由于需要查询多个限界上下文才能通过验证,因此您需要考虑最终一致性。也就是说,整个过程总是有可能在 "small" 时间内处于无效状态。例如,可以在接受命令后和订单发货前停用用户。网上商店是一个复杂的系统,它的任何一个子系统都可能出现异常。但是,作为事件驱动系统实施会有所帮助;每次下单过程进入无效状态都可以进行补偿actions/commands。例如,如果用户在此期间被停用,您可以取消其所有长期订单,释放保留的产品,向潜在客户宣布这些产品在愿望清单中不可用等等。

DDD 中有很多种验证,但我遵循的一般规则是验证应尽早完成,但不会影响数据一致性。因此,为了尽早,您可以查询 readmodel 以拒绝可能无效的命令,并且为了使系统保持一致,您需要在订单发货前进行另一次检查。

现在让我们谈谈您的具体问题:

How to know that the customer is really exists in database (query-side customer service) and still active?

您可以查询读取模型来验证用户是否存在并且它仍然处于活动状态。您应该这样做,因为来自无效用户的命令是某种攻击的强烈指示,您不希望这些命令通过您的系统。但是,即使命令通过了此检查,也不一定意味着该订单将被发货,因为这期间可能会引发其他异常。

How to know that the product is exists in database and the status of the product is published?

同样,您可以查询 readmodel 以通知用户该产品目前不可用。或者,根据您的业务,如果您根据之前的一些统计数据知道这些产品将在 24 小时内可用(例如,您知道电视机每天都会到达您的库存),则可以允许命令通过。或者您可以让客户选择是否等待。在这种情况下,如果产品在订购的最后阶段(运输)没有库存,您会通知客户产品不再有库存。

How to know whether the customer eligible to get the promo price from the related product?

您可能需要查询另一个限界上下文,例如 Promotions BC 来检查这一点。这取决于促销方式 validated/used.

Is it ok to call API directly (like point-to-point / ajax / request promise) to validate this payload in order command-side service? But I think, the performance will get worse if the API called directly just for validation.

这取决于您希望系统的弹性如何以及拒绝无效命令的速度。

同步调用更易于实现,但会导致系统弹性降低(您应该了解级联故障并使用 circuit breaker 等技术来阻止它们)。

异步(即使用事件)调用更难实现,但可以使您的系统更具弹性。为了进行异步调用,ordering 系统可以订阅其他系统的事件并维护一个私有状态,当命令到达时可以查询该状态以进行验证。这样,即使 link 到 inventorycustomer management 系统宕机,订购系统也能继续工作。

无论如何,这真的取决于您的业务,我们 none 可以准确地告诉您该怎么做。