【问题标题】:Event Sourced many-to-many aggregates事件源多对多聚合
【发布时间】:2022-02-13 09:45:07
【问题描述】:

我们有一个事件源系统,出于性能原因,我们将数据缓存到聚合中。

假设我们有 3 个实体。患者、医生、预约。

对我们有用的是一对多类型的关系。例如,想象以下事件:

* DOCTOR_CREATED
* DOCTOR_ARRIVED
* DOCTOR_LEFT

这我们可以聚合成一对多类型的关系。当医生添加到系统中时,我们可以在数据库中创建一行。

每次他们上班时,我们可以将那个时间添加到医生记录中,每次他们离开时,我们也可以添加它。所以我们最终会得到这样的结果:

{
  "id": 23,
  "name": "Dr Bill",
  "totalHoursWorked": 124,
  "timesheet": [
    {
      "arrivedAt": "2022-01-04 09:00:00",
      "leftAt": "2022-01-04 14:00:00",
      "hoursWorked":  5,
    },
    // etc...
  ]
}

没有问题。

现在假设我们要跟踪约会。这是用户和医生之间的多对多关系。

我对这些活动感兴趣:

* DOCTOR_CREATED
* PATIENT_CREATED
* APPOINTMENT_CREATED

由于事件流必须按时间顺序排列,因此在创建相关医生或患者之前,我无法创建预约记录。

如何从约会的角度创建数据模型的视图。

也许在 graphql 术语中考虑它可能会有所帮助,但我想优化这个查询:

query {
  appointment {
    day
    time
    patient {
      name
      age
    }
    doctor {
      name
      specialty
    }
  }
}

我希望能够将此数据结构作为聚合存储在数据库中。因此,可以在一个数据库查询中完成按 ID 获取约会。

我将在这里遇到时间线问题,因为如果我在数据库中创建行之前等待第一个 APPOINTMENT_CREATED 事件,那么我错过了相关的 PATIENT 和 DOCTOR 事件。

如果我在预期中首先捕获 PATIENT 和 DOCTOR 事件,那么我必须存储 Doctor 和 Patient 的所有可能组合,以防其中一个可能希望稍后进行预约。我也面临着数据结构不一致的聚合问题。表中的行可能由医患 id 或预约 id 索引,具体取决于我们要到达的事件流的哪个阶段。

我目前能想到的唯一方法是让尝试优化此查询的聚合等待 APPOINTMENT_CREATED 事件,然后必须异步查询数据库以检索该时间点的患者和医生记录.

尽管我们采用了实现系统的方式,但我们所有的聚合都是由纯函数组合而成的,这些纯函数仅采用先前的聚合状态、相关事件并返回新的聚合状态。

到目前为止,我想要的架构是不可能的吗?我是否需要一个逃生舱口来让我们的聚合水合执行异步数据库查询(不热衷于此)?

或者是解决这个问题的错误技术,我实际上需要使用其他东西(比如缓存)。话虽如此,但使用事件溯源的好处之一是我们不必为缓存而烦恼,因为我们可以预先构建所有聚合,以便为前端进行读取优化。

【问题讨论】:

标签: events many-to-many aggregate cqrs


【解决方案1】:

我可以明确地说,事件溯源并没有带来不必担心缓存的好处,也与预先构建聚合以进行前端读取优化没有任何关系。读取优化的数据模型是 CQRS,它与事件溯源完全不同(两者都不需要,尽管它们非常适合)。

CQRS/ES 系统中的读取端通常不会是事件源的:您将 CQRS 与事件源一起使用的原因是事件源模型对于查询来说很糟糕,因此您正在实施非事件源数据模型(例如关系或 NoSQL 文档...)以允许有效查询。

因此,您不需要在读取端使用与在写入端使用相同的结构。与写入端具有相同聚合的事件溯源尤其不太可能带来好处。

如果您确实在读取端使用事件源聚合,请注意这些聚合将不同于写入端的聚合。例如,构造约会的非规范化视图的过程可能非常适合事件源聚合:

  • APPOINTMENT_CREATED 事件(可能包括医生 ID 和患者 ID)上记录此过程开始构建非规范化视图
  • “开始构建...”事件的订阅者然后为相应的医生和患者重放事件(任何事件存储都应该支持这一点:重放特定聚合的事件是事件源中的规范操作)并使用这些事件为流程聚合构建“这里是此预约的医生(或患者)信息”命令
  • 聚合记录找到的信息,甚至可能记录“找到的一切”事件
  • 另一个订阅者正在构建要保存的非规范化视图,以便通过约会 ID 轻松查询

请注意,这里的“非规范化过程”聚合是纯粹的;副作用发生在对其事件的预测中。

我会在实践中这样做吗?可能不是。我可能会使用关系数据库并让读者加入。我可能只是让约会创建的事件启动异步查询以构建非规范化视图。不过,这种事情是可能的。

【讨论】:

  • 感谢您的输入,有道理。是的,我们正在旁边建立一个 CQRS 系统。
猜你喜欢
  • 2018-12-28
  • 1970-01-01
  • 2019-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多