☰
REA模型:资源-事件-参与者,让业务建模回归过程本质
2026/10/11 12:22:14 网站建设 项目流程

第一次听到 REA 这个缩写,是在一次进销存系统重构的评审会上。业务方坚持要把"客户订单"和"发货单"合并成一张"业务单据表",技术方则认为订单是承诺、发货是执行,必须分开。两边吵了两轮,谁都没法说服对方,最后有人翻出一篇二十多年前关于企业建模的旧文献,里面的 REA(Resource-Event-Agent,资源-事件-参与者)模型把这个问题回答得干干净净。作为一个常年和数据模型较劲的人,我后来把 REA 这套思路又系统地研究了一遍,发现它对做 ERP、进销存、业财一体化和数据中台的团队来说,价值被严重低估了。这篇文章不打算讲纯学术理论,而是从实际建模的角度,把 REA 拆开揉碎,讲清楚它解决什么问题、怎么落地、有什么坑,以及什么时候不该用它。适合正在设计业务系统的开发、产品经理和技术架构师参考。

REA 模型最早来自会计信息系统领域。上世纪八十年代初期,一位研究会计系统设计的学者发现,传统的借贷记账法虽然在财务核算上行之有效,但它把业务过程压扁成了"科目+金额"的抽象记录,导致信息系统很难回答"这笔销售是谁经手的、对应的发货实际发生了什么、为什么库存和财务对不上"这类过程性问题。他提出的 REA 模型,直接用资源、事件、参与者三类概念描述经营活动的全貌,后来被大量研究者和架构师当作业务建模的基础框架。到今天,很多讲数据中台、业务事件台账的资料,背后的思想源头都能追溯到 REA。

1. 那场把订单当账本的争论:REA 要解决的问题

1.1 订单到底是不是一张凭证

先把这个最常见的争论场景讲透。做进销存或者订单系统的团队,几乎都遇到过这样一个问题:客户下单以后,订单数据到底算"业务单据"还是算"记账凭证"?

如果按传统复式记账的思路来设计,订单往往会被处理成一张"待结算的凭证草稿",等到发货、开票、收款了再往凭证里补充分录。这么做在财务视角下没问题,但对业务系统来说隐患很大。因为订单在未发货之前,本质上是卖方对客户的一个承诺,它代表"我们答应在未来某个时间交付这些商品"。承诺和交付是两件事,把它们当成同一个对象来管理,就会出现一种尴尬:业务员想看"这个月接了多少订单",翻出来的却是已经发货甚至已经回款的记录;库房想看"还有多少货要发",又得在凭证中间做各种不算自然的过滤。

REA 在这一点上非常清晰:订单属于承诺事件,发货属于执行事件,两者在语义上必须分开建模。用生活化的比喻来说,承诺是一份"借条",执行是"还钱",借条和还钱是两回事,不能合并成同一条记录。这个区分是 REA 给我的第一个启发,也是我认为它比大多数自定义业务单据模型高明的地方。

1.2 传统建模为什么把过程搞丢了

再往深一层看,传统"凭证-分录-科目"这套叙事的核心目标,是保证财务结果借贷平衡,而不是还原业务过程。它天生倾向于把事件拍扁:一笔销售,在凭证里体现为"借应收账款,贷主营业务收入",至于货物是谁发的、客户是哪天签收的、这单的毛利贡献是多少,凭证系统一概不关心。

这种"只记录结果、不记录过程"的设计在信息化早期没有太大问题,因为那时候系统的边界就是财务部。但今天大多数业务系统要支持的是从前端获客、订单履约、库存调度到财会对账的全链路协同。如果底层的核心数据模型仍然是凭证化的,那所有过程性的问题——比如"这批货是哪位销售跟进签下来的""客户签收和财务入账之间差了多少天""库存账和实物账为什么对不上"——都要靠额外加字段、加中间表来补救,补来补去,业务人员和技术人员的理解就分裂了,这就是很多系统里"字段含义全靠猜"局面的根源。

1.3 换一个视角:从"记结果"到"记过程"

REA 提供的是另一种叙事:不先问"这笔业务该怎么记分录",而是先问"什么资源、通过什么事件、在什么参与者之间发生了流动"。资源是需要管理的有价值对象,事件是经营中真实发生的活动,参与者是活动的主体。三者加上关系,就构成了业务过程的事实底稿。

我整理了一个简短的对照表,每次团队里讨论建模口径时,我都会拿出来做参照:

