Domain driven design 事件寻源时,事件是否包含对当前对象及其依赖关系的更新?

Domain driven design 事件寻源时,事件是否包含对当前对象及其依赖关系的更新?,domain-driven-design,cqrs,event-sourcing,Domain Driven Design,Cqrs,Event Sourcing,在事件源中,写入系统的事件是否描述了对多个对象的更改?我试图理解如何记录N个项目的更改,而不会因为其他编写器而导致并发问题 背景 我的系统中有两种设置:全局设置和本地设置 有一个且正好有一个全局设置对象。这是由系统中的一小部分管理员控制的。有N个“本地”设置对象。这些都从全局设置继承其可用选项。版主使用这些控件来控制页面上的选项 为了进一步说明,全局设置对象指定了本地设置的内部内容。e、 g +------------------------+ +---------------------

在事件源中,写入系统的事件是否描述了对多个对象的更改?我试图理解如何记录N个项目的更改,而不会因为其他编写器而导致并发问题

背景

我的系统中有两种设置:全局设置和本地设置

有一个且正好有一个全局设置对象。这是由系统中的一小部分管理员控制的。有N个“本地”设置对象。这些都从全局设置继承其可用选项。版主使用这些控件来控制页面上的选项

为了进一步说明,全局设置对象指定了本地设置的内部内容。e、 g

+------------------------+    +----------------------------+
| GLOBAL                 |    |  LOCALL                    |
|                        |    |                            |
| Avalable Attributes:   |    |                            |
| custom_css: show       |    |  custom_css: enabled       |
| something_else: show   |    |  something_else: disabled  |
|                        |    |                            |
+------------------------+    +----------------------------+
因此,全局设置中声明的属性本质上类似于本地设置可以配置的功能切换。因此,这是我问题的核心,全局设置的更改必须级联到N个本地设置。由于审计需求(谁更改了内容/时间),级联更改是一项硬需求。i、 e.版主必须能够了解为什么他们以前的选项没有被删除

在传统的设置中,这将在事务中完成。但是,在事件源中,如果不能够以原子方式记录不同项目的事件,您将如何做到这一点?如果没有事务,我们将遇到本地设置(可能)被版主修改的问题,而从管理员到全局配置的写入仍在写入日志

因此,日志将显示一个本地设置,启用一个他们不应该拥有的选项,因为该选项已被删除

+----------------------------+------------------------+------------+
|  User: Admin               |  User: bob(moderator)  |   INVALID  |
|  RemovedOption: custom_css |  custom_css: True      |   STATE!!  |
+----------------------------+------------------------+------------+
我能想到的唯一解决办法是:

A.编写一个描述“事务”的复合事件

+----------------------------------------+
| SettingsChangedCompositeEvent          |
| -----------------------------          |
| events:[                               |
| {Global: {RemovedOption: custom_css}}, |
| {Local1: {RemovedOption: custom_css}}, |
| {Local2: {RemovedOption: custom_css}}, |
| ...                                    |
| {LocalN: {RemovedOption: custom_css}}  |
|
| ]                                      |
|                                        |
+----------------------------------------+
这似乎是完全错误的,因为它需要一种完全不同的处理方式(至少根据我目前对事情通常如何进行的看法)。当对本地设置进行水合时,我必须有特殊的逻辑,可以从事件的更改列表中提取相关的更改(如果存在),并有条件地应用它

B.根本不尝试级联写入,而是在读取时合并它们

+----------------------------------------+
| SettingsChangedCompositeEvent          |
| -----------------------------          |
| events:[                               |
| {Global: {RemovedOption: custom_css}}, |
| {Local1: {RemovedOption: custom_css}}, |
| {Local2: {RemovedOption: custom_css}}, |
| ...                                    |
| {LocalN: {RemovedOption: custom_css}}  |
|
| ]                                      |
|                                        |
+----------------------------------------+
停止尝试使N个本地设置对象与一个全局设置对象保持同步,而是始终加载全局设置的状态以及本地设置的状态,以进行验证(我尝试在本地切换的对象是否仍存在于全局中?)和演示(以便最终聚合对象是全局对象和局部对象的组合)

我认为,这种设置遇到的并发问题与试图在同一日志中保持所有内容同步的问题相同。即,用户可以在管理员用户删除事件所依据的选项的同时触发事件以更新其本地设置

C.还有别的吗?

+----------------------------------------+
| SettingsChangedCompositeEvent          |
| -----------------------------          |
| events:[                               |
| {Global: {RemovedOption: custom_css}}, |
| {Local1: {RemovedOption: custom_css}}, |
| {Local2: {RemovedOption: custom_css}}, |
| ...                                    |
| {LocalN: {RemovedOption: custom_css}}  |
|
| ]                                      |
|                                        |
+----------------------------------------+
我真的不确定如何处理这样紧密结合在一起的数据。处理基本上基于继承的数据模型的正确方法是什么

在事件源中,写入系统的事件是否描述了对多个对象的更改

是的,这可能取决于您如何在一致性边界内建模

在域驱动设计中,Eric Evans谈到了“聚合”;关于聚合是什么有很多不同且令人困惑的解释,但核心事实之一是:聚合中的所有元素都存储在一起

因此,全局设置中声明的属性本质上类似于本地设置可以配置的功能切换。因此,这是我的问题的核心,全局设置中的更改必须级联到N个本地设置

因此,为了实现在一个“事务”中更改所有这些设置的模拟,您需要设计您的模型,以便所有这些都是同一个“聚合”的一部分,并将所有事件存储在同一个事件流中

如果并发编辑很少,那就没问题了。当并发编辑发生时,通常的答案是让比赛中的失败者(自动)重试,如果这导致冲突,那么就让命令失败,让客户端重新加载状态,并决定怎么做

在事件流中,您可以拥有描述系统单个元素更改的事件、应用于所有元素的更改、应用于元素子集的更改,这一切都很好。请记住,事件只是描述状态更改的消息——您可以根据您的域的意义将其设置为精细或粗糙

当单个事务表示为多个事件时,重要的是要写入所有或不写入任何事件

List<Event> oldHistory = eventStore.get(key)
List<Event> newHistory = doSomethingInterestingWith(oldHistory)
eventStore.compareAndSwap(key, oldHistory, newHistory)
List oldHistory=eventStore.get(键)
List newHistory=doSomethingInterestingWith(旧历史)
eventStore.compareAndSwap(键、旧历史记录、新历史记录)

首先要挑战您最初的假设,即使用最终一致性是不够的

您可以创建一个事件处理程序来侦听来自GlobalSettings的事件,然后向每个受影响的LocalSettings发送一个命令更改将传播到所有LocalSettings。如果您在原始全局更改与正在传播的更改之间的短窗口中进行本地设置更改,LocalSettings的审核跟踪/事件流将显示该更改,然后是之后的GlobalSettings相关事件。即使原始GlobalSettings事件首先发生在在现实世界中,您不需要显示这一点,因为对于这个特定的聚合,情况并非如此。此外,保存GlobalSettings更改的UI可以在某种程度上指示更改已完全传播的时间,如果您希望它看起来是原子的