第22章 事件溯源
第22章 事件溯源
领域驱动设计DDD实践者通常喜欢使用CQRS来合并事件溯源作为增强扩展性和性能的基础。
- 将状态存储为快照的限制
如今大部分系统都只是存储了领域模型的当前状态,也就是状态存储为快照,因此不能分析历史行为,无法理解系统是如何变成该状态的,而事件溯源是存储了完整的历史changes数据。
- 通过将状态存储为事件流来获得竞争优势
-
- 时态查询
时态查询,可以追溯用户发生变化的任何时点的状态,且事件流的数据,可以用于帮助市场部门制定市场营销和产品开发决策或者指导试验。
-
- 投影
投影是让你能够从多个流中合并事件以便执行这些类型的复杂时态查询的潜在特性。
类似SQL数据库中的连接查询,在事件存储中,把这个概念称为投影,这就是将一系列输入事件流映射到一个或多个新的输出流上的查询。
如下例子:
回答“在某体育赛事期间某特定人群在某一天中一共进行了多少分钟的通话?”
将所有需要的事件放在单个流中,就能轻易高效地对所有事件进行查询以便找出需要被提取信息的合计值,平均值或者信息的其他部分等。
-
- 快照
将状态作为事件存储的一个后果就是,事件流会增长的非常大,这意味着重现事件所需要的时间会持续增长。为了避免这一性能影响,事件存储要使用快照。
快照是一个事件流中代表重现所有之前事件后的状态的中间阶段。
- 源自事件的聚合
为了得到事件溯源的兼容性,聚合需要面向事件。
DDD实践者发现,使用事件溯源机制下,其聚合更为面向行为,所以事件需要有较高水平的领域事件表述性。
-
- 构造
在为源自事件的领域模型创建聚合时涉及一些关键细节。
1 |
添加事件溯源能力 |
2 |
公开表述性专注于领域的API |
3 |
添加快照支持 |
|
|
-
- 持久化与再融合
持久化源自事件的聚合只是在事件存储中存储未提交事件的一个用例而已。同样,加载聚合,也称为再聚合,需要你加载和重现所有之前存储的事件,它提供了使用快照作为快捷方式的选项。
1 |
创建一个事件溯源存储库 存储库需要支持的三种主要操作:创建流,附加到流以及加载流。 |
2 |
添加快照持久化和再加载 |
3 |
处理并发性 首先,当一个聚合被加载时,它会在事务开始时保持版本号的记录。 第二,在将任何事件附加到流之前,会执行一个检查以确保最后被持久化的版本号与事务开始时聚合的版本号相匹配。 |
|
|
- 构建一个事件存储
可以自己构建事件存储,可以使用 RavenDB和SQL Server作为事件存储的基础。
-
- 设计一种存储格式
这里需要实现 创建事件流,将事件附加到流以及按照相同顺序重新将哪些事件拉回来的能力。对应的三种文档类似性就是:EventStream,EventWrapper以及SnapshotWrapper。
1 |
|
2 |
|
3 |
|
-
- 创建事件流
-
- 附加事件流
-
- 查询事件流
在构建事件存储功能的时候,你至少需要纳入对一个流中所有事件进行查询的能力。
你还需要提供通过事件的版本或ID进行查询的能力,以便支持快照加载。
-
- 添加快照支持
-
- 管理并发性
在一个源自事件的应用程序中,可以使用两个额外的实现开放式并发。
首先,当一个聚合被加载的时,它会在事务开始时保持版本号的记录。
第二,在将任何事件附加到流之前,会执行一个检查以确保最后被持久化的版本号与实务开始时聚合的版本相匹配。
-
- 一个基于SQL Server的事件存储
除了RavenDB,SQL Server外,还有Ncqrs,NEventStore 等开源项目。
1 |
选择一种数据结构 XML,JSON ? |
2 |
创建一个流 |
3 |
保存事件 |
4 |
从流中加载事件 |
5 |
快照 |
|
|
-
- 构建你自己的事件存储是一个好主意吗
在早期没有可行的事件溯源存储机制,但现在有Grep Young的Event Store这样的工具,提供了许多开箱即用的高级功能,比如 投影,复杂时态查询,以及增强扩展性,这些手工实现,并不会增加业务价值。
- 使用专门构建的Event Store
- 安装Greg Young的Event Store
- 使用C#客户单库
- 运行时态查询
- 创建投影
- 使用事件溯源的CQRS
原始 |
基于事件流直接查询统计。
|
CQRS |
基于一个事件流提前预计算出视图,外面视图直接查询视图即可,无需计算。 |
-
- 使用投影创建视图缓存
-
- CQRS和事件溯源协作
CQRS和事件溯源协作,可以毫不费力地从事件流中创建视图缓存作为投影。
1 |
作为队列的事件流; Event Store使用事件流作为超媒体Atom源来公开。 你不必另外使用一种技术用于视图缓存以及讲解另一种技术用于非规范化处理过程。 |
2 |
由于Event Store是你的主要数据源,用于投影的工具,以及你的队列技术,所以无需担心两阶段提交(分布式事务)的问题。 |
- 简要复述事件溯源的好处
-
- 竞争型业务优势
与领域专家进行交流是应用DDD并且认识其优势的重要方面。
源自事件的聚合几乎是声明式的,他们各自读起来像一个句子 When()的重载:
When (Domain Event) {Apply Business Rules};
-
- 专注于表述性行为的聚合
- 简化的持久化
- 更好的调试
- 衡量事件溯源的代价
切合实际地考虑事件溯源的负面影响也很重要。
需要考虑投入额外的时间,汇报,尤其是时间成本。
-
- 版本控制
数据的版本和迁移问题。
-
- 要学习新概念和要磨练的技能
- 需要学习和掌握新技术
- 大量的数据存储需求
相对于只存储状态而言,存储活动的整个历史将导致状态需要更多的信息被存储在硬盘上。
- 额外的学习资料
- 要点回顾
存储历史提供了解决围绕时间的强大查询能力—时态查询。
在使用事件溯源时,领域模型需要包含面向事件的聚合。
事件溯源通常需要投入大量精力,但不会带来足够的投资回报,所以要谨慎使用它。