site logo

Marico's space

事件驱动系统:Webhook vs EventBridge-style API vs Event Sourcing vs CQRS

前端技术 2026-09-21 11:28:29 12

最近折腾事件驱动架构,踩了几个坑,这篇把四种方案说清楚:Webhook、EventBridge 风格 API、Event Sourcing、CQRS。

这四种技术经常被混为一谈,但其实解决的问题完全不同。选错了要么白忙活,要么把自己坑进去。

什么是事件驱动系统?

事件驱动系统不直接请求数据或调用功能,而是通过发布「已发生的事实」来进行通信。

比如:

  • 订单已创建
  • 支付已完成
  • 用户已注册
  • 发票已生成
  • 快递已送达

事件本身不告诉消费者该做什么,只传达「发生了什么」。消费者自己决定是否响应、如何响应。

现代系统往往由多个独立服务、应用和团队组成,直接通信会产生依赖。事件能降低耦合,让生产者和消费者独立演进。

常见场景:

  • 系统集成
  • 业务流程自动化
  • 分布式架构
  • 审计合规系统
  • 事件驱动工作流
  • 数据同步
  • 响应式应用

消息系统和事件驱动系统经常一起用,但不是一回事。消息系统提供传输基础设施,事件驱动系统定义系统间如何通过事件通信和协调。

事件驱动集成 vs 事件驱动架构

这一类包含两个相关但不同的概念。

事件驱动集成聚焦于在不同系统之间传递事件。

比如:

  • Webhook
  • EventBridge 风格 API

目标是集成和事件分发。

事件驱动架构(EDA)聚焦于应用内部如何围绕事件设计。

比如:

  • Event Sourcing
  • CQRS

目标是通过事件建模、存储和处理业务行为。

一个实用判断标准:

  • 集成技术帮助系统通信。
  • 架构模式帮助系统内部组织和处理信息。

各自的目的

技术 类型 主要目的
Webhook 事件驱动集成机制 当事件发生时通知外部系统
EventBridge 风格 API 事件路由与集成平台 跨多个系统和消费者分发事件
Event Sourcing 架构模式 将应用状态存储为事件序列
CQRS 架构模式 分离命令和查询职责

这些技术经常组合使用:

  • Webhook 可以通知外部系统某个内部生成的事件。
  • EventBridge 风格平台可以分发来自 Event Sourcing 系统的事件。
  • Event Sourcing 和 CQRS 常一起使用来构建事件驱动应用。
  • CQRS 系统经常消费通过消息基础设施传输的事件。

决策树

集成和内部架构是独立的决策——一个系统可能只需要一个,或者同时需要两个。

外部事件如何集成?

  • Webhook通常是通知外部系统的最简单选择。
  • EventBridge 风格 API当事件需要路由到多个消费者或跨多个系统时变得有价值。
flowchart TD A1[How should events be<br/>integrated externally?] --> B1{Need simple notifications<br/>to external systems?} B1 -->|Yes| WEBHOOKS[Webhooks]
B1 -->|No — multiple consumers<br/>or systems involved| EVENTBRIDGE[EventBridge-style APIs]

内部事件如何处理?

  • Event Sourcing当事件历史本身成为业务需求时最有用。
  • CQRS当读写 workload 有显著不同需求时最有用。
  • Event Sourcing + CQRS经常出现在需要审计、可扩展性和专用读模型的事件驱动系统中。
flowchart TD A2[How should events be<br/>handled internally?] --> D{Need a complete historical<br/>record of state changes?} D -->|No| E{Need independent read and<br/>write models or optimized queries?} E -->|No| F[Traditional Architecture]
E -->|Yes| CQRSONLY[CQRS Alone] D -->|Yes| G{Need independent read and<br/>write models or optimized queries?} G -->|No| ES[Event Sourcing Alone]
G -->|Yes| ESCQRS[Event Sourcing + CQRS]

比如一个系统可能用 Webhook 做外部通知,内部却组合使用 Event Sourcing 和 CQRS——两个决策树是独立使用的,不是一个非此即彼的选择。