对比维度传统"凭证-科目"视角REA 视角
核心记录对象凭证、分录、科目余额资源、事件、参与者
对订单的看法待核销的凭证草稿一个承诺事件
对发货的看法影响库存与成本的分录来源一个执行事件(资源流出)
对客户回款冲销应收的分录一个执行事件(资源流入)
核心问题每一笔凭证算得平吗这一串事件完整还原了业务吗

从这个对照可以看到,REA 没有否定财务核算的必要性,它只是把过程语义放回了核心位置。财务凭证可以当作 REA 过程之上的一个"结果视图"来生成,而不是唯一的事实来源。这是我后面落地表结构时的基本思路。

2. 资源、事件、参与者:三要素的语义边界

2.1 资源:不是主数据表里那一行

很多朋友听完"资源、事件、参与者"之后第一反应是:资源不就是物料表嘛,事件就是流水表嘛,参与者就是客户和员工表嘛。这个理解大方向对,但少了一层关键含义。

在 REA 里,资源必须是"能够在交换或转换中被估值、被让渡、被消耗的对象"。商品库存是资源,货币资金是资源,仓库里的一次维修服务能力也可以建模为资源,某位资深顾问的一小时顾问时间同样可以建模为资源。判断一个对象是不是资源,要看它是否参与了"一方给出、另一方接收"的完整业务动作。员工的个人信息表虽然也是主数据,但它在 REA 模型里通常是参与者的属性,而不是资源,因为员工本人不会作为商品被让渡。物料主数据之所以是资源,是因为它参与了一个完整的买卖交换。这个界限很多人容易画错。

我在实际建模时还有一个体会:资源的定义要和业务片段的边界对齐。比如在做供应链协同系统时,产能可以被定义为"可排产的工时资源";但在做销售结算系统时,工时资源又可能退回到参与者的能力属性。资源不是客观存在的某个表,而是建模者根据业务问题划出的一种视角。

2.2 事件:业务真正发生的那一下

三要素里,"事件"是最容易理解、也最容易被设计烂的部分。在 REA 框架里,事件描述的是经营过程中真实发生的活动,必须具备时间、涉及资源、涉及参与者等事实属性。它是一段业务过程的"动词",而不是"名词"。

关键要区分两类事件。一类是承诺事件,比如销售接单、采购下单,它表示"未来会发生什么";另一类是执行事件,比如发货、收货、收款、付款,它表示"现在实际发生了什么"。把两者混在一张表里,是建模阶段最常见的错误。我的习惯是在设计阶段就给每个事件表打上一个标签:是承诺还是执行,承诺事件再发展成执行事件时,要用外键引用对方的编号,而不是直接覆盖原记录。用大白话说:承诺事件是计划,执行事件是事实。计划可以变更,事实不能。

2.3 参与者:谁能做业务动作

参与者指拥有决策权、能够发起或接受业务活动的单位和个人。它可以是一个人、一个部门、一个组织,也可以是系统里的一个自动化角色,比如"自动派单服务"。REA 特别强调参与者在交换关系里通常是两两成对的:有卖方就有买方,有授予方就有接收方。

这个"成对出现"的特性对权限设计和责任追踪非常实用。订单表里记录销售员是谁,发货表里记录仓管员是谁,收款表里记录谁确认了这笔款项——这些参与者字段不是冗余,而是还原业务事实的必备上下文。很多管理核查类查询能够通过参与者连线直接回答"某个人主导了哪些业务事件",在设计上比散落各处的"operate_id"字段要系统得多。

2.4 三要素串起来的完整画面

用一个最小例子演示。一家公司把一批现货卖给客户:

  • 资源:商品(卖方库存里的存量),货款(买方账上的资金)
  • 事件:销售订单(承诺,未来交付),发货(执行,商品从卖方流向买方),收款(执行,资金从买方流向卖方)
  • 参与者:销售员(卖方内部发起人)、客户(买方)、仓管员(卖方执行人)

这个画面里,任何查询都可以落回三要素的组合:"某位客户在什么时候收到了我们发出的什么商品,我们为此收到了多少钱,经手人是谁。"这种表达能力,就是 REA 的核心价值。下面我会演示,这样一张画面如何一步步落成真实的数据表结构。

3. 复式记账够用,为什么还要 REA:从"照片"到"录像"

3.1 复式记账的底层逻辑

先承认一件事:复式记账在商业史上足够伟大。它用"有借必有贷"的平衡机制,保证每一笔业务都能从两个侧面被捕捉,从而完成完整的价值核算。今天全球绝大多数企业账目,底层都是这套机制。

