☰
REA业务建模实战:用资源-事件-主体梳理订单、库存与对账难题
2026/10/14 12:58:59 网站建设 项目流程

常年跟业务系统打交道的人,多少都经历过这种场面:订单表里的“状态”字段从1改到8,每个状态还配了一堆说明文档;财务说库存金额对不上,仓库说账实不符;产品经理说“这个需求加个字段就行”,结果一上线数据就乱了。我在某个后台系统改造项目中,靠一套不算新的业务建模思路,把这些问题压住了一大半。这套思路就是标题里的“rea”——我在这里说的是 Resource-Event-Agent,资源-事件-主体,一个从业务语义出发、再落成数据结构的方法。这篇文章不打算复述理论教材,而是用一个个真实业务场景,讲清楚怎么用 REA 拆解业务流程、画出数据模型,以及我在实战里踩过的坑和排查经验。适合正在做业务分析、后台产品设计、财务系统建模或数据库设计的同学参考。

1. REA到底在解决什么问题

1.1 传统“建表思维”为什么容易翻车

大多数团队拿到需求后的第一反应是建表:客户表、订单表、库存表、流水表。订单表尤其容易爆炸,因为业务上“下单”这个动作,会牵出预占库存、锁定价格、分批发货、部分退款、售后换货、跨期结算等各种情况。于是订单表从一张干净的主表,慢慢变成堆满冗余字段的“万金油表”,状态枚举越加越多,查询越来越慢,对账逻辑越来越绕。

问题不在“订单”这个概念本身,而在于一开始建模时,只盯着数据结构长什么样,没回答业务到底发生了什么。订单表里存的不只是“订单”,它还混杂着承诺、交付、开票、收付款等多类事件。把这些不同性质的信息压在一张表里,返工几乎必然。

1.2 REA的出发点:先回答“发生了什么”

REA 的思路恰好反过来。它要求建模者先把业务中的活动拆成三类基础要素:资源(Resource)、事件(Event)、主体(Agent),再谈论表结构。所以画 REA 模型更像是在还原业务事实,而不是在设计一张张关系表。

1999 年我在读项目管理相关资料时第一次接触 REA 的原始框架,它的根子是会计信息系统理论,解决的是传统会计科目表在信息系统时代表达力不足的问题。传统借贷记账法把业务抽象成“借什么、贷什么”,这对财务报表够了,可对于数据系统远远不够。系统需要知道“谁在什么时候做了什么,导致哪些资源发生了增减”。REA 恰恰提供了这套表达体系。

1.3 什么场景适合用 REA

REA 不是万能的。轻量级管理工具,比如一个只有几十人用的刷题打卡小程序,直接建表更高效,强行套 REA 就是自找麻烦。但凡是多主体、多资源、多事件流转的业务,比如进销存、采购付款、订单履约、售后维修、会员积分体系,REA 都能帮你把混乱的需求理成一条清晰的线索。

还有一个典型场景是财务业务一体化改造。团队里财务和开发经常因为“这个字段到底记什么”吵起来。财务关心借贷,开发关心状态,REA 把两边语言对齐:财务上的“收入确认”对应业务上的“交付事件”,“应收账款增加”对应“已交付未收款”的事实。这个对应关系一旦理清,后续对账和审计都轻松很多。

2. 核心概念拆解:资源、事件、主体到底怎么理解

2.1 资源:必须能被识别和计量

REA 里的资源,不是指所有客观存在的东西,而是指“有价值、可被交换或使用、能被系统识别和计量”的对象。现金、库存商品、厂房设备、专利许可、劳务工时、会员积分,这些都能算资源。反过来,“客户关系”“品牌声誉”这种难以稳定计量的东西,如果系统里没有确定的口径,我不建议直接建模成资源。

实操中要注意,资源通常不只是一个“数值”,它往往自带批次、有效期、规格等维度。比如同样是库存商品,一批货可能是 2024 年 3 月生产,另一批是 2024 年 9 月生产,保质期不同,成本也可能不同。REA 本身不规定你把这些维度放哪,但它要求模型保证“每一条资源变动都能追溯到具体对象”。我的习惯是给资源加一个全局编号,类似“资源实例 ID”,再在实例下挂业务属性。

2.2 事件:唯一不需要怀疑的主角

如果说 REA 和传统建模有什么最关键的区别,那就是事件在 REA 里是绝对的核心。事件是“对资源产生影响的一次发生”,它必须具备时间、数量、相关资源、相关主体等基本信息。库存入库、销售出库、支付货款、确认收货,都是事件。

事件又分成两类:交换类事件和转换类事件。交换类事件对应“经济交换”,比如拿现金换商品,系统里会同时出现资源的减少和增加;转换类事件对应“生产加工”或“消耗使用”,比如原材料经过加工变成成品,它不一定立刻涉及外部主体,但内部一定发生了资源形态的变化。

