问题:

两名员工(A和B)在编辑客户#123(例如版本#20)时同时脱机,而在脱机状态下继续进行更改...

场景:

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

2-两名员工编辑客户#123,但不要进行相同的更改(他们彼此交叉而不接触)。

...然后他们都重新上线,首先是雇员A追加,从而将客户更改为版本#21,然后将雇员B更改为版本20

问题:

在方案1中,我们保留哪些更改?

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

内容:

1-CQRS +事件源样式系统

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

3-读取模型的最终一致性

4-RESTful API



EDIT-1:根据到目前为止的答案进行说明:

为了执行细粒度的合并,例如,对于表格中的每个字段,我都需要一个命令?



上面,用于ChangeName,ChangeSupplier,ChangeDescription等的精细命令,每个命令都有自己的时间戳,如果A和B都更新了ChangedName,它们将自动合并。

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

似乎我将使用@GetEventStore来保持事件流的持久性。

它们利用乐观并发性如下:


流中的每个事件使流版本增加1
写入可以使用写入器上的ES-ExpectedVersion标头指定期望的版本


-1指定流不应该存在
0及以上指定流版本
如果流不是该版本,则写入将失败,您或者尝试使用新的预期版本号重试,或者重新处理该行为并确定可以(如果您选择这样做)。

如果未指定ES预期版本,则禁用乐观并发控制
在这种情况下,乐观并发不仅基于消息ID,而且还基于事件#

最佳答案

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

在此设置中,方案2会自动被场景2自动合并,如果您明智地选择了命令,请阅读:使其细粒度:对于每个可能的更改,请选择一个命令。然后,在重新连接客户端时,可以以任何顺序处理命令,但是由于它们仅影响分离字段,因此没有问题:


客户在v20。
A离线,针对过时的v20模型编辑更改。
B处于脱机状态,根据v20的过时模型编辑更改。
一个联机,批处理发送一个排队的ChangeName命令,v20的客户已加载并保持为v21​​。
B联机,批处理发送一个排队的ChangeAddress命令,v21的客户已加载并保持为v22。
该数据库包含用户及其正确的名称和地址,如预期的那样。


在方案1中,使用此设置,两名员工都将覆盖另一名员工的更改:


客户在v20。
A离线,针对过时的v20模型编辑更改。
B处于脱机状态,根据v20的过时模型编辑更改。
A联机后,批处理将排队的ChangeName命令发送给“ John Doe”,v20的“客户”被加载并保留为v21,名称为“ John Doe”
B联机,批处理将排队的ChangeName命令发送到“ Joan d'Arc”,v21的客户(名为“ John Doe”)被加载并持久化为v22(名称为“ Joan d'Arc”)。
数据库包含名称为“ Joan d'Arc”的用户。


如果B在A之前在线,那么反之亦然:


客户在v20。
A离线,针对过时的v20模型编辑更改。
B处于脱机状态,根据v20的过时模型编辑更改。
B联机,批处理将排队的ChangeName命令发送到“ Joan d'Arc”,v20的客户被加载并保持为v21​​(名称为“ Joan d'Arc”)。
一个联机后,批处理将排队的ChangeName命令发送到“ John Doe”,v21的客户被加载并以名称“ John Doe”的形式持久保存为v22。
数据库包含名称为“ John Doe”的用户。


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


检查命令的创建日期(即,员工修改时间)是否在Customer的最后修改日期之后。这将禁用方案2的自动合并功能,但将为您提供针对并发编辑的完全冲突检测。
检查命令的创建日期(即,员工修改的时间)是否在Customer的各个字段的最后修改日期之后。这将使方案2的自动合并保持原样,但是将为您提供方案1中的自动冲突检测。


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

关于您的问题“方案1中我们保留谁的更改?” -这取决于您的业务领域及其要求。

EDIT-1:要回答澄清问题:

是的,您需要为每个字段(或分别为一组字段)单独更改一个命令。

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

10-06 04:36