java - 事件溯源“UNDO”流程中的乐观锁机制
问题描述
我一直在问很多关于事件采购的问题,所以对此表示歉意,但我想从一开始就明确这一点。
设置
| p_key | invoice_id | EmployeeId | Event type | Version | Data |
|-------|------------|------------|-------------------|---------|------|
| 1 | 12345 | E456 | Invoice_Generated | 1 | JSON |
| 2 | 12345 | E567 | Invoice_Reviewed | 2 | JSON |
| 3 | 12345 | E456 | Invoice_Paid | 3 | JSON |
| 4 | 12345 | E142 | Invoice_Comment | 4 | JSON |
| 5 | 12345 | E412 | Invoice_Comment | 5 | JSON |
| 6 | 12346 | E999 | Invoice_Paid | 7 | JSON |
| 7 | 12345 | E456 | Invoice_Refunded | 8 | JSON |
我假设 invoiceId 是聚合。由于版本号会随着发票的每次更改而增加。
用例:
事件存储包含应用于发票的所有事件,还包含有关哪个员工应用它的信息。在当前场景中,生成、审核、支付发票。有人注意到一些问题发表了一些评论,然后我们决定在退还旧款项之前发布新的付款[正在撤消的事件在历史上更早]。
API调用:
退款/发票/{invoiceid}/{employeeid}
事物的流动
选项1
- 检索给定发票的所有事件
- 将所有事件保存在事件流中,并保存最新版本。
- 循环遍历所有事件以找到所需的事件实例。
- 对该事件应用“撤消”操作并为其指定版本号 (latest+1)
- 调用数据库以检查事件存储中的最新版本。验证它是否与 Event Stream 中保存的相同。
- 确保新事件大于流中的上一个版本。我们假设它也是“DO ACTION”的更高版本。
问题
- 每次在应用程序中检索和保存所有事件可能会开始变得昂贵。
- 仅“撤消”操作似乎需要做很多工作。
选项 2:
- 我对数据库进行了 2 次调用
- 根据发票 ID 和员工 ID 调用以获取事件
- 调用以获取发票上应用的最后一个事件的最新版本。
- 根据事件中的数据撤消更改
- 创建一个版本号 1 大于最新版本的“UNDO”事件。
- 再次调用数据库以再次获取最新版本。
- 确保“UNDO”版本正好比最新版本大 1
问题
不确定这是否正确。我们是否应该多次将内容添加到事件流中。
也可以查询事件数据库两次,一次仅使用发票 ID,一次使用发票 ID 和员工 ID
如果我遗漏了什么,或者我的版本风格错误,或者我对聚合的假设是错误的,请告诉我。
解决方案
我建议在你的脑海中弄清楚几点。
无论您对域模型进行什么更改,您都可能希望使用相同的管道;无论是处理一个新命令,还是撤销一个较早的命令,实际的机制都应该以同样的方式对待。
如果“撤消”是您需要的,那么这应该是您的域模型理解如何做的事情,并且您保存在内存中的数据的形状应该支持这些功能。因此,如果您正在设计一个发票模型,允许您查询其历史记录中的特定事件,那么您的内存表示应该允许您访问这些事件。
性能优化的第一条规则是“不要”,但如果您担心从数据库读取数据的成本,您可以缓存模型的热表示。
如果您想弄清楚关系数据库中的乐观写入,Jonathan Oliver是一个很好的起点。
推荐阅读
- nestjs - 如何在独立的 NestJS 应用程序中使用模板引擎而不使用任何控制器?
- scala - Scala scopt:参数 required() 基于一个或多个其他参数
- sql - 试图将 4 年添加到现有的 Fiscal Yr 中,而我正在为 add_months(a.FY,48).FY 格式为 YYYY
- javascript - 通过组合两个数组创建一个数组对象
- java - 如何结合 Mono 和 Flux 创建一个对象?
- angular - 来自@nrwl/linter 的奇怪节点模块错误
- python - 为什么我的 Python 代码运行这么慢?我怎样才能加快速度?
- python - NumPy 中的双索引分配
- mysql - MariaDB 找不到表/数据库了
- arrays - 如何在使用 Hash.Lib 的同时使用 while 循环插入值?