不错的挑战。虽然我很欣赏user568109的答案,但这就是我在 CQRS/DDD 应用程序中处理类似情况的方式。
虽然在 DDD 应用程序中我有很多不同的命令和查询,但在 CRUD 应用程序中,对于每种类型的“记录”,我们都有 CREATE、UPDATE 和 DELETE 命令以及一个 READ 查询。
在我的系统中,在客户端上,我在一个包含以下内容的元组中跟踪先前的同步:服务器上的 UTC 时间,客户端上的时间(我们称之为LastSync)。
READ
读取查询不会参与同步。不过,在某些情况下,您可能必须向服务器发送 LogRead 命令来跟踪用于做出决策的信息。此类命令确实包含实体的类型、实体的标识符和 LastSync.ServerTime)。
CREATE
创建命令根据定义是幂等的:它们要么成功要么失败(当已经存在具有相同标识的记录时)。在同步时,您将不得不通知用户冲突(以便他可以处理这种情况,例如通过更改标识符)或修复时间戳,如下所述。
UPDATE
更新命令有点棘手,因为您可能应该在不同类型的记录上以不同方式处理它们。为简单起见,您应该能够强制用户最后一次更新总是获胜,并将命令设计为只携带应该更新的属性(就像 SQL UPDATE 语句一样)。否则,您将不得不处理自动/手动合并(但相信我,这是一场噩梦:大多数用户永远不会理解它!)最初我的客户要求大多数实体使用此功能,但过了一段时间他们接受了最后一个更新获胜以避免这种复杂性。此外,如果对已删除对象进行更新,您应该将情况通知用户,并根据更新的实体类型,应用更新或不应用更新。
DELETE 删除
命令应该很简单,除非您必须通知用户发生了可能导致他保留记录而不是删除记录的更新。
您应该仔细分析如何为每种类型的实体处理此命令(并且在更新的情况下,您可能被迫针对要更新的不同属性集以不同方式处理它们)。
SYNC PROCESS
同步会话应该开始向服务器发送一条消息
这样,服务器可以计算其时间和客户端时间之间的偏移量,并将这种偏移量应用于他收到的每个命令。此外,它可以检查在LastSync之后偏移量是否发生了变化,并选择一种策略来处理这种变化。请注意,这样一来,服务器将不知道客户端的时钟何时被调整。
在成功同步结束时(由您决定成功的含义),客户端将更新LastSync元组。
最后一点
这是一个相当复杂的解决方案。在开始实施之前,您应该与客户仔细考虑这种复杂性是否能给您足够的价值。