REA建模实战:从资源事件参与者到业务中台落地 做企业系统这些年我越来越觉得绕不开一个建模思路REA也就是Resource-Event-Agent资源-事件-参与者。它最早出现在会计信息系统设计领域但如今用在订单、库存、财务一体化系统里依然能打。简单说REA让你不再死盯着订单表、流水表和一堆对账脚本而是回到业务本身每一笔业务到底有多少资源发生了增减、是什么事件触发的、有哪些参与者经手。我给某图书电商公司做业务中台梳理时用REA把订单、库存、财务的主链重新过了一遍后面无论是改需求、加统计还是做数据追溯都顺了很多。这篇文章就从REA的模型核心开始一步步拆到能落地的建表方案适合正在做业务建模、中台设计或者被复杂业务流程搅到头大的同学参考。1. 设计思路拆解REA不是换个画法是重新理解业务1.1 传统记账模型为什么不够用先看一个很常见的现象很多业务系统核心表是“凭证”和“流水”结构其实就两行借方科目、贷方科目、金额。这套东西在财务视角没问题但放到全业务流程里马上露馅。比如“销售”这个动作系统里可能只有一张凭证借应收账款、贷主营业务收入但库存是哪个环节减少的、仓库是哪个经手人、客户从哪个渠道来全都要反查单据单据之间又没有明确的语义连接全靠各种code硬拼。一次数据校验要写十几个join才能解释“这笔库存到底是怎么没的”。如果认真追溯一下你会发现传统记账模型天生是结果导向的只关心钱和科目的增减不关心业务发生的过程。业务系统如果照搬这套模型等于把业务理解放在次要位置把记账结果当成了核心对象。早期做系统还能接受到了需要做数据分析、风控、供应链协同的时候缺口的代价就非常大。1.2 REA怎么重新定义业务REA的思路完全不同它把业务抽象成三样最稳定的东西资源、事件、参与者。事件负责“干活”资源负责“变多或者变少”参与者负责“谁在操作”。三个概念看着少组合出来的语义却很丰富。一个销售事件会牵扯库存减少、银行存款增加参与者同时是客户和店员一个采购事件会牵扯供应商交货、公司付钱参与者是采购员和供应商。关系一旦建起来业务的因果链就一目了然哪个事件触发了哪个资源变动、由谁负责、在哪个时间点发生全部可以沿着模型追溯。我第一次看REA模型时觉得它跟ER图差不多后来实际建模才发现区别很大。ER图描述的是数据长什么样字段、主键、外键REA描述的是业务到底怎么运作的资源和事件之间的关系、事件和事件之间的配对、参与者承担的角色。前者是静态数据结构后者是动态业务语义。用REA建出来的ER图天然就是可解释的。1.3 什么场景该用REA这里要泼盆冷水不是任何项目都适合上REA。如果业务就是简单增删改查没有跨部门的数据一致性要求或者团队没有一个人理解这个模型硬上REA只会增加建模评审会和沟通成本。我一般这样判断当数据需要支持多维追溯——比如“我要看某客户买了哪些商品分别在哪个时间点由哪个导购成交当时划走了哪个批次的库存”——REA的价值就非常明显。中大型系统的业务中台、ERP、供应链系统本质上都可以用REA描述只是很多团队不叫这个名字管它叫事件溯源或者领域模型。从成本上看REA的启动成本不低主要花在统一口径上。销售、仓库、财务平时各说各话销售说“订单已提交”仓库说“拣货完成”财务说“已开票”其实这些动作对应的是不同的资源变化。REA强迫大家在同一种语言下把业务事件定义清楚这一步跑通了后面的数据交换、指标统计、报表追溯都会省很多事。2. 核心要素拆解资源、事件、参与者怎么辨别2.1 资源一切有价值且可控制的对象很多人一开始会把订单当成资源但在REA语境里订单没有实物价值它是承诺不是资源。资源要有明确的存量控制特征能被记录数量、能被消耗、能被转化。现金、原材料、半成品、成品、设备、工时甚至外部服务都可以算资源。我做过一个项目把“客服热线时长”也建模成资源用来统计资源消耗和对外计费这就属于REA的灵活运用。判断一个对象是不是资源有一个很朴素的测试公司能不能给这个东西做盘点能不能记录它“增加了一件”或“减少了一件”如果能它就是资源如果只是订单状态、单据编号、备注字段那只是业务属性。注意有时一个实体会同时充当资源和属性比如客户赠送的服务积分客户维度下是属性积分池维度下就是资源。建模的时候先约定好分析视角不然评审会能开一下午。2.2 事件真正“动了资源”的动作事件是REA里最容易混淆的概念。判断标准只有一个这个操作让资源增加还是减少了库存被卖出是流出事件销售回款是流入事件原材料被领用是流出完工入库是流入。很多新手分不清事件和阶段。“提交订单”听起来很重要但它没有动库存也没有动钱它只是业务阶段不是REA事件真正动资源的“发货”和“确认收货”才是事件。审核、审批这类活动在REA建模里通常也不单独作为事件除非它本身伴随资源消耗比如引入外部质检服务。核心原则是REA关注的是经济事件和资源间的因果不是OA审批流。如果有人递上来一个流程图上面画了10个节点我一般会问他这10个节点里哪些让资源增加了或减少了通常答完这个问题节点就只剩两三个了。2.3 参与者内外部角色都算别把角色写成具体人参与者分成内部代理和外部代理。内部代理是公司内部的员工或部门外部代理是客户、供应商、物流商。同一个自然人可以扮演多个角色客户既可能是销售事件的购买方也可能是退货事件的退货方。建模时最好把“人”和“岗位职责”拆开一张人员表一张角色表人物通过角色关联到事件。否则一个员工离职整个历史数据就查不到“当时是谁负责的”。另一种常见问题是把“部门”当作参与者直接关联事件。部门确实存在但部门是组织单元不是代理。如果业务考核需要统计到部门应该通过“员工属于部门”这个映射来算而不是让部门和事件直接发生关系。多一张关系表后面做权限和数据隔离会轻松很多。2.4 三要素的关系与基数REA里关系分三类存量-流量关系连接资源和事件描述资源的流入流出参与关系连接事件和参与者描述谁参与了事件控制关系连接两个事件比如发货事件和收款事件配对出现。建模时最花精力的是基数。一个销售事件可以对应多条销售明细每条明细对应一种资源一个收款事件可以对应多个销售事件因为客户可能合并付款。这个一对多关系直接决定了后面数据表的分层和粒度设计。我建议在评审阶段就把每个关系的基数标注清楚并且在模型旁边注明业务原因。比如“为什么一个收款事件可以关联多个销售事件”是因为客户月底一次性结清。如果这个原因写不下来说明业务场景还没理透。基数不是技术人员拍脑袋定的是业务规则真实的形状。3. 实操建模从订单到回款一步步把模型落到表3.1 案例背景与范围划定为了方便说明这里用某个图书电商公司的订单履约链路当案例。业务范围是订单、发货、收款、退货四条主链。数据库里有订单表、库存表、财务流水表但三者之间全靠程序脚本对账。我们的目标是用REA把这些表背后的业务语义统一起来先不覆盖所有边缘需求只建立核心事件链路。第一步是圈范围。范围不要一开始划得太大把订单和售后放进来就够了采购、生产、营销一概先不管。模型是活的后续可以增量扩展。划定范围的唯一硬约束是每个进来的流程都得是能说清楚资源变化的流程不能是那种“只管状态流转、不管库存资金”的空转流程。3.2 列事件清单先回答“资源变了吗”拿订单履约来说候选事件看起来有一堆客户下单、客服审单、仓库配货、物流发货、客户签收、财务收款、客户退货、仓库验货、财务退款。逐个问“资源变了吗”之后事件清单会收缩得很干净发货图书库存减少签收确认应收确认形成债权收款银行存款增加退货入库图书库存增加退款银行存款减少下单、审单、配货不会让资源变多或变少暂时不进事件清单。它们可能在某些管理场景很重要但那是流程管理的范畴不是REA核心模型需要关心的。如果后续需要可以以“流程阶段”的方式挂在事件边上不污染模型主体。3.3 给事件接资源明确流入和流出有了事件清单下一步给每个事件接上资源。这个环节最容易暴露口径冲突仓库说发货少了库存销售说库存其实没少因为客户还没确认收货。这里我们约定以“实际库存增减”为准也就是发货就减少可售库存签收只影响物权转移不再重复增减库存。资源清单最终是图书库存SKU维度、应收账款、银行存款、退款义务。注意应收账款在REA里不是单纯的资金资源它是销售事件和收款事件之间的“承诺”桥接物建模时可以单独作为承诺资源处理。如果你不想把账期做进核心模型也可以先只建库存和银行存款两个资源把应收部分放到财务子域再扩展。我倾向于先用最少资源跑通主链路避免客户一赊销模型直接复杂一倍。3.4 绑定参与者谁对事件负责每个事件都至少要有一个内部参与者和一个外部参与者不然这个事件就缺了“责任”语义。发货事件内部参与者是仓库管理员外部参与者是物流承运商收款事件内部参与者是财务外部参与者是客户退货入库事件内部参与者是仓库验货员外部参与者是客户。客户虽然是同一个实体但在不同事件里扮演的角色要记录清楚这样才能回答“哪个客户在什么时间退了哪批书”。这里的角色关系表建议做成独立的事件参与关系表而不是给事件表加几个外键字段。只允许一个操作员、一个客户看着方便后面一旦出现导购、同行人、物流分单员多个角色同时参与的情况加字段会加到你怀疑人生。3.5 定义关系与基数销售-收款-退货之间的关系需要把控制关系配成对。发货事件和收款事件之间是典型的入流-出流配对正常销售是“先发货后收款”还是“先收款后发货”基数都能表达一个发货事件对应一次收款或对应多次收款分期货款。发货事件和退货入库事件之间不是所有退货都能反查到一个发货事件模型里可以单独挂“退货原因”属性不强求建立控制关系。基数的业务原因一定要标注清楚。为什么因为开发实现时如果不知道“一个收款事件可以合并多个发货事件”很容易把收款表设计成每个销售单一行回款记录导致合并付款全部拆成重复行。这个坑我在实际项目里踩过不止一次。3.6 映射数据库表模型跑通后映射表的思路就比较直接了。我建议主表只放参与者、时间、业务编号明细表放资源变化关系表放事件间配对。表类型表职责核心字段事件主表记录每种经济事件的基本信息事件ID、事件类型、事件时间、业务单号资源明细表记录事件造成的资源增减明细明细ID、事件ID、资源ID、增减方向、数量、金额参与者关系表记录事件中涉及的所有角色关联ID、事件ID、参与者ID、角色类型资源主数据表描述资源本身资源ID、资源类型、SKU/库位/批次等事件配对表记录事件间控制关系配对ID、源事件ID、目标事件ID、配对类型开发实现时事件主表和资源明细表一般是一对多查询某一事件带了哪些资源变动一条join就出来了。想查“某客户从某个仓库买了哪些SKU”走事件主表、参与者关系表、资源明细表三层也能在一屏SQL里完成。性能上事件明细数据通常比较大建议按事件时间做分区不要留一张超大明细表跑全表扫描。4. 常见问题与排查建模时踩过的坑其实很有共性4.1 把“承诺”误当成“事件”订单、合同、采购申请在REA里通常属于承诺不是事件。如果团队不理解这个区分评审会上会反复争论“为什么下单不算事件”。我的建议很直接如果想管订单的完整生命周期可以专门建模一个承诺子域订单承诺包含预期资源流出的明细事件子域只记录真正发生的资源变动。两者之间用“履约”关系连接。这样处理的好处是订单状态再怎么变都不会污染统计口径。库存减少了就是减少了不会因为订单取消而出现“负库存”这种荒谬结果。4.2 资源粒度不统一把库存商品整体当一个资源和把每个批次、每个库位当一个资源建模复杂度和分析能力完全不同。按SKU建模承销和批次库存的追溯就做不了按批次建模同一个销售事件可能会关联多条资源明细明细表的数量会明显膨胀。我的经验是资源主数据按业务可识别的最细颗粒度建但资源明细表允许按事件聚合不要为了粒度把事件拆得零碎。比如图书这件事业务上区分“某出版社某版次某仓库的库存”资源主数据就必须有这些维度的标识但一个订单的发货事件仍然只是一个事件只是关联了多条资源明细。粒度是资源主数据的事不是事件的事。4.3 基数设计拍脑袋前面提到客户合并付款这是最典型的基数反例。另一个反例是一个事件并发关联多个资源但设计表时只留了一个resource_id字段导致多条SKU只能拆事件。凡是出现“一个事件只能对应一个资源”的假设都要回到业务里去验证。如果业务方说不清就按最大可能的基数来建模大不了多一张关联表不会亏。4.4 参与者角色历史缺失人员会转岗、离职但历史事件不能跟着“人没了”就查不到。如果参与者表直接指向员工主数据员工离职后主数据被归档历史追溯自然断了。建议角色关系表里冗余一份操作时的角色描述哪怕只是一个显示用的角色名称也能大幅提升历史追溯的可用性。这是我试过的最省钱的方案。4.5 排查速查表把几个常见问题和排查方向整理成一张速查表方便建模评审时快速过症状可能原因排查方向订单状态频繁变化导致统计重复把状态流转当成了事件问一句资源变了吗库存对不上账发货和签收各减了一次库存明确资源增减的唯一时点客户合并付款出现重复流水收款事件建模成1:1放宽为1对N加事件配对表离职后人名在报表中消失参与者直接引用员工主数据关系表冗余角色和历史标识同一个SKU有多个批次但总值正确资源粒度不够资源主数据按批次/库位独立这张表做完以后建议贴在建模评审会的白板旁边。不是我故意偷懒实在是这些问题太常出现而且每次都要重新解释一遍口径。5. 从模型到真实场景扩展应用和我的几点体会5.1 扩展到采购与生产场景订单履约的模型跑通后往上游扩展其实很简单。把同构模式套到采购链采购订单是承诺到货是入库事件资源增加付款是资金流出事件。生产链上投料是原材料转出事件完工是成品转入事件。每条链的模型长得不太一样但抽象套路完全一致先找资源再找事件再绑定参与者最后配关系。唯一要注意的是跨链的资源主数据要好用。比如产成品的SKU如果在库存域和生产域两套编码事件一跨链就断了。一定要统一资源主数据的编码体系这是REA能纵向扩展的前提。5.2 与领域驱动设计的对照REA模型和领域驱动设计可以很好地互补。资源大体对应聚合根事件对应领域事件参与者对应领域服务里的角色。但REA多了一个“存量-流量”的视角让你在清点库存、核算余额时不是靠扫描表里所有行做加总而是能直接从资源表里拿出元数据。这个对照关系做技术方案时可以作为沟通翻译器。团队熟悉DDD的可以把REA翻译成DDD讲给他们听接受度会高不少。5.3 实际落地中的体会最后说几条我自己的操作体会。第一条建模第一版一定不要追求大而全把当前最痛的主链先跑通让开发尽快看到“模型能直接映射成表”团队信心会立刻建立起来。第二条模型评审时把业务方拉进来不是让他们签字画押而是借评审机会把“销售说已成交、仓库说已发货、财务说已回款”背后的资源变动对齐。第三条所有模型里的自定义语义比如什么是可售库存、应收账款算不算资源、哪些事件才计入营收都要写进团队内部wiki而不是靠老员工口口相传。否则换一个人口径就变了模型跟着飘。这套REA建模方法用了几个项目之后我最大的感受是它不会帮你减少一次沟通但能帮你把每一次沟通都收敛到同一个语义框架里。要不要用、怎么用、用多深最终取决于你对业务控制力的需求而不是别人推荐了某个模型就无脑上。先把资源和事件这两个概念和团队对齐迈出第一步后面的事情会顺很多。