偶尔连接CQRS系统

Aar*_*ron 13 domain-driven-design occasionallyconnected cqrs event-sourcing get-event-store

问题:

两名员工(A&B)同时离线,同时编辑客户#123,比如版本#20,当离线继续进行更改时......

场景:

1 - 两名员工编辑客户#123并对一个或多个相同的属性进行更改.

2 - 两名员工编辑客户#123但不要做出相同的更改(他们互相交叉而不接触).

......然后他们都回到网上,第一名员工A追加,从而将客户更改为版本#21,然后将员工B更改为版本#20

问题:

我们在方案1中保留了哪些变化?

我们可以在方案2中进行合并吗?

语境:

1 - CQRS +事件采购风格系统

2 - 使用事件源Db作为队列

3 - 读模型的最终一致性

4 - RESTful API

视觉倾斜图;  它是MS图的混搭和一些改变的东西

编辑-1:到目前为止基于答案的澄清:

为了执行精细的粒度合并,我需要为一个表单中的每个字段设置一个命令?

在此输入图像描述

在上面,ChangeName,ChangeSupplier,ChangeDescription等的细粒度命令,每个都有自己的时间戳,允许自动合并事件A和B都更新ChangedName?

编辑-2:根据特定事件存储的使用进行跟进:

好像我会利用@GetEventStore来保持我的事件流的持久性.

他们使用乐观并发如下:

  • 流中的每个事件都将流版本增加1

  • 写入可以指定预期版本,在编写器上使用ES-ExpectedVersion标头

    • -1指定流不应该已存在

    • 0及以上指定流版本

    • 如果流不在版本中,写入将失败,您要么使用新的预期版本号重试,要么重新处理该行为并确定如果您选择它就行.

  • 如果未指定ES预期版本,则禁用乐观并发控制

  • 在这种情况下,Optimistic Concurrency不仅基于消息ID,还基于事件#

Ale*_*ger 5

如果我正确理解您的设计图片,那么偶尔连接的用户会将命令排队,即更改请求,以及当用户重新连接排队的命令时,它们会一起发送; 只有一个数据库权限(命令处理程序查询加载其最新版本的aggretates); 只有视图模型才会同步到客户端.

在此设置中,方案2通过您的设计轻松自动合并,如果您明智地选择命令,请阅读:使它们细化:对于每个可能的更改,请选择一个命令.然后,在重新连接客户端时,命令以任何顺序处理,但由于它们只影响析取字段,所以没有问题:

  1. 客户是在20号.
  2. A处于脱机状态,根据v20的陈旧模型编辑更改.
  3. B处于离线状态,根据v20的陈旧模型编辑更改.
  4. A联机,批量发送排队ChangeName命令,v20的客户加载并保存为v21.
  5. B联机,批量发送排队ChangeAddress命令,v21的客户加载并保持为v22.
  6. 正如预期的那样,数据库包含具有正确名称和地址的用户.

场景1中,通过此设置,两个员工都将覆盖其他员工的更改:

  1. 客户是在20号.
  2. A处于脱机状态,根据v20的陈旧模型编辑更改.
  3. B处于离线状态,根据v20的陈旧模型编辑更改.
  4. A联机,批量发送排队ChangeName命令到"John Doe",v20的客户加载并保存为v21,名称为"John Doe"
  5. B联机,批量发送排队ChangeName命令到"Joan d'Arc",v21的客户(名为"John Doe")被加载并保存为v22(名称为"Joan d'Arc").
  6. 数据库包含名为"Joan d'Arc"的用户.

如果B在A之前上线,则反之亦然:

  1. 客户是在20号.
  2. A处于脱机状态,根据v20的陈旧模型编辑更改.
  3. B处于离线状态,根据v20的陈旧模型编辑更改.
  4. B联机,批量发送排队ChangeName命令到"Joan d'Arc",v20的客户加载并保存为v21(名称为"Joan d'Arc").
  5. A联机,批量发送排队ChangeName命令到"John Doe",v21的客户被加载并保持为v22,名称为"John Doe".
  6. 数据库包含名为"John Doe"的用户.

有两种方法可以启用冲突检测:

  1. 检查命令的创建日期(即员工修改的时间)是否最后修改日期之后Customer.这将禁用方案2的自动合并功能,但会针对并发编辑提供完整的冲突检测.
  2. 检查是否命令的创建日期(即员工修改的时间)的最后修改日期个别领域的的Customer它将会改变.这将使场景2的自动合并保持不变,但会在场景1中为您提供自动冲突检测.

两者都易于通过事件源实现(因为事件流中的各个事件的时间戳可能是已知的).

至于你的问题"我们在方案1中保留了哪些变化?" - 这取决于您的业务领域及其要求.

编辑-1:回答澄清问题:

是的,对于可以单独更改的每个字段(或字段组),您将需要一个命令.

关于你的模型:你展示的是典型的"CRUD"UI,即多个表单字段,例如一个"保存"按钮.CQRS通常并且自然地与"基于任务"的UI组合,其中,例如,Status将显示该字段(只读),并且如果用户想要改变状态,则单击,例如,"更改状态" "按钮,打开一个对话框/新窗口或其他UI元素,可以更改状态(在基于Web的系统中,就地编辑也很常见).如果您正在执行"基于任务"的UI,其中每个任务仅影响所有字段的一小部分,那么ChangeName,ChangeSupplier等的细粒度命令是很自然的.