【问题标题】:Validate Command in CQRS that related to other domain验证与其他域相关的 CQRS 中的命令
【发布时间】:2017-11-05 17:41:58
【问题描述】:

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

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

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

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

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

谢谢。

【问题讨论】:

标签: validation domain-driven-design microservices cqrs


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    如何知道客户确实存在于数据库中(查询端客服)并且仍然活跃?

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

    如何知道产品存在于数据库中,产品状态是否已发布?

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

    如何知道客户是否有资格获得相关产品的促销价格?

    您可能必须查询另一个有界上下文,例如 Promotions BC 来检查这一点。这取决于如何验证/使用促销活动。

    是否可以直接调用 API(如点对点 / ajax / 请求承诺)来验证此有效负载以进行命令端服务?但我认为,如果只是为了验证而直接调用 API,性能会变得更差。

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

    同步调用更容易实现,但它们会导致系统恢复能力较差(您应该注意级联故障并使用circuit breaker 之类的技术来阻止它们)。

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

    在任何情况下,这都取决于您的业务,我们谁也无法准确告诉您该怎么做。

    【讨论】:

    • 如果验证已经在命令端使用查询读取端完成,我们可以在命令端构建快照。快照字段将与物化视图相同,因为在我们进行验证时会检索所需的所有数据。那么,我们还需要非规范化器或事件处理器来构建物化视图吗?
    • 此外,当我们从其他域订阅事件(例如客户更改事件)时,该事件可能会更改客户订单中的数据。我们是否需要将客户事件持久化到订单事件存储,因为客户事件是在订单域中写入一些更改?
    • @Benedict 当远程事件到达您的本地 bc 时,您有一个选择: 1. 创建一个导致新本地事件的命令,OrderAggregate 使用该命令来验证 addOrderItemCommand 或2. 建立并维护一个验证器的私有状态,用于验证addOrderItemCommand 命令
    猜你喜欢
    • 2012-06-08
    • 2017-01-18
    • 1970-01-01
    • 2021-06-22
    • 2019-05-09
    • 1970-01-01
    • 2015-11-21
    • 2016-07-04
    • 1970-01-01
    相关资源
    最近更新 更多