CQRS/ES架构中有界上下文之间的通信是通过事件实现的;上下文A生成事件作为对命令的响应,然后这些事件通过事件总线(消息队列)转发到上下文B。
或者……您可以将事件存储在eventstore(属于上下文A)中。或者……两者兼而有之(存储和转发)。
我的问题是:从上下文B中,我应该从上下文存储中提取事件吗?还是简单地消费通过事件总线推送的事件?
我倾向于拉动方法。因为这样我们就可以在上下文B中迎头赶上。相比之下,在推送方法中,上下文B可能不知道在B遇到停机时传递的事件。
那么…这是否意味着…当我们有eventstore时,我们可以简单地忘记消息队列(似乎是多余的)?
还是我错过了什么?
您将需要查看没有Pub/Sub的消费事件流
在DDD欧洲会议上,我意识到与我交谈的演讲者尽可能避开酒吧/潜艇。
下面的讨论可能有价值。太长别读:那里没有多少酒吧/潜艇的粉丝。
Konrad Garus on Push or Pull?,描述了Pull设计:
在后一种(也是更简单的)设计中,它们只传播一个新事件已经保存的信息,以及它的顺序ID(这样所有的预测都可以估计它们落后了多少)。当被唤醒时,执行器可以继续沿着它的正常路径,从查询事件存储开始。
为什么?因为处理来自单个源的事件更容易,但更重要的是,DB支持的事件存储可以轻松保证顺序,并且不会出现丢失或重复消息的问题。查询数据库非常快,因为我们按主键顺序读取单个表,而且大多数时候数据都在RAM缓存中。瓶颈在于投影线程更新其读取模型数据库。
总的来说,这可以归结为:当人们考虑事件来源时,他们实际上是在考虑历史,而不是孤立的事件。如果你真正想要的是一个没有间隙的有序事件序列,那么查询该序列的权威比试图从一堆不相交的事件消息中重建if要好得多。
但是——一旦你决定这样做,那么历史记录和其中出现的所有事件突然成为上下文A的api的一部分。当团队A决定不同的事件存储实现更合适时会发生什么?他们可以推出自己服务的新版本,还是我们需要大中断,因为每个消费者也必须更新?
同样,如果我们决定将上下文A重构为上下文C和上下文D会发生什么?同样,我们是否必须在上下文B中胡思乱想才能获得我们需要的数据?
也许真正的问题是上下文B与上下文A中的历史相耦合,而这些历史应该是私有的?上下文B应该访问上下文A的数据,还是应该将这项工作委托给上下文A的能力?
乌迪·达汉关于SOA的文章可能会让你朝着这个方向思考。