ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

REA模型实战:从业务事件到ER建模与会计分录生成

REA模型实战:从业务事件到ER建模与会计分录生成 第一次在项目文档里看到“REA”这三个字母时我并没有太在意以为又是某个咨询团队造出来的新缩写。直到后来在一套订单履约系统的数据建模里我反复被“业务部门说的流程”和“财务科目表里的账务”之间那层说不清的断层卡住才重新把REA模型翻出来认真啃了一遍。这篇文章想聊的就是这个被很多人当成“学术概念”的REA模型Resources-Events-Agents资源-事件-参与者以及它在真实信息系统建模里到底怎么落地。我会用一套电商履约的业务场景把从业务事件梳理、ER建模到会计分录对照的完整过程拆开讲也会把我在实际项目里踩过的最典型的三个坑一并交代。如果你想找一种能同时兼顾业务语言和财务语言的数据建模思路这篇文章应该能给你一些直接能用的东西。1. 为什么业务建模会撞上“账本思维”的天花板1.1 业务部门讲的是流程技术部门画的是科目表做一个企业级系统第一件要吵起来的事永远是业务需求怎么翻译成数据结构。业务人员说“客户下单之后我们要审核订单安排仓库拣货出库然后开票最后收款。”这个过程听起来很清晰五个环节、时间顺序、责任人都有。但等需求文档到了技术这边很多人下意识做的一件事是把这些流程全部塞进“凭证—科目—借贷”的结构里订单变成应收账款科目出库变成库存商品减少收款变成银行存款增加。业务上明明是连续发生的一串事件到了数据模型里只剩下一条条相互割裂的会计分录。复式记账法当然没毛病它已经运行了几百年在记录资金流向这件事上极其可靠。但一个账务模型如果直接当作业务系统的核心数据结构来用问题马上就会出现会计科目是结果的分类不是业务事实的记录。你从科目表里能看出这个月卖了多少货但你很难回答“那批货是哪位销售在什么条件下签下来的仓库为什么分了三次才发完”。而这些恰恰是业务系统最需要回答的问题。1.2 REA模型的核心主张把“交换”而不是“记账”当作建模原点REA模型最早是由会计信息系统领域的学者提出的它想解决的就是上面这个断层。它的做法很朴素既然业务本质上是一系列资源在不同参与者之间流动和交换的过程那我们就直接记录这些“流动和交换”而不是一开始就套上借贷分录的外壳。REA把业务世界抽象成三类核心实体Resource资源具有经济价值、能够在事件中被获取或消耗的对象比如库存商品、现金、应收账款、服务工时。Event事件导致资源控制权发生变化的行为比如销售出库、收款、采购入库。Agent参与者参与事件并承担责任的个人或组织比如客户、销售员、仓库管理员、供应商。这个结构最巧妙的地方在于它把“谁、在什么时间、通过什么事件、让什么资源发生了怎样的流动”完整记录了下来。业务流程的所有关键信息不再依赖科目名称来隐式表达而是由事件实体本身承载。REA并不否定复式记账而是把账务看作这些业务事实在“财务视角”下派生出来的结果。1.3 REA适合解决什么问题不适合解决什么问题说句公道话REA不是银弹。我在几个不同类型的项目里观察下来它的适用边界很清楚适合需要跨部门追踪业务全链条的系统尤其是订单、采购、库存、供应链这类有多方参与、有资源流动的业务。对审计追溯、合规内控有刚需的系统尤其合适。适合作为数据仓库或数据分析的语义层。REA模型天然记录了事件明细从任何维度聚合都相对方便。不适合传统的财务总账系统。总账的记账规则非常成熟稳定你非要重造成REA模型等于给自己找麻烦。所以这篇文章后续的建模过程都是基于“业务系统需要对接财务又不能被财务科目绑死”这个前提来展开的。如果你做的就是一个纯财务核算模块可以不用引入REA那是另一套更成熟的做法。2. REA三要素的边界到底怎么划2.1 Resource别把“资源”理解成“字段”很多第一次接触REA的人容易在Resource这里翻车因为他们会把系统里的各种数据对象一股脑都当成资源。判断一个东西到底该不该建模为Resource标准其实很硬核这个对象是否会因为某个事件而被获取、消耗、转换或失去控制权。举个例子。一个商品主数据里面有商品编码、名称、规格、重量、供应商信息这些属性只是描述性的。那商品本身是不是Resource是因为它的库存数量会随着出库、入库事件发生变化。但商品主数据里的“供应商联系方式”不是Resource那只是属性。再比如客户。很多建模者习惯把客户做成一个“主数据表”然后跟订单关联。在REA里客户不是Resource而是Agent参与者因为客户是控制权转移的另一端他不是一个被转换或消耗的对象。但“应收账款”是Resource因为它会被“收款事件”消耗掉。这里我常用的判断方法是问一句话如果业务上发生了一个事件这个对象会不会在系统里新增或减少一条记录如果会它就是Resource如果不会它多数情况下只是Event或Agent的某种属性。这个问题的区分度非常高在实际建模评审时能避免一大半的争论。2.2 Event操作、状态、事件三者要严格分开Event是REA模型里最核心也最容易混淆的实体。我见过最典型的错误是把系统的“操作日志”直接当成事件来建结果日志表里什么都有真正能支撑业务分析的事件却找不出来。真正的REA Event需要满足两个条件它导致了某个Resource的控制权发生变更它在业务上有明确的时点和责任主体。我习惯把系统里发生的事情分成三类业务事件如“销售出库”“采购到货”“收款核销”。这类事件是REA需要建模的核心对象它们改变资源状态。系统操作如“打印了出货单”“导出了库存报表”。这类操作虽然也会留痕但没有任何资源的控制权转移不应该混入REA的Event实体里。状态变化如“订单状态从待审核变为已审核”。状态变化通常是事件发生后的结果而不是事件本身。你可以用事件推导出状态但不要反过来把状态更新当成事件来记录。在一套订单系统里“订单审核通过”这一个动作如果发生在业务语义上是重要的资源控制权变更起点它就可以是一个Event但如果只是内部流程中一个临时检查点了我会把它作为订单实体的状态而不是单独的事件记录。具体划法要回到业务上下文里看它对资源流动有没有实质贡献。2.3 Agent内部参与者、外部参与者以及“角色”的问题Agent是REA里容易被人弱化的一类实体。很多人觉得“客户、供应商、员工”这不就是几个外键字段吗有什么值得建模的。实际上Agent的正确建模直接决定了审计追溯和职责分离能不能做起来。我通常会区分两类Agent内部参与者企业内部的人或部门比如销售员、仓管员、财务审核人。外部参与者客户、供应商、代理商以及各种与业务资源流动有关的外部机构。关键点是REA模型里Agent和事件之间的关联要落到“具体是谁在那个时间点参与了这个事件”。所以建表时Agent不应该是订单表里的一个“下单人”字段而应该是独立实体通过参与关系与Event实体建立关联。这样同一个销售员做了哪些销售单、经手了哪些收款核销都能被清晰地追溯出来。还有一个高频问题要不要把“岗位”和“具体的人”分开建模。我的答案是必须分开。因为人员会流动而审计和流程控制关心的是职责是否分离不是张三还是李四。以“角色”为主体建立Agent-Event关系再把员工挂到角色下面是更稳妥的做法。这样某位员工离职后历史事件链条也不会断。下面用一个表把三要素的判断逻辑快速总结一下候选对象判断问题结论库存商品出库事件会不会减少它的数量Resource应收账款收款事件会不会核销它的余额Resource客户主数据客户是否被某个事件消耗或获取否是描述性主数据客户订单上的客户是否参与了经济事件Agent销售员是否参与了经济事件Agent内部支付状态“已支付”是否由某个支付事件导致不是Event是Event的结果打印发货单是否导致资源控制权变化不是Event是系统操作3. 从业务场景到ER图一套订单履约的REA建模全流程3.1 先讲一个具体的模拟项目背景为了让整个建模过程不那么抽象我拿一个模拟项目来走一遍全流程。假设某家公司主要做电商销售业务链条是客户在线下单销售部审核后仓库拣货出库财务开票并收款。现在要重新设计一套核心交易系统要求既能完整记录业务过程又能方便后续对账和经营分析。拿到这个需求先不要急着画ER图把业务事件按时间顺序列出来才是正经第一步。我会和业务方一起把从接到订单到最终回款的全过程走一遍统一事件的名称、时点和责任方。当时梳理出来的核心事件如下客户提交订单销售员审核确认订单仓库按单拣货仓库完成出库发货财务开具销售发票财务记录客户回款这一串看起来简单但每一条都要和业务方反复确认“这个动作会不会让某项资源减少、增加或者换手”。这一步确认完了建模的地基才算打牢。3.2 识别TY卷里的资源与参与者事件流水理清之后再切换到资源视角把每个事件涉及的经济资源标出来客户提交订单涉及“订单承诺”这类尚未来到实际资源流动的权益记录我习惯先不把它直接建模为Resource而是把订单事件本身作为记录载体。销售审核确认同样暂不产生库存变动但审核这个动作已经在业务上生效。拣货库存商品被锁定或准备出库库存数量开始发生变化。出库发货库存商品实际减少同时可能形成“应收款”概念。开票产生“应收票据”或对应收账款的确认。回款现金/银行存款增加同时应收账款减少。所以在资源实体表里至少要建立库存商品、应收账款、银行存款或现金等价物、销售发票如果可以作为一种权益凭证。参与者则包括客户外部、销售员内部、仓管员内部、财务审核人内部、系统对接的银行渠道外部。参与者先按角色建模具体员工挂在角色下。3.3 建立事件-资源-参与者之间的核心联系到了这一步方法就很机械了但也很考验耐心。我给每条事件和资源、参与者之间的连线关系做一个完整梳理销售出库事件关联Resource为“库存商品”变化方向为减少关联Agent为“仓管员”和“客户”。收款核销事件关联Resource为“应收账款”减少和“银行存款”增加关联Agent为“财务审核人”和“客户”。开发票事件关联Resource为“应收账款”增加或确认以及“销售发票”新增关联Agent为“财务审核人”和“客户”。一个Event可以同时关联多个Resource这是REA模型正常的状态不要因为它不符合“一张单一方向流水表”的习惯就觉得不对劲。另外所有Event之间也存在业务上的先后依赖关系。比如“销售出库”通常发生在“销售订单审核确认”之后“收款核销”发生在“发票开具”之后。这些依赖关系同样要落到模型里作为流程合规性校验的基础。3.4 订单实体到底是Resource还是Event这里要单独说一下订单因为它是在大多数系统里最容易被建模成“资源”的对象但严格按REA的边界来看它更像一个事件的载体。订单本身并不是被消耗或获取的经济资源它记录的是一次交易的承诺和执行过程。所以我在模型里会存在“订单”实体但它是作为销售事件的聚合记录而不是Resource。真正发生资源流动的是发货时减少的库存、回款时增加的银行存款。订单把这次交易的事件链串起来但它自己不是Resource。用一句话给团队解释这个区别库存账上少了一批货那批货是资源订单表里多了一条编号那串编号只是线索不是资源。这个区分决定了你后续做库存账和交易流水时的数据组织方式。实际落地到表中我会把Event实体拆成一张事件主表和若干事件类型子表也可以让订单作为事件类型的载体不同业务类型共享同一套REA结构。无论哪种实现核心原则不变资源变动、事件节点、参与人关系三要素要能被独立查询和组装。4. 从REA模型怎么倒推出一张合格的会计分录4.1 业务事实与会计凭证之间的映射逻辑很多团队看到REA模型后的第一反应是那财务记账怎么办总不能让业务系统彻底跟总账脱钩吧。REA模型的设计本意不是消灭会计分录而是让会计分录可以从业务事实中可靠地推导出来。推导的核心逻辑是每一个经济事件都会引起至少一个Resource的增加和至少一个Resource的减少增与减的双方正好对应会计分录里的借方和贷方。拿“销售出库”来演示一下。在REA里这个事件关联了两类资源变化库存商品减少资源流出企业应收账款增加企业取得了收款权益。那么映射到会计规则就是借记应收账款贷记库存商品。金额就是出库商品对应的成本金额或销售金额具体按企业会计政策来定。再看“收款核销”银行存款增加Resource流入应收账款减少Resource减少。映射结果是借记银行存款贷记应收账款。只要REA建模时把每一个事件涉及的Resource增减方向都记录下来会计分录的生成就是一套确定性很高的规则翻译而不是靠人工判断。这能大幅减少对账时业务数据和财务数据不一致的扯皮。4.2 两套体系并行业务流水与财务总账各司其职我经历过的落地效果最好的方式是双轨制建模核心业务数据全部按照REA模型存储在数据出口处设一个“记账引擎”把REA事件转换为总账分录。这样做不是因为REA不能直接当账务账本用而是因为传统总账经过多年发展在期末调账、汇兑损益、税金计提、合并报表等场景里已经有成熟稳定的流程。你没必要把这些全部重写一遍。合理的做法是REA模型负责业务事实层总账负责财务核算层中间用事件记录和资源变化数据作为自动化记账的数据源。这个结构做完之后还有一个额外的红利每一条会计分录都能追溯到具体的事件、资源、参与者审计人员提问时不需要再从系统里翻半天直接顺着事件表往下钻就行。4.3 会计分录对照示例我用上面那套模拟电商场景把几个核心事件的REA结构与会计分录的对应关系列成了一张对照表这张表也是我当时给团队配的一张内部参考表业务事件REA中的资源变化会计分录方向简化销售出库库存商品减少应收账款增加借应收账款贷主营业务成本按成本计价时另有调整开发票应收账款确认或无实际资源变动时仅作凭证借应收账款贷主营业务收入收款核销银行存款增加应收账款减少借银行存款贷应收账款采购入库库存商品增加应付账款增加借库存商品贷应付账款支付供应商货款银行存款减少应付账款减少借应付账款贷银行存款这张表里的分录属于极度简化的示意真正的记账引擎里还要处理税额、折扣、坏账等等。但核心思路很清晰资源增减的方向性是连接REA模型与会计记账规则的天然桥梁。5. 建模项目里最容易踩的三个坑5.1 坑一把“状态”当成“事件”记录导致分析时什么都查不到我第一次带着团队做类似项目时在产品讨论里就埋下了这个坑。当时我们为了方便给前端展示设计了一张“订单状态变更表”记录了从“待支付”到“已支付”再到“已发货”的每次变化。门面上看这表很像事件但实际上它只存了“状态变成了什么”没有存“是什么业务动作导致的”。结果做对账分析的时候我们发现一个问题订单状态已经显示“已发货”但出入库流水里找不到对应的出库事件记录。最后查来查去原来是前端一调接口就把状态改成“已发货”而仓库实际的出库动作记录在另一个模块里两边的数据没接上状态和真实资源流动完全脱节。修复思路也很直接把“订单状态变更表”作废重新按REA定义了真正的出库事件和资源变动记录。状态字段可以继续保留用于查询展示但它只能由事件表聚合而来不允许前端直接修改状态字段。这个约束写进系统规范之后数据质量问题基本消失。5.2 坑二事件里塞了方向语义业务规则一变就把模型推翻还有一个很容易犯的建模错误是把“方向”直接塞进事件本身。比如设计出库表的时候直接加上“库存变化方向减少”的字段设计收款表时直接加上“资金方向增加”的字段。一开始看起来没毛病但业务规则调整后就麻烦了。举个例子。库存盘点有时候会发现盘盈也就是账面上没有但仓库实际多出一批货。这个业务动作会让库存增加但它本质上还是一个“库存调整事件”。如果你在设计事件表时把“出库”和“入库”强行分成两个方向不同的事件类型那盘盈这种“增加方向的调整”就会找不到合适的事件类型只能硬塞到入库事件里去语义完全被扭曲。我后来的经验是模型里只需要记录“哪个Resource参与了哪个Event”具体是增加、减少还是只是冻结占用放到业务规则层去推导。REA模型在存储层保持事实中立的记录方式后续任何业务政策变化都不会推翻表结构。这一点对快速迭代的业务系统尤其重要。5.3 坑三参与者建模只挂人名不挂角色职责分离无处落地最后一个坑比较隐蔽一般要等到审计和内控流程开始使用系统时才暴露出来。我们曾经在一套采购系统里把“供应商资料录入”和“采购订单审批”都挂到了具体员工ID上。平时没什么问题直到做职责分离检查时才发现原来同一个员工既负责录入供应商主数据又负责审批该供应商的采购订单这在内部控制上属于重大缺陷。由于历史数据已经按真实人员绑定事后想去追溯“当时是谁以什么角色做了这件事”已经非常麻烦。正确做法是前面提到过的参与者与事件的关系应该接到“角色”上而不是直接接到员工ID上。比如“供应商资料维护员”是一个角色“采购审批人”是另一个角色。系统运行时记录角色和事件的关系具体人员与角色的绑定可以随时调整。这样既能审计事件链条又能灵活应对人事变动。如果你正在设计合规要求比较高的系统这条建议一定要提前考虑。角色建模看似增加了一张表实际上帮你省掉的审计整改成本远比这张表的开发成本高。6. 从账务数据走向业务事实REA模型的延伸应用6.1 用REA语义层支撑经营分析与多维追溯当一套系统按REA模型稳定运行一段时间后它的数据资产价值会远远超出“能用”这个层面。比如经营分析场景传统做法是从总账科目表里拉出收入、成本、费用再做维度拆分。但总账科目是没有天然的业务维度的你很难知道“华东区域在上个月第四周因为促销活动产生的那批应收款对应的是哪些订单、哪些仓库的出库单”。而REA模型里事件实体天然带着时间、资源、参与者三个维度的完整关联。做BI分析时直接以事件表作为事实表以资源表和参与者表作为维度表绝大多数分析需求都能直接覆盖。我另一个实际体会是这种语义层对业务方的沟通成本极低。给业务部门看数据报表时他们意识里就是“哪个业务员、在什么时间、把什么货发给了哪个客户”REA模型几乎是他们业务语言的直接映射。不需要解释什么叫科目也不需要解释什么叫借贷。6.2 供应链上下游协作与审计追溯如果把REA模型的应用范围扩大到一个供应链网络里它的优势会更加明显。不同企业之间可能使用各自的系统但只要大家都以资源、事件、参与者来抽象业务协作那双方的订单、发货、收货、对账数据就有了共同语言。比如一次跨企业的物资调拨上游企业记录“库存商品减少-发运事件-承运商”下游企业记录“库存商品增加-收货事件-质检员”两个系统之间的数据交换只需要统一事件编码和资源编码对账逻辑天然成立。这是REA模型在数据中台和数据交换场景里被越来越多团队采纳的原因。审计追溯更是REA模型的主场。会计审计的核心逻辑之一是“经济业务发生—账簿记录—报表反映”的链条可查。传统系统里这个链条依赖大量中间表、日志表和人工凭证查起来非常痛苦。而REA模型的每一条会计分录都可以顺着“分录-事件-资源-参与者”一路穿透到原始业务动作审计人员从一张异常凭证开始几分钟内就能定位到具体的事件和经手角色。6.3 一个值得记住的经验把REA当沟通工具先画事件流水再谈表结构最后我想给所有准备尝试REA建模的团队一个可能反直觉的建议不要把REA直接当作一个数据库设计规范来推先把它当作跨部门沟通工具。在我实际跟业务部门评审模型的时候完全不提“Resource”“Agent”这类术语只用一个画满业务事件的图纸来表达谁在什么时间对什么资源做了什么动作。业务方确认这张图没问题之后技术团队再按REA的实体划分去落地表结构。这样一来业务和技术是在同一张图上对齐认知的而不是各自用各的术语争吵。还有一个小技巧可以用来判断你的REA模型是否真的建好了试着让一个不了解系统的人只看事件流水和资源变化记录能不能完整复述出一笔业务从头到尾的真实过程。如果能说明模型抓住了业务的核心事实如果不能说明还有重要的对象被藏在了某张表里没有露头。REA模型不是一个需要几年时间才能落地的抽象理论它就是一套很朴素的建模视角。只要你能克制住“什么都想做成科目”的冲动愿意先把业务事件一条一条梳理清楚你的数据模型会比绝大多数“订单表加科目表”的传统设计耐用得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进