我正在学习使用DDD,CQRS和ES开发微服务。它是HTTP RESTful服务。微服务是关于网上商店。有几个领域,如产品,订单,供应商,客户等。这些域构建在单独的服务中。如果命令有效负载与其他域相关,如何进行验证?
例如,这是订单服务(命令端)中的addOrderItemCommand有效负载。
{
"customerId": "CUST111",
"productId": "SKU222",
"orderId":"SO333"
}
如何验证上面的命令?如何知道客户真的存在于数据库(查询端客户服务)中并仍然活跃?如何知道产品是否存在于数据库中并且产品的状态是否已发布?如何知道客户是否有资格从相关产品中获得促销价格?
是否可以直接调用API(如点对点/ ajax /请求承诺)来在命令端服务中验证此有效负载?但我认为,如果API直接调用验证,性能会变差。因为,我们在命令服务之外开发了一个事件处理器,用于监听事件并将事件应用于资料化视图。
谢谢。
答案 0 :(得分:2)
一如既往,一切都取决于域的具体细节,但作为一般原则,跨域验证应通过读取模型完成。
在这种情况下,我会在每个微服务中维护一个读模型以用于验证。当然,这带来了最终一致性的问题。 你如何处理这应该来自你对域的理解。应考虑诸如最终一致性的长度与更新频率之间的因素。与开发成本相比,将业务弄错的成本最小化问题。在许多情况下,只记录存在问题的事实对于企业来说已经足够了。
我有一篇专门用于验证的博客文章,您可以在此处找到:How To Validate Commands in a CQRS Application
答案 1 :(得分:1)
由于需要查询多个有界上下文以便验证通过,因此需要考虑最终的一致性。话虽如此,整个过程总有可能在“小”的时间内处于无效状态。例如,在接受命令之后和订单发货之前,可以停用用户。在线商店是一个复杂的系统,例外可能出现在任何子系统中。但是,作为事件驱动系统实施有助于;每次订购过程进入无效状态,您都可以采取补偿措施/命令。例如,如果用户在此期间停用,您可以取消所有常规订单,发布预订产品,宣布愿望清单中没有这些产品的潜在客户等等。
DDD中有多种验证方式,但我遵循一般规则,即验证应该尽早完成,但不会影响数据的一致性。因此,为了尽早,您可以查询readmodel以拒绝无法生效的命令,并且为了使系统保持一致,您需要在订单发货之前进行另一次检查。
现在让我们谈谈您的具体问题:
如何知道客户确实存在于数据库(查询端客户服务)中并且仍处于活动状态?
您可以查询readmodel以验证用户是否存在且仍处于活动状态。您应该这样做,因为来自无效用户的命令是某种攻击的强烈指示,并且您不希望这些命令通过您的系统。但是,即使命令通过了此检查,也不一定意味着订单将被发送,因为其他异常可以在两者之间产生。
如何知道产品存在于数据库中并且产品的状态已发布?
同样,您可以查询readmodel,以便通知用户此时产品不可用。或者,根据您的业务,如果您知道这些产品将在不到24小时内根据以前的某些统计信息(例如您知道电视机每天都会在您的库存中到达),则可以允许该命令通过。或者您可以让客户选择是否等待。在这种情况下,如果产品在订购的最后阶段(发货)没有库存,则通知客户产品不再有库存。
如何知道客户是否有资格获得相关产品的促销价格?
您可能需要查询另一个有界的上下文,例如Promotions BC
来检查这一点。这取决于如何验证/使用促销。
是否可以直接调用API(如点对点/ ajax /请求承诺)来在命令端服务中验证此有效负载?但我认为,如果API直接调用验证,性能会变差。
这取决于您希望系统恢复的弹性以及拒绝无效命令的速度。
同步调用更容易实现,但它们会导致系统性能降低(您应该注意级联故障并使用circuit breaker之类的技术来阻止它们)。
异步(即使用事件)调用更难实现,但会使系统更具弹性。为了进行异步调用,ordering
系统可以为其他系统订阅事件,并维护一个私有状态,可以在命令到达时查询以进行验证。通过这种方式,即使inventory
或customer management
系统的链接已关闭,订购系统仍可继续运行。
无论如何,这实际上取决于您的业务,我们都不能告诉您exaclty该做什么。