DDD 处理随着时间的推移聚合更新

DDD handling Aggregate updates over time

使用事件溯源,我有一个域,其中的聚合应该不时更新。当我创建一个聚合时,我有一个到期时间(这可以是任意的),在那之后我必须更新实体的一些属性。 (这也可以使用 UpdateCommand 强制执行。)我脑子里有几个进程:

  1. 聚合创建后,我将聚合 ID 和到期时间存储在 RDBMS 中。
  2. 在 cron 作业中,我在数据库中查询过期聚合,并提交 UpdateCommand

其他包括从读取端发出 UpdateCommand(或事件?)。 使用 saga 来协调更新,这与第一个类似。但无论哪种方式,我都必须存储到期时间。

所以,我必须存储事件并在写入端以事务方式写入数据库。但是,我不确定为写入端创建 读取端 (?) 是否是 DDD 世界中的正确解决方案,或者是否适用?推荐的解决方案是什么?

ES和DDD的兼容性很好

However, I am not sure if creating a read-side for the write-side (?) is the correct solution in the DDD world, or is it applicable?

是的,在您的情况下它是域聚合的一部分(如果您谈论在写入端存储过期时间)。

So, I have to store the events and write into a database on the write side transactionally.

我建议你使用 saga 写入数据库。

我还需要在一段时间后 运行 一些命令。

例如,我需要在 1 年后发出一个 ContractExpiredEventContractAggregate 决定何时,但通常是 1 年)。问题是聚合必须是决定何时执行什么命令的聚合,所以这是一个领域问题而不是基础设施问题。

我是怎么做到的?我受到 Udi Dahan's 视频的启发,他在视频中介绍了术语 Timeout。长话短说,Aggregate 请求在一段时间后向自己发送命令。它通过从命令处理程序中产生它来做到这一点。底层 CQRS 框架获取 scheduled command 并将其保存在一个特殊的存储库中。然后,cron job 在时机成熟时处理所有计划的命令。

John Carmack, 1998:

If you don't consider time an input value, think about it until you do -- it is an important concept

您应该寻找的模式是现实世界(时间所在)告诉聚合体当前时间,聚合体决定是否自身过期

有了这个模式,您可以使用任何您喜欢的策略来调度真实世界告诉集合现在是什么时候。

您不需要在聚合中立即进行一致的调度,您只需要一些幂等消息处理和一个 "at least once" 传递过程。

the aggregate has a method which can cause an update if it is necessary based on the current time, not blindly. At some time I have to fetch the right aggregate from the store, call that method and store the changes back (if any), or retry later, right?

是的,这是正确的想法。

请注意,如果您在到期时间后调用该方法两次,第一次调用将加载历史记录、追加到期事件并存储更新的历史记录。 second 调用加载历史,可以看到聚合已经过期,并在不对历史进行任何更改的情况下退出。

您还可以使用双时态事件溯源。存储事件时,有两个日期:

  • 事件添加到数据库的日期(createdAt)
  • 必须应用事件的日期(validFrom)

然后按照 validFrom 属性.

定义的顺序应用事件

使用它,您可以:

  • "fix the past" 通过添加新事件(createdAt = nowvalidFrom = now - x
  • 通过添加新活动(createdAt = nowvalidFrom = now + y)安排将来的活动

我建议在 DDD Europe 2018 观看 Thomas Pierrain 的精彩视频:https://www.youtube.com/watch?v=xzekp1RuZbM