对比

方面 Webhook EventBridge 风格 API Event Sourcing CQRS
类别 事件驱动集成 事件路由与集成 架构模式 架构模式
抽象层级 集成机制 事件分发平台 状态管理模式 读写分离模式
主要目的 通知外部系统 路由和分发事件 将状态变更存储为事件 分离命令和查询
存储事件? 有时,取决于平台
事件路由? 基础 核心能力
外部集成? 间接 间接
内部架构?
历史重放? 有限 取决于平台 核心能力 否——仅在与 Event Sourcing 配对时可用
复杂度 中到高
典型场景 第三方集成、通知 事件分发、云集成 审计、金融系统、工作流追踪 大规模系统、专用读模型
优势 简单、广泛支持、易采用 集中式事件路由和扇出 完整事件历史和可重放性 灵活扩展和优化的读写路径
劣势 路由和可靠性保证有限 额外的基础设施和运维复杂性 显著的建模和运维复杂性 额外的架构复杂性

Webhook

Webhook 起源于 2000 年代中期。Webhook 从来不是正式协议,它作为一种实用的集成模式演化而来。早期的 API 靠轮询数据:

Your App |
GET /status |
No Change Wait GET /status |
No Change Wait GET /status

随着 SaaS 产品增长,轮询变得低效。企业需要更好的方式向客户通知事件。

Webhook 回答的问题是:一个系统如何在事件发生时立即通知另一个系统?

不再是:

Consumer |
"Anything new?"

反复询问,而是:

"I'll call you when
something happens."

这彻底改变了 API 集成的方式。

Webhook 用回调思维

核心假设:消费者提供一个 URL 并等待事件。

不再是:

Client ---> Server

而是:

Client gives URL Server ---> Client

方向反转了。

示例

假设支付成功。服务提供商发送:

POST /webhook Content-Type: application/json

Body:

{ "event": "payment.succeeded", "amount": 100
}

你的应用接收:

app.post("/webhook", (req, res) => { console.log(req.body); res.sendStatus(200);
});

输出:

{ "event": "payment.succeeded", "amount": 100
}

优缺点

优点 缺点
极其简单 可靠性需要仔细处理
实时通知 消费者必须暴露公网端点
消除轮询 可能出现重复投递
使用标准 HTTP 调试失败可能很困难
跨公司集成容易 没有内置重放机制
被 SaaS 平台广泛支持 不适合高流量场景
基础设施要求低 投递保证有限

EventBridge 风格 API

云原生事件总线在 2010 年代后期兴起,云厂商希望将「生产者声明,消费者响应」模式formalize为托管服务。AWS EventBridge 在 2019 年推出,随后是 Azure Event Grid、Google Eventarc 等类似服务。这些平台让事件驱动集成在大规模场景下变得流行,团队有了托管的事件总线,而不必自己构建和运维。

随着系统变得更分布式,企业的架构变成了这样:

User Service | +--> Email Service | +--> Analytics | +--> Billing | +--> CRM

每个新消费者都需要再集成一次。

时间久了:

One Event |
Ten Integrations |
Twenty Integrations

变得难以管理。EventBridge 风格系统回答的问题是:服务如何只发布一次事件,就能让任何消费者响应,而不用创建直接集成?

不再是:

Producer |
Many Consumers

而是引入:

Producer |
Event Bus |
Many Consumers

生产者只知道总线。

EventBridge 用业务事件思维

核心假设:系统应该宣告事实,而不是调用其他系统。

不再是:

Order Service |
Call Email Service

而是:

Order Service |
OrderCreated Event |
Event Bus |
Email Service Reacts

示例

发布事件

{ "source": "orders", "type": "OrderCreated", "data": { "orderId": 123 }
}

事件规则

{ "type": "OrderCreated"
}

目标

Email Service

当订单创建时:

Order Service |
OrderCreated |
Event Bus |
Email Service

邮件服务自动收到事件。

优缺点

