Ben*_*n M 1 validation one-to-many relationship cqrs microservices
场景:
发票的收件人必须是有效的联系人.
CreateInvoiceCommand:
{
"content": "my invoice content",
"recipient": "42"
}
Run Code Online (Sandbox Code Playgroud)
我现在读了很多次,写边(=命令处理程序)不应该调用读取端.
考虑到这一点,Invoices微服务必须监听所有事件ContactCreated和ContactDeleted事件,以便知道给定的收件人ID是否有效.
然后我会在Invoices微服务中拥有数千个联系人,即使我知道其中只有少数会收到发票.
有没有最佳实践来处理这些情况?
发票的收件人必须是有效的联系人.
所以你需要注意的第一件事 - 如果两个实体是不同聚合的一部分,你就不能真正实现"仅当该实体满足规范时才对这个实体应用更改",因为该实体可能会在当下发生变化您评估规范和执行写入的那一刻.
换句话说 - 您只能在聚合边界上获得最终的一致性.
聚合是其自身状态的权限,但是其他所有内容(例如,命令消息的内容),它几乎必须接受某些外部权限已经检查了数据.
你可以在这里采取几种方法
1)您可以盲目地接受命令中指定的收件人是有效的.
2)您可以尝试从一些外部机构(也就是:某些其他聚合的读取模型)验证收件人的有效性,从不受信任的来源接收它并将其提交给域模型.
3)您可以盲目地接受所述的命令,但将发票视为临时发票,直到确认收件人的有效期为止.这意味着在发票上运行第二个命令以验证收件人.
注意 - 从模型的角度来看,这些不同的命令是等价的,但在应用程序层它们不需要 - 您可以将命令的访问权限限制为可靠的源(不要使它成为public api,需要只有可靠来源的授权等).
方法#3是最微观的,因为这两个命令可以及时分开 - 您可以在它到达时立即接受CreateInvoice命令,并异步验证收件人.
您会在哪里找到方法4),其中Invoices Microservice有自己的联系人存储,只要有ContactCreated或ContactDeleted事件,它就会更新?然后,两个实体都是同一服务和边界的一部分.现在应该可以使事情保持一致,对吗?
不,你已经让这两个实体成为同一服务的一部分,但问题从来都不是它们在不同的服务中,而是它们在不同的聚合中 - 这意味着我们可以同时改变实体状态,这意味着我们无法确保它们立即同步.
如果您希望立即保持一致性,则需要一个以不同方式划分界限的模型.
例如,如果发票实体被建模为Contacts聚合的一部分,则聚合可以确保新发票需要有效收件人的不变量 - 域模型使用内存中的状态副本来确认收件人是否有效当我们加载时,写入记录簿会验证记录簿自加载发生以来没有改变.
聚合状态的写入是记录簿中的比较和交换; 如果某个并发进程使收件人失效,则CAS操作将失败.
当然,权衡是联系人聚合的任何变化也会导致发票失败; 与同一收件人同时编辑不同的发票会从窗口中删除.
聚合是全有或全无的; 它们是不可分离的.
现在,一个可能是你的发票聚合有一个必须立即与收件人一致的部分,另一部分最终是一致的,甚至是不一致的,是可以接受的.在这种情况下,您的目标是重构模型.
| 归档时间: |
|
| 查看次数: |
862 次 |
| 最近记录: |