我在建模时会给自己定一条规矩:凡是系统中需要追溯“这笔数据是怎么来的”时,一定先找事件,而不是先找状态。状态只是结果的快照,事件才是原因。比如“库存数量为 10”是一个状态,我们更关心的是“哪些入库事件让库存变成 10,哪些出库事件又让它减少”。

2.3 主体:不要忽略了角色和时间维度

主体就是事件的参与方,可以是内部员工、部门,也可以是外部客户、供应商,甚至可以是系统定时任务。一个比较常见的坑:把“用户”单一地对应成“主体”。真实业务里,同一个自然人可能既是普通顾客,又是代理商,还是公司内部采购员。如果主体表里只放一个人名,后面算返佣、算员工折扣、区分客群时,只能靠加角色字段硬撑。

我的做法是主体和角色分开。主体表只管“这是谁”,比如姓名、统一信用代码或身份证号维度的唯一标识;角色或者参与关系单独建模,比如“某主体以客户身份参与销售出库事件”“以供应商身份参与采购入库事件”。这样既不会复制主体数据,也能支持同一主体多个身份的情况。

2.4 三个要素如何连成一张网

要素之间靠关系连接。资源与事件之间是“流入流出”关系:事件发生导致某种资源增加或减少。事件与主体之间是“参与”关系:某个主体发起了事件,某个主体接收了事件。事件与事件之间还可能有“对应”关系:比如销售出库事件和应收账款增加事件,在逻辑上是成对出现的;采购付款事件和采购入库事件也会互相勾稽。

用一句话概括:REA 模型是“资源经事件流向主体,事件之间又通过经济对应关系互相绑定”的业务网络。这跟复式记账的“有借必有贷”很像,但 REA 走得更远,因为它保留了业务事实,而不是只留下借贷数字。

3. 实操:把一套销售加采购流程改造成 REA 模型

3.1 场景设定:某贸易公司的核心业务

我拿“某贸易公司”作为模拟项目 X 来讲。这家公司从供应商采购商品,入库到自家仓库,再销售给客户,同时做库存盘点和损耗处理。业务看着简单,但牵扯到的资源有货币资金、库存商品、应收账款、应付账款;主体有供应商、客户、采购员、销售员、仓库管理员;事件更是五花八门。

第一步不是画表,而是把所有业务活动列出来,然后逐一判断:它是不是一个 REA 事件?这里有张我常用的分类表,给你参考。

业务活动是否事件涉及资源涉及主体事件类型
采购下单是无直接资源变动,形成采购承诺采购员、供应商承诺事件
供应商发货是在途库存增加供应商、仓库管理员交换类事件
采购入库是库存商品增加供应商、仓库管理员交换类事件
向供应商付款是货币资金减少,应付账款减少采购员、供应商、财务交换类事件
销售下单是形成销售承诺销售员、客户承诺事件
销售出库是库存商品减少,应收账款增加销售员、客户、仓库管理员交换类事件
收到客户付款是货币资金增加,应收账款减少客户、财务交换类事件
盘点调账是库存商品增减仓库管理员、盘点员转换类事件
查看库存报表否不改变资源无非事件

判断标准只有一个:这个活动是否改变资源存量或资源流向。如果既不改变存量也不产生新的权利义务关系,就不应该成为事件。

3.2 从事件反推资源与主体的明细映射

事件确定后,再给每个事件标注资源流向和参与主体。

以“销售出库”为例。这个事件至少涉及两类资源:库存商品减少(从仓库流向客户),应收账款增加(公司对客户的收款权利)。它至少涉及三类主体:销售员(发起事件)、客户(接收商品)、仓库管理员(执行发货)。如果公司还要记录物流承运方,承运方也应当作为主体挂上来。

再比如“收到客户付款”。这个事件涉及货币资金增加,应收账款减少;事件由客户发起,公司财务确认,银行账户可以理解为一个内部主体,也可以作为资源实例处理。我见过不少团队在这纠结银行账户到底是资源还是主体。我的建议是:账户本身作为“货币资金资源”的实例,银行作为外部主体,这样后续做资金流水和对账单都方便。

这一步做完后,你已经拥有了一张“事件—资源—主体”矩阵。这个矩阵特别适合跟产品经理、财务对需求,因为每个事件都对应业务上的一件具体事,大家不会为了表字段吵架。

3.3 落地成数据库实体与关键字段

矩阵确认后,才进入数据库设计。核心表可以分成四类:资源表、事件表、主体表、关联表。下面是一套简化但可运行的 SQL 示意,覆盖核心逻辑。