优点 缺点
服务间松耦合 调试和追踪更困难
容易添加新消费者 最终一致性
非常适合事件驱动系统 事件 Schema 版本管理挑战
集中式路由规则 可能产生隐藏依赖
云集成能力强 不适合请求/响应场景
组织层面可扩展 增加架构复杂性
支持自动化工作流 执行路径更难预测

Event Sourcing

Event Sourcing 在 2008-2012 年左右成为主流架构模式。

大多数应用只存储当前状态,历史丢失。

比如:

500 -> 700 -> 400 -> 900

更新后,只剩 900。

企业需要审计、可追溯和可重建能力,不想依赖脆弱的审计表。Event Sourcing 回答的问题是:如果我们存储每次变更而不是只存最新状态呢?

不再是:

Current Balance = 900

而是存储:

AccountOpened
MoneyDeposited(200)
MoneyWithdrawn(300)
MoneyDeposited(500)

当前状态随时可以重建。

Event Sourcing 用事实而非状态思维

核心假设:事件序列比当前状态更重要。

不再是:

User
------
Name: Alice
Plan: Pro

而是存储:

UserRegistered
PlanUpgraded

当前状态变成派生值。

示例

传统 CRUD

UPDATE accounts
SET balance = 500
WHERE id = 1;

结果:

Balance = 500

历史可能丢失。

Event Sourcing

追加事件:

{ "type": "MoneyDeposited", "amount": 100
}

然后:

{ "type": "MoneyWithdrawn", "amount": 50
}

当前余额由事件计算:

+100
-50
----
50

优缺点

优点 缺点
完整审计轨迹 复杂性显著
事件重放能力 事件 Schema 演进挑战
时间旅行调试 最终一致性
强合规支持 学习曲线陡峭
天然适合事件驱动系统 存储随时间增长
更容易历史分析 需要更多基础设施和工具
随时重建投影 对简单 CRUD 系统来说过度设计

CQRS

CQRS(命令查询职责分离)由 Greg Young 在 2010 年左右提出。

大多数应用对读写使用同一个模型:

Reading Data
Writing Data

比如:

User Table | Read Users Update Users Delete Users Create Users

这在初期工作得很好。

但大型系统经常发现读流量和写流量不匹配。

比如抖音可能每秒有几百万次读取,但只有几千次写入。

CQRS 回答的问题是:如果读写被当作独立关注点处理呢?

不再是一个模型处理所有,而是引入:

Write Model |
Read Model

每一方可以独立优化。

CQRS 用命令和查询思维

核心假设:更新数据的方式往往和读取数据的方式不同。

比如更新订单:

Validate
Authorize
Apply Business Rules

查看订单:

Fetch
Format
Display

这些关注点不一定要放在同一个模型里。

示例

传统 CRUD

Orders Table

用于:

SELECT *
FROM orders

和:

UPDATE orders
SET status='shipped'

同一个模型。

CQRS

写侧:

ShipOrder Command

读侧:

OrderDetails Query

两条独立路径。

Command |
Write Model
Query |
Read Model

优缺点

优点 缺点
读写独立优化 复杂性显著增加
更好的可扩展性 最终一致性问句
领域逻辑更清晰 需要更多基础设施
读模型更灵活 调试更困难
非常适合读密集型系统 需要维护更多移动部件
天然适合事件驱动架构 学习曲线更陡
可能显著提升性能 对很多应用来说过度设计

关键要点

用 Webhook 当...

  • 外部系统需要通知
  • 集成需求相对简单
  • 想要最低的运维开销

用 EventBridge 风格 API 当...

  • 事件必须分发到多个消费者
  • 系统高度解耦
  • 集中式事件路由有价值

用 Event Sourcing 当...

  • 事件历史是业务需求
  • 审计和可重放很重要
  • 状态变更必须永久保存

用 CQRS 当...

  • 读写 workload 有不同需求
  • 查询性能成为关注点
  • 命令和查询独立扩展有益

用 Event Sourcing + CQRS 当...

  • 构建复杂事件驱动系统
  • 历史事件存储和专用读模型都需要
  • 额外的架构复杂性被业务需求证明合理