当前位置: 首页 > 知识库问答 >
问题:

DDD、CQRS/ES

汪晟睿
2023-03-14

所以我将通过使用一个例子来解释这个问题,因为它使一切更加具体,希望能减少歧义。

架构非常简单

1微服务

每个微服务都将使用CQRS/ES设计模式,这意味着

  • 每个微服务都有自己的Aggregate映射现实世界问题的域
  • 聚合的状态将从事件存储重建
  • 每个事件都将表示聚合中的状态更改,并将通过消息代理传输到对更改感兴趣的任何服务
  • 每个微服务都将在其自己的域内进行事务处理
  • 每个微服务最终都会与其他域保持一致
  • 每个微服务都将根据其他微服务发出的事件构建自己的视图模型

这个例子假设我们有一个银行系统

  • 往来账户微服务负责映射客户往来账户。。。取款、存款
  • 奖励微服务将负责对银行提供的任何奖励进行清点和盘点
  • 航空里程微服务将负责监控来自经常账户的所有交易,并通过我们的奖励微服务向客户提供奖励

因此,问题是,航空里程微服务是否应该根据其自己的视图模型做出决策,该视图模型根据来自经常账户的事件进行更新,同样,在选择应该向客户提供哪些奖励时?

在局部视图模型上进行决策的缺点;

  • 复制关于如何维护这些视图的域逻辑
  • 视图中的bug可能会传播错误的奖励
  • 损坏的视图模型上的状态更改(即发出的事件)可能会在其他服务中产生后果,这些服务会自行决定这些事件

对局部视图模型进行决策的优势;

>

系统应该更快,资源消耗更少

或者它应该使用来自服务的事件来触发对拥有该域的聚合的查询,在这样做时,我们接受这样一个事实,即视图模型可能会损坏,但最终决策应该始终与拥有该域的聚合协商?

请注意,上述问题并不是我对架构的理解,本文的目的是就如何在微服务环境中有效地使用该架构来保持每个服务的解耦,同时避免级联损坏场景,而不会在服务之间产生太多喋喋不休。

共有2个答案

宋臻
2023-03-14

因此,问题是,航空里程微服务是否应该根据自己的视图模型做出决策,该模型根据来自经常账户的事件进行更新,同样,选择应该向客户提供的奖励?

每个使用者使用生产者计算的表示的本地副本。

因此,如果航空里程需要来自经常账户的信息,它应该查看由经常账户服务计算的视图的本地副本。

关键思想是:微服务应该彼此隔离;您应该能够在不影响其他服务的情况下重新设计和部署一个。

所以试试这个思维实验——假设我们有这三个微服务,但都保存了当前状态的快照,而不是事件。一切正常,然后想象一下,往来账户维护人员发现,事件源实现可以更好地服务于业务。

对经常账户的更改是否需要对航空里程进行相应的更改?如果是这样,我们真的可以声称这些服务是相互隔离的吗?

对局部视图模型进行决策的优势

我并不特别喜欢这些“优势”;首先,它们受性能轴的支配(请记住,性能优化的第二条规则是“还没有”)。其次,他们假设服务边界是正确绘制的;也许性能问题是责任分离需要审查的证据。

江鸿羲
2023-03-14

所以问题是,航空里程微服务是否应该根据自己的视图模型做出决定,该模型正在根据来自当前账户的事件进行更新,同样,选择应该给予客户的奖励?

对事实上,你应该修改你的架构,甚至创建更多的微服务。我的意思是,作为一个事件驱动架构(也是一个事件源架构),您的微服务有两个职责:它们需要保留两个不同的模型:写模型和读模型。

因此,每个聚合都应该是一个微服务,它只保留写模型,也就是说,它只处理命令,而不构建读模型。

