当系统服务越来越多,业务流程跨越多个服务时,你会发现:
- 一个小改动可能影响 N 个服务
- 同步调用链太长,容易雪崩
- 服务耦合高,迭代成本大
传统微服务的同步调用模式已经不够用。
事件驱动架构(Event-Driven Architecture, EDA)解决了这个问题,让服务之间 “只关心事件,不关心谁在订阅” 。
在本篇文章里,我们将从 DDD 的领域事件出发,讲解如何在微服务中实现 高可扩展、低耦合、易演化的事件驱动架构。
第一部分:什么是领域事件
领域事件(Domain Event)是:
业务状态发生重要变化时发布的事件,表达业务事实,而不是技术消息
特点:
- 语义明确:用业务语言描述
- 不可变:事件一旦发生,内容不可更改
- 可订阅:其他服务可以响应事件,执行异步逻辑
示例:
AssetDiscovered { AssetID: "A123", IP: "192.168.0.1", Ports: [80,443] }
OrderPaid { OrderID: "O456", Amount: 120.0, PaidAt: "2025-12-02" }
第二部分:事件驱动解决的问题
| 问题 | 同步调用 | 事件驱动 |
|---|---|---|
| 服务耦合 | 高,调用链长 | 低,服务只关注事件 |
| 可靠性 | 一个服务异常可能连锁失败 | 异步,可重试、可补偿 |
| 扩展性 | 新功能需改老服务 | 新订阅者可随时加入 |
| 系统演化 | 复杂且危险 | 安全可演化 |
简单一句话:事件驱动 = 微服务的“神经系统”。
第三部分:领域事件在微服务中的实践
1. 发布事件
服务内部状态变化后发布事件,而不是直接调用其他服务:
type Asset struct {
ID string
IP string
Ports []int
}
func (a *Asset) Discover() {
events.Publish(AssetDiscovered{AssetID: a.ID, IP: a.IP, Ports: a.Ports})
}
- 聚合根负责触发事件
- Infrastructure 层负责事件的发送(消息队列 / Kafka / RabbitMQ)
2. 订阅事件
其他服务订阅事件并执行业务逻辑:
events.Subscribe(func(e AssetDiscovered) {
// 调用策略匹配服务
strategyService.Match(e.AssetID)
})
- 订阅者可以是多个
- 新增订阅者无需改发布者
- 松耦合,高可扩展
3. 事件建模技巧
-
以业务事实为中心
- 而不是技术动作
- ✅ AssetDiscovered, OrderPaid
- ❌ AssetInserted, PaymentProcessed
-
粒度适中
- 事件过大 → 消费复杂
- 事件过小 → 数量庞大
-
保证事件顺序(必要时)
- 对同一个聚合内事件保证顺序
- 不同聚合事件可以异步处理
-
配合 Saga 处理跨服务事务
- 异步补偿代替分布式事务
第四部分:实战案例(资产策略匹配)
假设业务流程:
- 资产发现 → 发布 AssetDiscovered
- 策略服务订阅事件 → 匹配策略 → 生成匹配结果 → 发布 StrategyMatched
- 告警服务订阅 StrategyMatched → 发送告警
事件链示意图:
AssetService ── AssetDiscovered ──▶ StrategyService ── StrategyMatched ──▶ AlertService
- 每个服务独立演化
- 新业务逻辑只需订阅事件即可
- 系统松耦合、可扩展
第五部分:总结
- 事件驱动是微服务的天然伴侣:解决服务间强依赖与同步调用问题
- 领域事件让业务语言贯穿整个系统:发布者和订阅者都用统一业务术语
- 可配合 Saga / 补偿事务,实现分布式事务处理
- 核心原则:只关心事件,不关心谁在订阅