-- 资源表:一种资源可以有一个实例表,这里先简化为单一表 CREATE TABLE resource ( id BIGINT PRIMARY KEY AUTO_INCREMENT, resource_code VARCHAR(64) NOT NULL UNIQUE, resource_name VARCHAR(128) NOT NULL, unit VARCHAR(32) NOT NULL, category VARCHAR(64), current_quantity DECIMAL(18,4) NOT NULL DEFAULT 0, valid_from DATETIME, valid_to DATETIME, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 事件表:记录每一次业务发生 CREATE TABLE event ( id BIGINT PRIMARY KEY AUTO_INCREMENT, event_no VARCHAR(64) NOT NULL UNIQUE, event_type VARCHAR(64) NOT NULL, event_time DATETIME NOT NULL, location VARCHAR(128), remark VARCHAR(512), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 主体表 CREATE TABLE agent ( id BIGINT PRIMARY KEY AUTO_INCREMENT, agent_code VARCHAR(64) NOT NULL UNIQUE, agent_name VARCHAR(128) NOT NULL, agent_type VARCHAR(32) NOT NULL, tax_no VARCHAR(64), valid_from DATETIME, valid_to DATETIME, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 资源与事件的流入流出关系 CREATE TABLE event_resource ( id BIGINT PRIMARY KEY AUTO_INCREMENT, event_id BIGINT NOT NULL, resource_id BIGINT NOT NULL, flow_direction VARCHAR(8) NOT NULL, -- IN 流入资源,OUT 流出资源 quantity DECIMAL(18,4) NOT NULL, amount DECIMAL(18,4), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 事件与主体的参与关系 CREATE TABLE event_agent ( id BIGINT PRIMARY KEY AUTO_INCREMENT, event_id BIGINT NOT NULL, agent_id BIGINT NOT NULL, role VARCHAR(64) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 事件与事件的对应关系,用于表达经济交换 CREATE TABLE event_correspondence ( id BIGINT PRIMARY KEY AUTO_INCREMENT, increment_event_id BIGINT NOT NULL, decrement_event_id BIGINT NOT NULL, correspondence_type VARCHAR(32) NOT NULL, amount DECIMAL(18,4), created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP );

这套结构最大的好处是,业务加需求时不需要给一张主表疯狂加列。比如“客户下了订单但还没付款”,系统里自然存在一条销售承诺或销售出库事件,没有对应的付款事件,这就是应收账款;等收款事件出现,再用 event_correspondence 把两者挂上,账目自然平了。

3.4 一个具体实例:卖出一批货的全过程

我们走一遍“卖出 10 件商品给客户”的完整数据流程。

第一个事件是“销售出库”。在 event 表插入一条记录,event_type 为“SALES_DELIVERY”,event_time 为发货时间。同时在 event_resource 表插入两条记录:一条是库存商品资源 ID 的 OUT 方向,数量为 10;另一条是应收账款资源 ID 的 IN 方向,金额为 1000 元。在 event_agent 表插入销售员、客户、仓库管理员三条参与记录。

第二个事件是“收到货款”。再插入一条事件,event_type 为“CUSTOMER_PAYMENT”,同时在 event_resource 表插入一条记录:货币资金资源 ID 的 IN 方向,数量为 1000 元;应收账款资源 ID 的 OUT 方向,数量为 1000 元。最后用 event_correspondence 把两个事件关联起来,correspondence_type 为“PAYMENT_FOR_DELIVERY”。

这样处理之后,查询“这笔订单是否回款”就变得非常直观:找到销售出库事件,看有没有对应关系的收款事件。如果状态不对,一定是事件没录全,要么漏建了收款事件,要么对应关系没建立。

有的同学会问:那现有的“订单表”怎么办?我不建议立刻删掉订单表。订单表可以作为“业务单据入口”或“聚合展示层”保留,但它背后的数据来源,应该逐步替换成事件表和关联表。换句话说,让订单表从“多方事实的存储容器”变成“多个事件结果的实时视图”。

4. 常见问题与排查技巧实录

4.1 把事件当状态,把状态当事件

这是新手最容易踩的坑。比如有人把“出库完成”设计成一个字段,每次流程走完就更新字段。表面上看也能运作,可一旦需要追溯“上次出库具体是哪些批次、经手人是谁、当时库存是多少”,字段式方案就完全不够用。

我的排查方法是:拿到一个需求,先问三个问题。第一,它是一次发生的事实,还是一个截止当前的结果?发生过的事实就是事件,结果就是状态。第二,这个字段会不会在同一对象身上反复变化?如果会,很可能是事件被压缩成字段了。第三,如果业务要回溯 90 天内的每一次变化,这个字段方案能回答吗?

如果发现不对,就把相应的“状态位”拆回事件表。订单状态里的“已发货”,就可以拆成“发货事件已存在”;“已完成”就可以拆成“发货事件和收款事件均已存在”。状态依旧可以保留为冗余字段,方便快速查询,但备份真相必须来自事件。

4.2 资源增减方向搞反,对账怎么都不平

我在一次模拟项目中,把“采购付款”的 resource_out 方向写错了,导致应付账款越对越多。排查下来发现是建模时误把“付款”理解成“供应商资源流入”,而不是“货币资金流出、负债减少”。

这里有一个消除歧义的技巧:每建立一个事件资源关系,不要只写“数量”和“金额”,一定要在业务层面说清“谁的什么资源发生了什么变化”。比如采购付款事件,站在公司角度是“银行存款减少”和“应付账款减少”,站在供应商角度是“货币资金增加”和“应收账款减少”。REA 模型允许从不同主体视角建模,但一套系统里必须绑定同一个视角,推荐统一站在公司或平台运营方视角。

4.3 过度建模,把查看、操作记录都当事件

刚上手 REA 时,很容易走向另一个极端:所有动作都建事件,连“用户点击了一下查询”也建事件。这是过度建模。真正的业务事件应当具备经济后果,要么改变了资源,要么形成了权利义务。纯粹的操作痕迹,可以由日志系统负责,不需要混入核心业务模型。

如果团队判断一个动作是否要建模成事件,我提供一个简单规则:这个动作如果相隔一年后再看,还需要知道它的具体内容和影响吗?如果不需要,日志就够了;如果需要,进入 REA。比如“仓库管理员今天打扫了仓库”不需要建模,“仓库管理员盘点后确认库存少了 3 件”必须建模。

4.4 主体身份时变:员工离职、客户改名的处理

主体表如果不考虑时间维度,早晚出问题。一个客户 2023 年叫“张三贸易”,2025 年更名成“张三供应链”,如果主体表直接更新名称,历史销售事件里“卖给谁了”就跟着被改掉,审计时说不清。

解决办法很简单:主体表加 valid_from 和 valid_to,或者把主体与身份信息拆开。历史事件关联主体 ID,不做覆盖式更新。名称变更时新建一条主体记录,并在标识字段上维护同一个全局唯一编码。销售出库事件始终指向那个唯一编码,展示层可选显示当前名称或历史名称。这样既保证了身份的连续性,也保留了历史事实。

4.5 排查脚本和快速自查清单

针对几个高频问题,我准备过一个自查 SQL 思路,供你参考。第一个常见问题是“资源存量与事件流水不一致”。排查时先汇总所有 event_resource 里某个资源 ID 的 IN 和 OUT 数量,再对比 resource 表的 current_quantity,就能定位是事件录入缺失还是资源初始值不对。第二个问题比较隐蔽:“某些事件找不到对应关联”。用一段简单查询,找出所有 increment_event 在 event_correspondence 里没有配对的记录。特别是应收账款和库存类事件,业务上应该成对出现。

结合这些排查经验,我给自己总结了一份每次建模都要过的清单:

提示:第一,每个资源变动是否都能向上追溯到某个事件?第二,每个事件是否都记录了至少一个参与主体?第三,交换类事件是否建立了事件间对应关系?第四,是否存在被当成字段的事件?第五,主体名变更是否会影响历史记录?

这五条里,任何一条答不上来,模型都要先补全再开发,不要指望后期靠补数据兜底。

5. 我的习惯:先画 REA 草图,再谈其他

最后分享一个我个人的操作习惯。接到一个新业务模块时,我基本不动手建表,而是找一块白板,把资源、事件、主体用三列写下来。先列出所有看得到的“东西”,再列出所有线上线下“动作”,最后在旁边标注“谁发起、谁接收”。把这些列齐后,再把动作按“换东西”和“变东西”分组。这时候数据需求已经自己浮出来了。

有一次某团队老板级别的人看我在白板上画这些,说“这不就是流程图吗”。我觉得很像,但不完全是。流程图表达的是操作顺序,REA 表达的是业务事实与事实之间的勾稽关系。它更像物理学家画受力分析图,资源是对象,事件是力,主体是施力点。受力图画清楚了,运动轨迹自然就判断出来了。

这套方法在一个后台系统改造中带来的变化很明显,最大的变化是“返工”少了。以前改需求要动四五张表,现在大多只是新增一种事件类型,或调整事件与资源的关联。产品经理和开发也找到了共同语言,不再各说各话。后续我还想把 REA 跟微服务划分、领域事件设计结合起来聊一聊,这又是一个能展开的话题。希望这篇实战向的梳理,对正在跟业务模型缠斗的你有些许帮助。

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

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

立即咨询