事件定义与分类:业务事件、系统事件、集成事件
好,咱们进入第二章。上一章聊了事件治理为什么重要,这一章得落地了——先搞清楚到底什么是事件,以及怎么给它分门别类。
说实话,我见过不少团队,一上来就定义事件,结果业务说这是事件,技术说那不是,吵了半天。为什么?因为大家对“事件”的理解不在一个频道上。所以,咱们先把概念对齐。
事件的本质:发生了值得关注的事
事件,说白了就是“某件事已经发生了”。它是个事实,不可更改。你想想看,在现实世界里,“订单已创建”、“支付已成功”、“库存已扣减”,这些都是已经发生的事实。在系统里,我们就把这些事实记录下来,广播出去,让需要知道的人知道。
我个人习惯把事件分成三大类:业务事件、系统事件和集成事件。这三者各有各的用途,也各有各的坑。
业务事件:业务人员看得懂的事
业务事件,就是业务人员能理解的事件。它描述的是业务领域里发生的、有业务含义的事情。
举个例子,“用户下单”就是一个业务事件。业务人员知道这意味着什么——用户选好了商品,提交了订单,接下来要处理支付、发货等等。业务事件的名字通常用过去分词,比如“OrderPlaced”、“PaymentReceived”、“ShipmentDelivered”。
我在项目中遇到过,有些团队把业务事件定义得特别技术化,比如“InsertOrderRecord”。业务人员一看就懵了——这啥意思?插入记录?所以,业务事件一定要用业务语言,别用技术术语。
业务事件的特征:
- 用业务语言描述,业务人员能看懂
- 代表一个已经发生的业务事实
- 通常对应一个业务操作的结果
- 事件名称用过去时态
系统事件:技术内部的事
系统事件,是系统内部发生的事情。它可能跟业务无关,但跟系统的运行状态有关。
比如,“数据库连接超时”、“CPU使用率超过90%”、“服务启动完成”。这些事件业务人员不关心,但运维人员和开发人员必须关注。
系统事件的作用是监控和告警。你想想看,如果系统出了故障,没人知道,那后果多严重。所以,系统事件是保障系统稳定性的重要手段。
我的经验:系统事件的定义要跟监控系统对齐。我曾经在一个项目里,系统事件和监控告警是两套体系,结果运维人员得看两个地方,效率极低。后来我们把它们统一了,一个事件源,多个消费者,清爽多了。
集成事件:系统之间的事
集成事件,是不同系统之间通信时使用的事件。它通常用于解耦——系统A不需要知道系统B怎么处理,只需要把事件发出去就行。
比如,订单系统发出“OrderCreated”事件,库存系统收到后扣减库存,物流系统收到后准备发货。这些系统之间没有直接调用,全靠事件驱动。
集成事件有个特点:它必须包含足够的信息,让消费者能独立处理。你不能让消费者再去查数据库,那又变成同步调用了。所以,集成事件的Schema设计特别重要。
三类事件的对比
为了让你看得更清楚,我整理了一张表:
| 维度 | 业务事件 | 系统事件 | 集成事件 |
|---|---|---|---|
| 受众 | 业务人员、产品经理 | 运维、开发人员 | 系统间消费者 |
| 语言 | 业务术语 | 技术术语 | 双方约定的契约 |
| 示例 | OrderPlaced, PaymentReceived | ConnectionTimeout, CpuHigh | OrderCreated, InventoryUpdated |
| 用途 | 驱动业务流程 | 监控、告警、运维 | 系统解耦、数据同步 |
| Schema要求 | 包含业务关键字段 | 包含技术指标 | 包含消费者所需全部信息 |
事件Schema设计原则
定义好事件类型之后,下一步就是设计事件的Schema。说白了,就是事件长什么样,里面包含哪些字段。
我见过最糟糕的Schema设计,就是一个事件里塞了几十个字段,大部分都没人用。还有更离谱的,事件里只放了一个ID,消费者拿到后还得去查数据库。这两种都是极端,都不对。
下面是我总结的几条原则:
原则一:最小化原则
事件里只放消费者需要的信息。别把整个数据库记录都塞进去。你想想看,如果订单事件里放了用户的所有信息、商品的所有信息、地址的所有信息,那这个事件会变得非常庞大,而且很多字段根本没人用。
我曾经踩过的坑:在一个电商项目里,我们把订单事件设计成了“全量快照”,每次订单状态变更都发一个完整的订单对象。结果事件体积巨大,消息队列经常积压。后来改成只发变更的字段,问题就解决了。
原则二:版本化原则
事件Schema一定会变。业务在变,需求在变,字段在变。所以,从一开始就要考虑版本化。
我建议在事件里加一个schemaVersion字段,标明当前事件的版本。消费者可以根据版本号决定怎么解析。这样,新旧版本可以共存,不会因为升级导致系统崩溃。
原则三:自描述原则
事件本身应该能说明“发生了什么”。消费者拿到事件后,不需要去查其他系统就能理解。
比如,一个“OrderShipped”事件,应该包含订单ID、发货时间、物流单号。消费者拿到这些信息,就知道该做什么了。
原则四:不可变原则
事件一旦发出,就不能修改。如果业务需要变更,那就发一个新的事件,而不是去修改旧事件。
这个原则很重要。你想想看,如果事件可以修改,那消费者怎么知道哪个版本是对的?所以,事件是追加的,不是覆盖的。
一个完整的Schema示例
说了这么多,不如看个例子。下面是一个“订单已创建”事件的Schema:
{
"eventType": "OrderPlaced",
"schemaVersion": "1.0",
"eventId": "evt_20240301_001",
"occurredAt": "2024-03-01T10:30:00Z",
"source": "order-service",
"data": {
"orderId": "ORD-20240301-001",
"customerId": "CUST-12345",
"totalAmount": 299.00,
"currency": "CNY",
"items": [
{
"productId": "PROD-001",
"quantity": 2,
"unitPrice": 149.50
}
],
"shippingAddress": {
"city": "北京",
"district": "朝阳区",
"detail": "建国路88号"
}
}
}
你看,这个事件包含了:
- 元数据:事件类型、版本、ID、时间、来源
- 业务数据:订单ID、客户ID、金额、商品、地址
消费者拿到这个事件,完全可以独立处理,不需要再去查订单系统。
事件分类与Schema的关系图
为了让你更直观地理解,我画了一张图:
避坑指南
最后,分享几个我踩过的坑:
- 别把事件当命令用:事件是“已经发生了”,命令是“请去做”。两者不要混。我曾经见过一个团队,事件里写着“请更新订单状态”,这其实是命令,不是事件。
- 别在事件里放敏感信息:比如密码、身份证号。事件可能会被多个系统消费,安全风险很大。
- 别让事件Schema太灵活:有些人喜欢用JSON的任意字段,结果消费者解析起来特别痛苦。还是老老实实定义好字段类型和约束。