CQRS +微服务:如何处理关系/验证?

Ben*_*n M 1 validation one-to-many relationship cqrs microservices

场景:

  • 我有2个微服务(内部都使用CQRS +事件采购)
  • 微服务1管理联系人(=聚合根)
  • 微服务2管理发票(=聚合根)

发票的收件人必须是有效的联系人.

CreateInvoiceCommand:

{
  "content": "my invoice content",
  "recipient": "42"
}
Run Code Online (Sandbox Code Playgroud)

我现在读了很多次,写边(=命令处理程序)不应该调用读取端.


考虑到这一点,Invoices微服务必须监听所有事件ContactCreatedContactDeleted事件,以便知道给定的收件人ID是否有效.

然后我会在Invoices微服务中拥有数千个联系人,即使我知道其中只有少数会收到发票.


有没有最佳实践来处理这些情况?

Voi*_*son 5

发票的收件人必须是有效的联系人.

所以你需要注意的第一件事 - 如果两个实体是不同聚合的一部分,你就不能真正实现"仅当实体满足规范时才对这个实体应用更改",因为实体可能会在当下发生变化您评估规范和执行写入的那一刻.

换句话说 - 您只能在聚合边界上获得最终的一致性.

聚合是其自身状态的权限,但是其他所有内容(例如,命令消息的内容),它几乎必须接受某些外部权限已经检查了数据.

你可以在这里采取几种方法

1)您可以盲目地接受命令中指定的收件人是有效的.

2)您可以尝试从一些外部机构(也就是:某些其他聚合的读取模型)验证收件人的有效性,从不受信任的来源接收它并将其提交给域模型.

3)您可以盲目地接受所述的命令,但将发票视为临时发票,直到确认收件人的有效期为止.这意味着在发票上运行第二个命令以验证收件人.

注意 - 从模型的角度来看,这些不同的命令是等价的,但在应用程序层它们不需要 - 您可以将命令的访问权限限制为可靠的源(不要使它成为public api,需要只有可靠来源的授权等).

方法#3是最微观的,因为这两个命令可以及时分开 - 您可以在它到达时立即接受CreateInvoice命令,并异步验证收件人.

您会在哪里找到方法4),其中Invoices Microservice有自己的联系人存储,只要有ContactCreated或ContactDeleted事件,它就会更新?然后,两个实体都是同一服务和边界的一部分.现在应该可以使事情保持一致,对吗?

不,你已经让这两个实体成为同一服务的一部分,但问题从来都不是它们在不同的服务中,而是它们在不同的聚合中 - 这意味着我们可以同时改变实体状态,这意味着我们无法确保它们立即同步.

如果您希望立即保持一致性,则需要一个以不同方式划分界限的模型.

例如,如果发票实体被建模为Contacts聚合的一部分,则聚合可以确保新发票需要有效收件人的不变量 - 域模型使用内存中的状态副本来确认收件人是否有效当我们加载时,写入记录簿会验证记录簿自加载发生以来没有改变.

聚合状态的写入是记录簿中的比较和交换; 如果某个并发进程使收件人失效,则CAS操作将失败.

当然,权衡是联系人聚合的任何变化也会导致发票失败; 与同一收件人同时编辑不同的发票会从窗口中删除.

聚合是全有或全无的; 它们是不可分离的.

现在,一个可能是你的发票聚合有一个必须立即与收件人一致的部分,另一部分最终是一致的,甚至是不一致的,是可以接受的.在这种情况下,您的目标是重构模型.