REA模型实战:用资源-事件-代理重构业务语义与对账逻辑

发布时间:2026/10/21 4:57:11
REA模型实战:用资源-事件-代理重构业务语义与对账逻辑
先说一个我印象特别深的场景去年帮朋友复盘一个进销存系统对账对了两天最后的根因特别尴尬——销售出库单和收款单在数据库里各自为政靠状态位互相拉扯。查“到底谁欠钱”的时候得写一堆 join 加条件判断任何一个人改错了状态位整个账面就对不上。这种问题我见过太多次它往往不是某个字段设计得不合理而是整个团队从一开始就没有把业务语义主线立起来。后来我重新研究 REAResources-Events-Agents资源-事件-代理模型发现它正好解决的就是这件事。REA 最早是被提出用于会计信息系统设计的业务建模方法圈子并不大但确实有用。它逼着你在建表和写接口之前把所有业务动作拆成“谁、在什么时候、对公司什么资源做了什么”再把这些动作串成因果链。对做订单、库存、账务、风控这类系统的人来说理解 REA 以后再看业务眼光会完全不一样。这篇就用一个采购闭环的完整案例讲透它顺带复盘我落地过程中踩过的坑。1. REA到底解决什么问题业务建模里“看不见的语义”1.1 从一次对账事故说起那个进销存系统的设计其实非常有代表性销售表、出库表、收款表、退款表各有一套表之间用订单号弱关联然后用一堆“状态”字段表达进度。表面上看 ER 是完整的客户、订单、流水都有但真要回答“哪些销售还没有收齐钱”就得把所有表 join 一遍再用状态位做排除法。更麻烦的是出库和收款往往不是同一套流程中间还隔着一道“应收款”的概念而这个概念在表结构里根本不存在只存在于开发人员的注释里。这就暴露了传统 ER 建模的一个盲区ER 能描述“有哪些对象、对象之间是什么关系”但它不负责告诉你“哪些事件必须成对出现、资源从哪一方流动到哪一方”。而业务真正的规则恰恰藏在这里——一次销售必然伴随着资源的流出一次收款必然伴随着补偿性的资源流入这两个事实之间有一个不可分割的经济互换关系。我用 REA 之后最大的变化是建模的第一步不再是画表而是问自己这个业务里有哪些“经济事实”每个事实是谁发起的、涉及什么资源、在什么时间点发生、跟哪个事实是配对的把这些排清楚了表结构反而是水到渠成的事。1.2 REA三要素资源、事件、代理分别指什么REA 把所有业务浓缩成三类东西。资源Resources公司拥有或控制、具有经济价值的东西。不只是库存商品和现金也包括应收账款、预付款、设备产能、在制品。判断依据很简单——它能不能被交换、能不能被损耗、能不能形成债权债务。事件Events改变资源状态的经济活动。典型的有采购收货、销售出库、收付款、生产领料、完工入库。注意这里的事件不是随便一条系统日志必须是有经济语义的原子动作。代理Agents参与事件的个人或组织。内部代理是员工、部门外部代理是客户、供应商、物流商。强调一点REA 通常要求一个完整的经济事件同时关联至少一个内部代理和至少一个外部代理因为它要回答“谁对谁做了什么”。三个要素组合起来就能描述任何业务动作。再加上三条核心规则每个事件至少关联一个资源和一个代理每个减量事件必然与一个或多个增量事件配对形成经济互换或转化承诺/契约与实际执行要沿着责任链对齐。这套规则就是后面所有推导的基础。1.3 它回答的四个关键问题把 REA 用到实际业务里它主要帮你回答四个问题谁是交易对手各自承担什么角色资源的控制权在哪个时点发生了转移哪些业务事实之间存在成对关系允许哪些组合业务承诺到这个承诺被真正执行中间有没有断点。这四个问题传统 ER 画图时没人逼你想。这也是为什么很多系统上线时功能正常一运行到对账、审计、纠纷仲裁就到处漏风。2. 把一个采购闭环拆成REA三要素一单到底的资源-事件-代理2.1 场景设定从下单到付款的完整循环光讲概念太空我们走一遍具体业务某制造公司要向一家供应商采购 500 套传感器单价 30 元整个流程大概长这样采购员在系统里提交采购申请部门审批通过后转成正式采购订单。供应商确认采购订单。仓库到货后做收货入库形成入库单。财务根据入库单和发票确认应付账款。出纳在账期内向供应商付款。车间领用 500 套传感器进入生产。生产完工后传感器变成产成品入库。这套流程在很多企业的 ERP 里跑了好多年大部分人习以为常。但用 REA 的镜头看它有一个隐藏的主线——资源的控制权在不同代理之间一次次换手。你只需要找出每次换手的事实就能完整重建整个采购业务不需要任何“状态字段”的辅助。2.2 资源清单不只是“库存商品”在这个闭环里出现的资源至少有这么几类原材料传感器进来的时候是供应商给公司领用的时候是公司内部转移。现金存款付款时从公司流出到供应商。应付账款严格来说它不是实物资源但它代表公司未来需要向供应商兑付资源的经济义务在 REA 里作为资源建模目的是让“欠钱”这件事本身可以被记录和追踪。在制品和产成品生产转化流程中出现的中间资源。说到这我想特别强调一个容易混淆的地方现金流水不是资源银行账户里的存款才是资源。应收款余额也不是资源它是由“交付了但未来会收到钱”的一系列事件推导出来的状态。“流水”和“余额”都是资源变动留下的痕迹是派生数据不是业务事实本身。2.3 事件与代理识别表五个核心经济事件把上面的采购闭环拆下来核心经济事件和对应代理如下事件资源方向内部代理外部代理供应商发货/公司收货传感器流入仓库管理员供应商确认应付账款应付账款增加财务核算员供应商支付货款现金流出、应付减少出纳供应商车间领料传感器流出领料员内部转化完工入库在制品流出、产成品流入车间主管内部转化顺着这个表再看业务你会发现以前很容易忽略的一点采购订单、审批记录、库存状态这些统统不是经济事件。它们要么是“承诺层”的东西要么是管理状态真正改写资源的是上面这几个动作。2.4 成对规则的魅力为什么 REA 不需要“借贷方向”会计里记明细账讲究借贷借表示资源流入贷表示资源流出。REA 更彻底它只记录“哪个事件让资源进来、哪个事件让资源出去”并且强制它们成对出现。比如确认应付账款这个事件让“应付账款”这个资源增加对公司来说这是一个索取权的反转方向之后付款事件让“现金”减少同时让“应付账款”减少。在系统里你不需要约定这笔业务是借还是贷只需要把事件和资源的方向记录下来余额自然就能算出来。这对于开发人员更友好不用去理解复式记账的规则只需要保证每个减量事件都有对应的原因事件账就不会平不了。3. 事件是事实余额是推导这套模型如何帮你重构账务逻辑3.1 从事件集合推导余额一种可审计的对账思路REA 一个反直觉的点是正式模型里根本不保存余额。库存数量是多少不是查库存表而是把“所有入库类事件”加总减去“所有出库类事件”加总。应付余额是多少也是从“确认应付事件”和“付款事件”的差得到。用 SQL 表示就是-- 物料库存 所有收货事件总量 - 所有领用事件总量 SELECT resource_id, SUM(CASE WHEN event_type IN (RECEIVE) THEN quantity ELSE 0 END) - SUM(CASE WHEN event_type IN (ISSUE, CONSUME) THEN quantity ELSE 0 END) AS stock_balance FROM economic_events WHERE resource_id :sensor_resource_id GROUP BY resource_id;这套逻辑最大的好处是可审计——任何余额都能追溯到一组已知事件而不是某个“被人手一改就再也讲不清”的数字。对财务、风控、法务来说这不只是实现细节而是真实性问题。3.2 用配对规则自动发现业务异常成对规则还能直接用来做完整性校验。如果销售系统里存在“已经发完货但既没有生成应收、也没有后续收款”的事件那基本就是业务异常。放到采购侧也一样有收货却没有对应付款义务说明应付流程断了。实际应用时我会在事件表里维护“配对关系字段”或者建立配对事件关联表然后定期跑类似下面这段的未配对检查-- 找出所有已收货但尚未形成应付记录的事件 SELECT e.event_id, e.event_time, e.resource_id, e.quantity FROM economic_events e LEFT JOIN economic_events_pairing p ON e.event_id p.decrement_event_id WHERE e.event_type RECEIVE AND p.increment_event_id IS NULL;这类查询逻辑上非常直白因为模型里天然要维护“每一笔源的流入都有对应源头流出关系”。以前要在十几个表的状态字段里绕来绕去才勉强查清的“差异”在 REA 数据模型里只是一条简单的 join。3.3 REA与事件溯源语义约束和存储架构可以各司其职很多做分布式系统的朋友会联想到事件溯源Event Sourcing它们的理念确实有亲缘关系都是把“事实”作为核心把“当前状态”当成投影。但二者有本质不同。事件溯源是一种架构实现策略关心“怎么追加存储、怎么重放事件流、怎么保证幂等”REA 是一种领域建模方法关心“这些事件是否有经济语义、哪些事件必须成对出现、资源和代理怎么关联”。两者可以配合使用而不是竞争关系用 REA 定义业务域里的经济事件和配对规则用事件溯源作为存储和集成的实现底座。这样既能获得可追溯、可回放的架构优势也不会把“用户改了个昵称”这种没有经济语义的事件混进账户体系。4. REA、ER模型和“三个最常踩的坑”4.1 REA不是ER的替代品而是语义层REA 和 ER 经常被放在对立面这是误解。我认为最准确的定位是REA 关心这个业务“应该被怎么理解”ER 关心“理解完之后用什么表来存储”。而且一张设计良好的 ER 图完全可以表达 REA 模型的结论——无非是资源表、事件流水表、代理角色表、承诺与执行链条表。关键差别在于建模次序。传统做法是“先想表再想业务”REA 是“先想事件事实与配对再落成表”。如果已经有一套 ER 模型也可以拿 REA 三要素去评审现有表结构这是成本最低的接触方式。4.2 坑一代理被压成单纯的外键字段最常见的失误是把“代理”只建造成订单一侧的 customer_id 和 seller_id所有人都当成普通外键看。麻烦很快会来同一家客户既可能是收货方也可能是付款方还可能是开票方一旦三方不是同一主体你只能继续加字段或者用字符串拼状态审计和风控全都别扭。REA 的建议是让“人/组织”与“角色”分离。party主体是一张表客户、供应商、内部部门这些“角色”是另一张关系或字典事件只关联“在特定业务场景下承担特定角色的主体”。这样每个事件都能干净地回答“谁以什么身份做了什么”。4.3 坑二把流程活动当成经济事件第二个高频坑是把“修改”“审核”“状态流转”也当事件往主事件表里灌。订单从草稿到已确认、审批人点了通过这些确实发生了“变化”但并没有任何资源控制权转移它们不是 REA 意义上的经济事件。把它们混入事件流会让所有聚合和配对检查全部失真。我的判断标准很硬只有导致资源进入、退出、消耗、转化或债权债务产生的行为才能进入经济事件表。流程审批与状态机单独用流程表管别跟经济事件混在一张表里。4.4 坑三资源语义漂移把记录体当资源还有一个坑藏在命名里有人把“现金流水表”当成资源有人把“应收余额字段”当成资源还有人把“库存台账”这个记录当成了库存本身。这都犯了同一个毛病——把资源的“影像”和资源本身搞混。资源是那笔钱本身流水只是它的履历资源是库房里那堆实物台账只是它的纪录。建模时我会给资源主体表和流水表完全不同的命名前缀比如 sensor_stock 代表资源本身inventory_movement 代表它的事件轨迹防止新人一上来就把两张表当成一回事。5. 落地步骤与建模自检清单5.1 三步走先圈定互换闭环再映射到表结构想在一个新项目里落地 REA我建议别一上来就想全公司建模。先选一个你最头疼的“钱货闭环”比如“销售-收款”或者“采购-付款”走这三步第一步列出这个闭环里的全部资源包括实物资源和债权债务类资源。第二步列出全部经济事件给每个事件标出内部代理、外部代理和资源方向。第三步检查每个减量事件是否有对应的增量事件画一张事件配对矩阵。配对矩阵我一般就用表格手工画例如销售-收款闭环减量事件对应增量事件配对语义商品出库确认应收账款把货交给客户同时取得了未来收款权应收账款核销收款入账收到钱应收义务终结退款支出商品退回如适用交易退回资源逆向流动矩阵画完表结构怎么落其实已经定了照事件清单建流水表照资源清单建资源表照代理清单建主体和角色表即可。5.2 一张自检问题清单每次建模接近完成我会拿下面几个问题当验收标准全部能回答清楚才算过每个经济事件是否同时关联了至少一个内部代理和一个外部代理资源变动是否都能落实到具体经济事件而不是靠直接修改状态字段所有减量事件是否都有关联的增量事件形成闭环资源表是否明显区别于流水表没有把记录体冒充成资源承诺层与执行层之间是否有一条可追踪的链条我实际用下来这几个问题能挡掉至少一半的返工。5.3 推进策略把它当评审工具而不是文档仪式最后说点推广层面的体会。REA 在团队里容易遇冷因为它不像 ER 图那样接近设计师直觉术语也有点“会计味”。我的建议是别强求所有人都会画正规的 REA 图而是在评审表结构或定义接口事件时用 REA 三要素提问这是资源、事件还是代理这个事件配对了吗这个资源有没有对应的流水主体只要评审会上大家习惯性问出这些问题语义就自然而然变得清楚了。我自己踩过几次坑之后现在反而更愿意把 REA 定位成“提问范式”而不是必须填的文档模板。它能让你在讨论阶段就把那些以后会让财务、风控和开发集体头疼的问题提前暴露出来远比画图本身值钱。