site logo

Marico's space

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

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

最近折腾事件驱动架构,踩了几个坑,这篇把四种方案说清楚: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 当...

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