Architecture DDD/CQRS聚合根是否可以是微服务?

Architecture DDD/CQRS聚合根是否可以是微服务?,architecture,domain-driven-design,cqrs,event-sourcing,Architecture,Domain Driven Design,Cqrs,Event Sourcing,我目前正在设计新的系统,我坚信CQRS+ES将是一个完美的选择。我想验证我的“大规模”设计假设是否听起来不错,并且我没有朝着错误的方向前进 在我看来,由于一致性和网络边界的重合,让每个聚合根(写模型)生活在自己的微服务中似乎是个好主意 我认为,由于一致性保证,可以安全地假设每个聚合实例可能只有自己的事件流。在实践中,为每个聚合实例结束一个事件存储是一种过分的做法,但在相同聚合类型的微服务之间共享一个或几个复制的事件存储对我来说似乎是明智的选择。如果不需要复制,我们甚至可以基于聚合ID切分事件存储

我目前正在设计新的系统,我坚信CQRS+ES将是一个完美的选择。我想验证我的“大规模”设计假设是否听起来不错,并且我没有朝着错误的方向前进

在我看来,由于一致性和网络边界的重合,让每个聚合根(写模型)生活在自己的微服务中似乎是个好主意

我认为,由于一致性保证,可以安全地假设每个聚合实例可能只有自己的事件流。在实践中,为每个聚合实例结束一个事件存储是一种过分的做法,但在相同聚合类型的微服务之间共享一个或几个复制的事件存储对我来说似乎是明智的选择。如果不需要复制,我们甚至可以基于聚合ID切分事件存储

这样,由于聚合类型作用域的微服务很少,每个微服务都处理大量聚合实例的命令,因此扩展应该对系统的其余部分足够透明

然后,让投影仪(阅读模型)也生活在自己的微服务中是有意义的,每个微服务都有自己的数据库,应该在相同类型的投影仪之间共享

因为投影仪的查询界面对外部世界不是很友好(我假设投影仪提供类似于存储库的界面来发布查询,在我看来,这与访问控制、速率限制等不太好),所以每个投影仪都应该提供一些统一的网络接口,供BFF(前端后端)使用,实际上服务于一些API端点,确保安全性,提供版本控制等等

tl;dr:我提供了上面(包围的)的图形表示,以用我糟糕的绘图来弥补我糟糕的措辞。PS:Replay服务是一种服务,它在新事件附加到事件存储中时监视新事件,并将其广播给感兴趣的订阅投影仪或流程管理器(未绘制),或者为具有陈旧或空DBs的投影仪重播整个事件序列

这个CQRS+微服务的改编听起来好吗,还是我从根本上误解了什么,整个设计都是垃圾

UPD1:

为什么在事件源和投影仪之间有负载均衡器?如何平衡负载? 如果我正在生成多个相同类型的投影仪实例来处理繁重查询带来的额外负载,那么它们将如何侦听事件?对我来说,只分配一个实例来完成所有事件处理工作、更新数据库等等似乎很奇怪,因为随着负载的增加,它很可能会过载。所以,分发事件处理也是有意义的,对吗

另外,在我写这篇文章的过程中,我一直在思考将投影仪进一步分为“投影仪编写器”(在DB中侦听事件并更新共享状态的编写器)和“投影仪阅读器”(侦听查询并返回状态的编写器)是否是一个好主意,DB将充当真相和整合点的来源。这样,我们可以更好地扩展不对称负载(小事件、大量查询),而无需任何成本

其中一个必要条件是防止不同的projector writer实例同时处理来自同一聚合的事件,因为使用无序事件更新表示将导致内部一致性的丢失和即时灾难

至于“如何”,我想不出几个解决办法:

  • 为所有传入事件保留单个RabbitMQ队列,并使所有投影仪使用带有确认的队列中的事件。更新数据库后,projector writer向RabbitMQ发出ack,事件将从队列中丢弃。否则,如果放映机写入程序因某种原因死亡,事件将再次被重新调用到队列中的下一台放映机

    对于每个聚合,我们应该保留高度/修订号,并且仅当下一个修订号(包含在事件中)正好比当前修订号多一个时,才允许更新成功。如果此条件不成立,则重新查询此事件,希望届时不一致性将得到解决,并获取下一个事件

    最终,它将完成,并且考虑到足够多的聚合,它将永远不需要重新计算

  • 设置某种调度程序服务来侦听事件,每种投影仪类型一个调度程序。此调度程序应基于聚合ID的哈希将事件分发给投影仪写入程序,因此,同一聚合始终由索引为
    哈希(event.aggregateId)%numberOfProjectorWriters
    的同一聚合处理

    这将永远不会重新请求,但会提前终止MQ,引入单点故障,并且如果由于某些节点死亡、动态缩放或

  • >P.不知何故,使用头交换机实现了1和2的组合,使消费者更喜欢同一组的集合ID,但如果中间的消费者数量变化,则不需要拧紧。
    我相信,虽然技术上聚合的根可以存在于它们自己的微服务中,但这种粒度级别可能会带来不需要的复杂性。通常他们说,从结构良好的整体开始

    通常,如果一个BC中有几个聚合,它们可能共享一些服务、存储库,因此这些聚合在一起可以显著降低复杂性,并形成一个内聚组件。但这可能取决于扩展需求

    顺便问一下,什么是事件存储一致性保证?事件流最有用的保证是事件的顺序。这在分布式环境中很难实现。如果您为候选活动商店提供此类保证的链接,那就太好了

    我同意你的看法,read模型可能位于不同的微服务中,并且需要进行单独的缩放。实际上,read模型管理器可以是单独的r