但复式记账有个本质特征:它是一张快照。一笔交易只要被记成借贷分录,业务的时序、参与方、资源流动路径就都被压缩成了两个数字。系统能回答"这笔业务金额多少",却很难回答"这笔业务是怎么一步步发生的"。就像一个摄像机只保留最后一帧,所有的过程都被丢掉了。在只需要算账的年代,这个压缩是可接受的;但在需要跨部门协同、追踪履约、分析业务贡献的今天,过程本身就是最重要的数据资产。

3.2 经济交换与经济转换

REA 模型用两种基本关系描述资源的状态变化。第一种是经济交换,两个执行事件结对出现,一方的资源流出去,另一方的资源流进来,买卖支付都属于交换。第二种是经济转换,某种资源被消耗、加工、重组,变成了另一种资源,生产组装、服务执行都属于转换,它描述的是企业内部的价值增值过程。

理解这两种关系特别重要,因为很多业务系统的核心逻辑都可以归结为"一系列交换和转换的组合"。库存模块做的是转换(原料变成品),销售模块做的是交换(商品换资金),供应链协同则是交换和转换的嵌套。我见过不少数据团队在设计指标口径时对"收入""成本""毛利"吵得不可开交,其实往底层一看,都是在交换和转换的边界上没对齐:某一步到底是消耗了资源还是让渡了资源,答案不同,指标当然不同。

3.3 三种核心关系决定表结构

在 REA 里,三要素之间主要通过三种关系连接:

  • 资源与事件之间是存量-流量关系:一个事件消耗、产出或接收了多少资源。
  • 事件与事件之间是交换/转换关系:哪些事件配对构成完整的业务动作。
  • 事件与参与者之间是权责关系:谁在这个事件里承担了什么角色。

这三种关系不仅仅是 ER 图上的连线,它们直接决定了表结构的设计:资源表和事件表之间需要有流量明细表;事件表之间需要有配对字段或配对表;事件表里必须保留参与者维度。很多系统表结构混乱,拆开来看其实是"该拆的流量明细没拆、该连的事件配对没连、该留的参与者维度没留"。

3.4 事件是天然的"事实表"

如果从数据分析的视角看 REA,你会发现事件表几乎就是数仓模型里最理想的事实表:它有时间、有度量、有关联的资源与参与者,天然支持多维分析。对问题"这个季度哪个客户的毛利贡献最高",传统做法要从凭证数据反推业务,REA 模型则是直接从事件事实表按参与者维度聚合,查询路径短,语义也更不容易产生歧义。

这解释了为什么很多讲业财一体化、数据中台的文章最终都会绕回 REA:因为它为"业务事实"给出了一套稳定的、不随组织架构变化的底层结构。组织会变、流程会改,但只要业务本质还是"资源通过事件在参与者之间流动",REA 就依然有效。我在第五章会专门讲这种稳定性带来的落地优势。

4. 从订单到回款:一个销售业务片段的完整建模

4.1 圈定业务片段

建模之前先圈定单元。我习惯把"一个可独立闭环的业务动作"作为最小建模单元,而不是把整个 ERP 一次性画完。下面以一个标准销售业务片段为例:客户下订单,仓库发货,客户付款。公司角色是卖方。这个片段的核心问题是:如何让系统准确回答"我们答应卖什么、实际发了什么、实际收了多少钱"。

4.2 实例化三要素

把三要素落到这个业务片段里:

要素实例说明
资源SKU 商品卖方的库存资源,发货时减少
资源货币资金买方支付的货款,到账后增加
事件销售订单承诺事件:计划交付的标的
事件发货单执行事件:商品实际流出
事件收款单执行事件:资金实际流入
参与者客户买方,接收商品、支付资金
参与者销售员卖方内部发起订单的经办人
参与者仓管员卖方发货执行人

注意这里没有把"应收账款"直接建模成资源。应收/应付是后续核销阶段的会计视角,REA 更倾向于先把"发货"和"收款"两个实际事件记清楚,再在账务层推导应收关系。这个取舍在落地时非常重要,后面专门的落坑章节里我会展开说。

4.3 连接关系的推演过程

现在开始画关系,这个环节是建模的核心,不能用外键名称替代真实的业务语义:

  1. 销售订单是一个承诺事件,它与未来的发货事件和收款事件构成"承诺-执行"关联。订单记录的是计划,发货和收款记录的是实际。
  2. 发货事件消耗了"商品"资源,收款事件增加了"货币资金"资源。资源变化要落到具体金额和数量。
  3. 发货事件与收款事件构成一个经济交换的配对。一笔发货对应一笔(或多笔)收款,这就是配对关系。
  4. 参与者连线:销售员发起销售订单,仓管员执行发货,客户接收商品并且支付资金。客户在"发货-收款"交换里同时是接收方和支付方。