然后,对于每个读取/查询用例,您应该有一个构建完美读取模型的微服务。如果您需要保持聚合微服务的干净(正如您应该做的那样),这是必需的,因为一般来说,读取模型需要来自多个聚合类型/有界上下文的数据。读取模型可能会跨越有界的上下文边界,聚合可能不会。所以你看,如果你需要完全尊重DDD,你真的没有选择。

有人说域事件应该隐藏,只在拥有的微服务的本地。我不同意。在事件驱动架构中,域事件是一级公民,允许它们访问其他微服务。这使其他微服务有机会构建自己对系统状态的解释。否则,发射微服务将承担不可能的额外责任/任务,即建立一个必须满足所有微服务可能需要的所有可能需要的状态(!);i、 e.可能微服务想要查找已删除远程实体的标题,如果发出的微服务只保留未删除实体的列表,它怎么能这样做?你可能会说:但是它会保留所有的实体,不管是否删除。但可能有人需要删除实体的日期;你可以说:但是我还保留了deletedDate。你看到你在做什么了吗?你打破了开放/封闭原则。每次创建微服务时,都需要修改发出微服务。

还有微服务的弹性。在可扩展性艺术中,作者谈到了泳道。它们是一种将系统组件分成故障泳道的策略。泳道中的故障不会传播到其他泳道。我们的微服务是泳道。泳道中的组件不允许访问其他泳道中的任何组件。一个关闭的微服务不应该导致其他组件关闭。这不是速度/优化的问题,而是弹性的问题。域事件是保持两个远程系统同步的完美方式。它们还强调数据最终是一致的;事件以有限的速度传播(从纳秒到偶数天)。当系统设计时考虑到这一点,那么没有其他微服务可以将其关闭。

是的,会有一些代码重复。是的,虽然我说你没有选择,但你有。为了以较低的弹性为代价减少代码重复,可以使用一些规范的读取模型来构建正常的平面状态,而其他微服务可以对此进行查询。这在大多数情况下是危险的,因为它打破了泳道的概念。如果规范的微服务失效,那么所有依赖的微服务失效。规范微服务最适用于类CRUD的有界上下文。

然而,在一些有效的情况下,您可能有一些不想公开的内部事件。换句话说,您不需要发布所有域事件。

 类似资料:
  • 我对DDD和CQRS的概念是新的,无法找到一个最终的解决方案如何以一种干净的方式上传图像或文件。 我有三个想法来解决这个问题,但我对它们不是很满意。 方式1: 1。在单个请求中发布所有数据,包括图像(多部分) 2。创建,它返回. 3。之后,创建并将与构造函数中的根数据一起传递。就CQRS而言,一个用户交互应该只有一个命令。 方法2: 1。将图像发送到分离endpoint,创建临时文件并返回id或文

  • 在CQRS ES和DDD中,聚合中的小读模型从其他聚合或有界上下文中获取数据是件好事吗? 例如,在订单验证(订单聚合)中,有一个业务规则,该规则仅在未标记客户时验证订单。标志信息通过同步域事件放入读取模型(特定于聚合)。 你怎么看?

  • null 我们尝试了几件事: > 发出命令: 激发命令 同步处理此命令,如果命令无效或引发事件,则返回错误。 null 缺点:-据我所知,应该用佐贺来编排流程。这里我们介绍“验证”的概念。我不确定这是不是推荐的方法。 验证是一个非常常见的概念。在分布式完全异步系统中如何处理它?

  • CQRS.NET https://www.nuget.org/packages/Cqrs Symbols now available on nuget.org. See https://devblogs.microsoft.com/nuget/improved-package-debugging-experience-with-the-nuget-org-symbol-server/ for de

  • GNU DDD (Data Display Debuger) 是一个图形化的调试工具,通过对调试命令例如 GDB、DBX、WDB、JDB、XDB 等等命令行工具的封装,提供一个非常友好的调试界面。

  • All Things CQRS A bunch of ways of doing CQRS with various Spring tools. Getting Started These instructions will get you and overview of how to synchronize two different datasources. We will do so by