我们正在考虑在工作中实施CQRS模式,并且有一些关于验证的问题。
我们说我们有3个聚合根:
User
Business
UserToBusinessRelationship
当用户注册时,发送的事件将是:
UserCreated
BusinessCreated
UserAddedToBusiness
需要验证事件,例如,要创建用户和业务之间的链接,应创建用户和业务。
我看到两种方法。
预先验证:使用上次处理的快照和未处理的事件动态构建读取模型,并将其用于验证。
处理时验证:按原样接受事件/命令,并在处理事件时进行验证。
第一种方法有即时反馈,但它需要创建一个最终的读取模型,仅用于验证。第二个更简单,但没有向消费者反馈出现问题。
我在考虑这样的事情:当你发出一个事件时,你会得到一个你可以在以后用来查询事件状态的id。如果事件处理成功,您将获得“确定”,否则您会收到错误消息,告知错误。
这是一种有效的方法还是一种过度杀伤力?消费者如何知道事件已被处理并且数据已准备好使用?
答案 0 :(得分:4)
很棒的问题Leonti - 您可以在CQRS系统中找到有关验证的博客文章。你可以在这里找到:How To Validate Commands in a CQRS Application。
从标题中可以看出,CQRS的重点是验证命令而不是事件。
为什么?
因为命令可以来自用户输入,因此不应该受信任。 另一方面,事件从域内发出并且可以被信任。可能存在CQRS的实现,其涉及离开系统边界的事件或接收不是源自系统内的事件。在这些边缘情况下,应该小心谨慎。
另一个潜在的红旗是您描述聚合的方式。在我看来(我不知道你的域,所以不能100%肯定)它们更类似于数据库中的表而不是聚合。重要的是不要让你的持久性机制决定你的模型。
更具体地回到你的问题。
从广义上讲,有两种类型的验证。表面和深刻。肤浅的东西就像缺少字段,有效的电子邮件地址等。深度是一个域概念在起作用。例如需要货物的重量并进行表面验证,但货物是否适合承运人可能是一个领域概念。
您可能需要验证事物的存在。是否存在业务或用户。虽然这种验证很可能需要您访问数据库,但它仍然是肤浅的,因为它不太可能是一个明确的域概念。
无论如何,我希望有所帮助。
答案 1 :(得分:2)
这里有两个主要方面:
在CQRS中,您有命令和查询。事件来自哪里?假设您还意味着事件源,当使用命令时,您在客户端上进行一些验证,在命令处理程序中进行一些验证,在域模型中进行一些验证(聚合不变量和约束)。此特定示例看起来像在客户端和命令处理程序内验证的内容。
请记住,由于您在三个不同的聚合上有三个不同的操作,因此您需要有三个事务。这意味着,如果其中一个不能完成 - 您的系统将进入无效状态。您需要重新考虑聚合边界以拥有一个聚合根,或者考虑使用其他技术,如事件驱动策略,传真(流程管理器)或路由滑动(sagas)。
当然,UserToBusinessRelationship
听起来更像是一个RDBMS多对多链接表而不是聚合,但我对你的域名一无所知。