推演时我有个习惯:把每一条关系都用一句"人话"声明出来。比如"发货让库存里的商品减少 N 件,同时让客户获得这 N 件商品的处置权"。如果一句话说不清楚,说明关系还没想透,先不要建表。

4.4 落成最小表结构

基于上面的推演,可以设计一组最小可用的表。为便于演示,我采用简化字段:

-- 事件表:销售订单(承诺) CREATE TABLE sales_order ( order_id BIGINT PRIMARY KEY, order_no VARCHAR(32) UNIQUE, customer_id BIGINT, -- 参与者:客户 seller_id BIGINT, -- 参与者:销售员 status VARCHAR(16), -- 草稿/已确认/已完成 created_at TIMESTAMP ); -- 事件表:发货(执行) CREATE TABLE shipment ( shipment_id BIGINT PRIMARY KEY, shipment_no VARCHAR(32) UNIQUE, order_id BIGINT, -- 关联承诺事件 customer_id BIGINT, warehouse_keeper_id BIGINT, shipped_at TIMESTAMP ); -- 事件表:收款(执行) CREATE TABLE payment ( payment_id BIGINT PRIMARY KEY, payment_no VARCHAR(32) UNIQUE, customer_id BIGINT, amount DECIMAL(18, 2), received_at TIMESTAMP ); -- 事件-资源:发货的资源流出明细 CREATE TABLE shipment_resource_flow ( flow_id BIGINT PRIMARY KEY, shipment_id BIGINT, resource_type VARCHAR(32), -- 商品/服务等 resource_id BIGINT, quantity DECIMAL(18, 3), unit_price DECIMAL(18, 2) ); -- 事件-资源:收款的资源流入明细 CREATE TABLE payment_resource_flow ( flow_id BIGINT PRIMARY KEY, payment_id BIGINT, resource_type VARCHAR(32), resource_id BIGINT, amount DECIMAL(18, 2) ); -- 事件配对:发货与收款的交换关系 CREATE TABLE exchange_pairs ( pair_id BIGINT PRIMARY KEY, shipment_id BIGINT, payment_id BIGINT, settlement_note VARCHAR(255) );

这个结构里的关键点有三个。第一,承诺事件订单与执行事件发货分离;第二,事件与资源的关联通过资源流量明细表完成,而不是在主单里塞一个"库存数量"字段;第三,发货和收款通过交换配对表建立联系,将来部分收款、分批回款都可以拆行表示。

4.5 与传统"凭证+分录"方案的对比

同样一个业务片段,传统方案大致是:一张业务单据主表,加一张分录明细表,字段包含科目、借方金额、贷方金额。订单、发货、收款都被压成分录,靠"单据类型"字段区分。跑报表时常见两种尴尬:一是想看业务履约进度,必须在分录表里反复 JOIN 业务单据号,语义绕;二是库存明细和财务往来被绑定在一起,库存盘点和财务对账任何一边动数据,另一边跟着抖。

REA 方案的差别在于:事件表就是事实,资源流水表就是存量变化,交换配对表就是业务归属。查询"某客户的毛利贡献",可以从发货资源流水按客户聚合,再对比对应的资金流入,整个过程不用经过任何科目编码,业务人员也能看懂查询条件。财务核算视图则可以在 REA 之上单独生成,作为一层结果视图,而不是反过来把业务过程压扁成凭证。

5. 落地过程中最容易翻车的四个设计细节

5.1 订单状态机放哪:承诺事件要不要留修订痕迹

承诺事件最大的特点是可以变更。客户可能改数量、改交期,甚至取消订单。如果把订单状态存在同一张表里,直接 UPDATE 状态字段,历史承诺就看不到了,REA 强调的"过程可还原"就破功了。我的建议是把承诺状态变化单独建成一个状态历史表或追加事件日志,主记录保留最新状态,历史记录用于追溯。这个设计带来的额外收益是:业务上需要回答"这单被改过几次、每次改了什么"时,不再需要从审计日志里人肉翻。

5.2 退货与退款:逆事件怎么处理

逆向流程是 REA 落地时最容易被绕晕的地方。注意不要用负数把退货和发货混在同一张表里。REA 的标准做法是把退货建模为一个独立的执行事件:退货是商品的反向资源流动,退款是资金的反向流动,它们再组成一个新的交换配对。这样"退货原因""责任归属""复检结果"这些品控维度都能挂在独立事件上,不会污染正向发货的统计。用生活类比:一个人在会议室 A 作了一次演讲,又在会议室 B 作了一次相同内容的演讲,它们依然是两场会议,而不是同一场会议打了负号。

