CQRS、事件溯源和 Web 应用程序

CQRS, Event-Sourcing and Web-Applications

在阅读一些 CQRS 资源时,有一个反复出现的问题我没有听清。例如,假设客户端发出命令。此命令由域集成,因此它可以刷新其域模型(DM)。另一方面,命令保存在事件存储中。这是最常见的情况。

1) 当我们说 DM 被刷新时,我想数据是持久保存在底层数据库中的(如果有的话)。我对吗 ?否则,我们将处理内存瞬态模型,我想这不是一件好事? (状态不应该在客户端请求之外保留在服务器端的内存中)。

2) 如果数据持久化,我想依赖它的读取模型会自动更新,因为每个请求它的客户端都会在应用程序中生成一个新的 "state/context"(如果是 Web-应用程序或 RESTful 架构)?

3) 如果命令被持久化,这是否意味着我们处理事件源(当我们使用 CQRS 时通过构造)?事件溯源是否会使数据库更新过程无效? (好像状态是从事件存储中重建的,维护数据库似乎没用)?

CQRS 是否仅适用于多数据库系统(当数据在单独的数据库上传播时),并且,如果它处理内存瞬态模型,它是否适合 Web 应用程序或 RESTful 服务 ?

需要注意的是,唯一存储的是事件*。域模型是根据事件重建的。

所以是的,正如您所说,域模型是内存瞬态的,因为没有存储域模型的表示*仅存储发生在域中以使模型处于当前状态的事件。

当加载域模型中的元素时,会创建该元素的新实例,然后影响该实例的事件将以正确的顺序一个接一个地重播,以将元素放入正确的位置状态。

你可以保留域对象的实例并订阅新事件,这样它们就可以保持最新,而不必每次都从所有事件中加载它们,但通常它足以快速加载所有事件数据库并每次都以与每次调用 Web 服务时从数据库加载实例相同的方式应用它们。

*除非您有领域对象的快照以减少您需要 load/process

的事件数量

并非严格需要数据持久化。在足够多的不同位置 (GigaSpaces) 拥有足够的副本可能就足够了。所以不,不需要数据库。这是(至少在几年前)被荷兰 eBay 等价物用于生产。

1) 如前所述,唯一真正存储的是事件。 命令所做的唯一事情是在事件发生之前进行一致性检查。在伪代码中:

public void BorrowBook(BorrowableBook dto){
    if (dto is valid)
        RaiseEvent(new BookBorrowedEvent(dto))
    else
        throw exception
}

public void Apply(BookBorrowedEvent evt) {
    this.aProperty = evt.aProperty;
    ...
}

当前状态通过顺序应用检索。因此,你必须在设计阶段特别注意,因为有一些常见的陷阱需要避免(也许你已经读过,但让我推荐一下 Martin Fowler 的 this 文章)。

到目前为止一切顺利,但这只是事件溯源。如果您决定使用不同的数据库来保存聚合状态,CQRS 就会发挥作用。 在我的项目中,我们有一个 projection,每 x 分钟在聚合上应用新事件(来自事件存储),并将结果保存在 MongoDB(表示层)的单独实例中将访问此数据库进行读取)。这个模型显然最终是一致的,但是这样你就真正把命令(写)和查询(读)分开了。

2) 如果您决定将写入模型与读取模型分开,您可以使用多种选项使它们同步:

  • x 秒从最后一个检查点应用事件(一些解决方案提供 快照 以避免重新应用繁重的命令)
  • 订阅事件并在事件发生后立即更新读取模型的投影

3) 唯一存储的是事件。事实上,我们有一个事件存储,而不是命令存储:)

数据库没用了吗?要看!聚合到当前状态需要重新申请多少个事件? 三?也许你不需要为 read-model

建立数据库