事件驱动架构
00:00
事件驱动架构、事件溯源、CQRS模式与应用。
1. 事件驱动架构概述
1.1 核心概念
事件驱动架构以事件的产生、检测和消费为核心:
事件生产者 → 事件通道 → 事件消费者
| 概念 | 说明 |
|---|---|
| 事件 | 状态变化的记录 |
| 生产者 | 产生事件的组件 |
| 消费者 | 处理事件的组件 |
| 通道 | 事件传输的媒介 |
| 事件存储 | 事件的持久化 |
1.2 事件拓扑
| 拓扑 | 说明 | 适用场景 |
|---|---|---|
| 事件通知 | 简单通知,不含数据变更 | 状态变更通知 |
| 事件携带状态转移 | 事件包含完整数据 | 跨服务数据同步 |
| 事件溯源 | 所有变更以事件序列存储 | 审计、回溯 |
2. 事件溯源
2.1 核心思想
不存储当前状态,而是存储所有状态变更事件:
传统: 账户余额 = 1000
事件溯源:
AccountCreated(balance=0)
MoneyDeposited(amount=500)
MoneyDeposited(amount=300)
MoneyWithdrawn(amount=100)
→ 余额 = 0 + 500 + 300 - 100 = 700
2.2 事件存储
事件流:
┌──────┬──────────────────┬───────────┐
│ 序号 │ 事件类型 │ 数据 │
├──────┼──────────────────┼───────────┤
│ 1 │ AccountCreated │ {id: "A1"}│
│ 2 │ MoneyDeposited │ {amt: 500}│
│ 3 │ MoneyDeposited │ {amt: 300}│
│ 4 │ MoneyWithdrawn │ {amt: 100}│
└──────┴──────────────────┴───────────┘
2.3 快照优化
当事件过多时,定期保存快照:
快照@事件100: {balance: 700, ...}
事件101: MoneyDeposited(200)
事件102: MoneyWithdrawn(50)
重建: 从快照开始,重放事件101-102
2.4 事件溯源优缺点
| 优点 | 缺点 |
|---|---|
| 完整审计追踪 | 事件流可能很长 |
| 时间旅行(任意时刻状态) | 查询复杂 |
| 天然事件驱动 | 存储空间大 |
| 事件不可变 | 需要快照优化 |
3. CQRS
3.1 核心思想
命令查询职责分离:写模型和读模型独立设计:
┌── 命令模型 (写) ──→ 写数据库
客户端 ──┤
└── 查询模型 (读) ←── 读数据库
↑ 同步 ↑
└── 事件 ──┘
3.2 CQRS + 事件溯源
命令 → 聚合根 → 产生事件 → 事件存储
│
┌─────────┼─────────┐
▼ ▼ ▼
投影1 投影2 投影3
│ │ │
▼ ▼ ▼
读模型1 读模型2 读模型3
- 写端:事件溯源,保证一致性
- 读端:多个投影,针对不同查询优化
- 同步:异步事件驱动,最终一致性
3.3 CQRS适用场景
| 适用 | 不适用 |
|---|---|
| 读写比差异大 | 简单CRUD |
| 复杂业务规则 | 小型应用 |
| 需要不同读模型 | 团队经验不足 |
| 事件溯源需求 | 强一致性要求 |
4. 事件模式
4.1 事件设计
{
"eventId": "uuid-1234",
"eventType": "OrderCreated",
"timestamp": "2024-01-15T10:30:00Z",
"aggregateId": "order-5678",
"version": 1,
"data": {
"orderId": "order-5678",
"customerId": "cust-9012",
"items": [{ "productId": "p1", "quantity": 2 }],
"totalAmount": 99.99
}
}
4.2 事件版本化
| 策略 | 说明 |
|---|---|
| 向后兼容 | 新增字段有默认值 |
| 多版本消费者 | 消费者支持多版本 |
| 事件升级 | 中间层转换旧事件 |
5. 事件驱动架构模式
| 模式 | 说明 | 示例 |
|---|---|---|
| 事件通知 | 只通知变更 | 订单状态变更通知 |
| 事件携带状态 | 事件包含完整数据 | 库存变更事件 |
| 事件溯源 | 存储所有事件 | 金融交易系统 |
| CQRS | 读写分离 | 高并发查询系统 |
| Saga | 事件驱动的分布式事务 | 订单-支付-库存 |