5.3 多组织、多币种带来的粒度问题

REA 的三要素本身不限定组织边界,但落地时要在资源与事件上区分"记账主体"。多公司场景下,同一批货物从母公司仓库调拨到子公司,在 REA 里不应该简单记成一条库存转移,而应该建模为"子事件+资源流动明细"的组合,这样才能支持每个组织的独立核算,又不破坏总体资源守恒。多币种也是同样逻辑:资金资源要保留币种维度,交换配对表里要记录结算汇率,不要在金额字段里偷偷做汇率折算。这类问题早期不处理,等报表要对齐的时候就非常痛苦。

5.4 事件表不等于领域事件,别直接照搬事件溯源

近几年事件驱动架构流行,很多团队一看到 REA 里有"事件",就以为可以把事件表当消息总线的领域事件用。两者确实有相似之处,但定位不同。REA 的事件表是业务事实的持久化记录,它关心的是"某个业务动作发生后的确定性状态";消息总线的领域事件关心的是"系统之间如何异步协调"。一个 REA 事件可能会触发多条消息,多条消息也可能最终汇成一个 REA 事件。落地时我的做法是:事件表作为最终一致性的落库点,消息队列只承担通知职责,不要让消息替代事件表成为唯一事实源。

6. 什么时候该上 REA,什么时候别硬套

6.1 越复杂越划算:业务追溯、业财一体化、数据中台

如果你在做进销存、供应链协同、业财一体化,或者要建一个面向全链路的数据中台,REA 几乎是"教科书级"的底层框架。这类系统的共同特点是:多个部门围绕同一批资源协作,必须回答"谁在什么时间对什么资源做了什么"。REA 正好把这个问题的答案结构化地记录下来了。我见过不少团队在数据中台里花大量时间定义"事实表"和"维度表",其实从 REA 出发,事实表天然就是事件表,维度表天然就是资源和参与者,口径冲突能少一半。

6.2 小而准的记账工具就不需要

也不是所有系统都应该上 REA。如果一个工具的定位就是"把账记平、把凭证打印出来",用户不关心过程追溯,那 REA 模型带来的复杂度的确不值得。这种情况用传统的凭证-分录-科目模型反而干净利落。REA 不是要取代所有会计系统,它是在"你确实需要过程数据"的前提下才真正发挥作用的框架。怕的是团队在不必要的地方为了"模型先进"硬套,结果把简单的需求全部做重。

6.3 判断标准:你更看重结果还是过程

我总结了一个一句话判断标准:系统最核心的价值是"算得平",还是"还原得清"?前者适合传统账务模型,后者适合 REA。如果两者都要,也没有问题——先用 REA 把过程和事实记录完整,再在其上生成核算视图。很多做业财一体化的团队最终都会落在"REA 事实层+账务结果层"的双层架构上,这是我目前见过的最不容易跑偏的组合。

7. 实战中很管用的三条经验

三件事是我自己用了 REA 模型以后觉得最值得分享的。

第一,先从白纸上的关系图开始,不要在第一次就急着定义表的字段。用"人话"把资源、事件、参与者之间的每一条连线说清楚,再把连线翻译成外键和流量明细。字段永远可以被后来者补充,但关系画错了,返工成本是最高的。

第二,把一个"完整业务片段"作为建模单元,而不是从整个系统的 ER 图开始。每个片段做完,跑通一轮真实业务数据,确认查询口径对上了,再接着扩展下一个片段。这个过程很像搭积木,片段之间的接口就是共用的资源和参与者。

第三,命名尽量用业务语言。事件表叫"发货""收货""收款"就比叫"biz_event_type_03"好理解得多。REA 最怕过度抽象,一旦事件表和资源流量表被命名成泛化的流水表,业务人员和技术人员很快就会出现理解断层。保持命名贴近业务,数据模型才能真的变成团队的通用语言。

我还想说一个更实际的体会:凡是做过系统重构的人都知道,最难的不是重写代码,而是把散落在老系统里的"隐性业务规则"重新找出来。REA 模型此时是一个很好的梳理工具。你只需要问三个问题:这里有什么资源在流动?动作是什么事件?谁在参与?很多纠缠不清的业务逻辑会迅速显形。这也算是我坚持在团队里传播这套建模方法最重要的原因。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询