事件定义与分类:业务事件、系统事件、集成事件

好,咱们进入第二章。上一章聊了事件治理为什么重要,这一章得落地了——先搞清楚到底什么是事件,以及怎么给它分门别类。

说实话,我见过不少团队,一上来就定义事件,结果业务说这是事件,技术说那不是,吵了半天。为什么?因为大家对“事件”的理解不在一个频道上。所以,咱们先把概念对齐。

事件的本质:发生了值得关注的事

事件,说白了就是“某件事已经发生了”。它是个事实,不可更改。你想想看,在现实世界里,“订单已创建”、“支付已成功”、“库存已扣减”,这些都是已经发生的事实。在系统里,我们就把这些事实记录下来,广播出去,让需要知道的人知道。

我个人习惯把事件分成三大类:业务事件、系统事件和集成事件。这三者各有各的用途,也各有各的坑。

业务事件:业务人员看得懂的事

业务事件,就是业务人员能理解的事件。它描述的是业务领域里发生的、有业务含义的事情。

举个例子,“用户下单”就是一个业务事件。业务人员知道这意味着什么——用户选好了商品,提交了订单,接下来要处理支付、发货等等。业务事件的名字通常用过去分词,比如“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设计关系 事件源 业务事件 系统事件 集成事件 事件Schema设计原则 最小化原则 版本化原则 自描述原则 不可变原则 事件分类决定了Schema的侧重点,但设计原则是通用的 业务事件重业务语义,系统事件重技术指标,集成事件重契约完整性

避坑指南

最后,分享几个我踩过的坑:

  • 别把事件当命令用:事件是“已经发生了”,命令是“请去做”。两者不要混。我曾经见过一个团队,事件里写着“请更新订单状态”,这其实是命令,不是事件。
  • 别在事件里放敏感信息:比如密码、身份证号。事件可能会被多个系统消费,安全风险很大。
  • 别让事件Schema太灵活:有些人喜欢用JSON的任意字段,结果消费者解析起来特别痛苦。还是老老实实定义好字段类型和约束。