我从一个旧项目的数据模型文档里翻到过一个简写:rea。旁边只留了一行注释,写着“resource-event-agent model”。当时整理资料的人可能觉得这三个词已经够直白,不需要再解释,但对于后来接手的人来说,这行注释基本等于没说。我花了不少时间把模型还原出来,又在几个实际项目里反复用、反复改,最后发现,这套上世纪八十年代提出的建模思路,放在今天的数据架构和领域建模场景里,依然非常能打。这篇文章就把 REA 模型怎么理解、怎么建模、怎么落地到关系数据库、以及和传统复式记账到底差在哪里,一次讲透。
不管你是做数据建模、后端设计,还是刚接触事件溯源这类新概念,REA 都值得你花半小时搞清楚。它不是一套花哨的新框架,而是一种看待业务本质的视角,学会了之后,你再看很多表结构设计,一眼就能看出哪些是“真相”,哪些只是“视图”。
1. 从一个“rea”开始:REA 模型在解决什么问题
REA 的全称是 Resource-Event-Agent,中文一般翻译成“资源-事件-主体”。它最早由 McCarthy 在 1982 年的一篇论文里提出,初衷是重新设计会计信息系统(AIS)的数据模型。当时的主流做法,是直接围绕“借贷分录”来设计数据库:一张凭证表、一个分录明细表、一堆科目表。这套模型从复式记账的角度看非常严谨,但它有一个致命问题——数据里只保留了“金额怎么加减”,丢掉了“业务究竟是什么”。
举个例子。一张报销单在传统模型里,体现为“管理费用借一百,银行存款贷一百”。至于这笔钱花在了哪里、买了什么、供应商是谁、合同在哪,全部要靠另一套单据系统去管。两套系统的数据一旦对不上,对账就成了灾难。REA 的出发点是反过来的:先把业务事实记录清楚,再把财务视图作为一种派生结果从事实里推导出来。它的核心信念是:业务数据应当描述“发生了什么”,而不是描述“记了什么账”。
在实际项目里,REA 帮我解决的第一个痛点是数据模型的可扩展性。传统会计模型的表结构高度依赖科目的定义,业务一变化,科目表就得调,历史数据的口径就会乱。REA 把模型建立在“资源”“事件”“主体”这三个稳定的业务概念上,业务再怎么演进,骨架不用变,只需要新增事件类型和资源类型。当时我把一个进销存系统的核心几张表全部按 REA 重构之后,后面加了会员、优惠券、服务订单这些新业务,几乎没有改过核心表结构,只加了关联表和事件类型枚举。
REA 解决的第二个问题,是让技术人员、业务人员和审计人员能说同一种语言。“资源”对应业务管理者关心的一切被占用、消耗、获取的有价值物品;“事件”对应业务动作;“主体”对应谁参与了动作。画出来的模型天然就是业务故事的剧本。我在做用户调研的时候,直接拿着 ER 图和业务负责人逐条核对,“这个事件有没有改变资源?谁参与了?你确认这个动作发生后库存真的变了?”——对方不需要懂任何数据库概念,就能指出模型里的问题。
简单说,REA 是一套面向业务语义的事件建模方法。它不教你怎么记账,它教你怎么把真实世界的经济交换完整地数字化。这个定位,决定了它和传统会计模型之间既有交集,又有本质区别。后面我会逐一展开。
2. 资源、事件、主体:三个概念的精确定义与边界判断
要用好 REA,第一步不是画图,而是先把三个基础概念吃透。很多人在这一步就犯迷糊,因为这几个词太常见了,常见到每个行业都有不同的默认理解。我给出我认为最实用的一套判断标准。
2.1 资源(Resource):会被事件增减的有价值对象
资源定义的核心,是“被经济事件直接影响其可获取数量或价值的事物”。它可以是有形的(商品、现金、原材料),也可以是无形的(应收账款、知识产权、数据资产)。判断一个东西是不是资源,就看两件事:第一,它是不是企业能够控制的、有经济价值的对象;第二,它会不会被某个业务事件改变状态或数量。
最容易出错的地方,是把“订单”当作资源。订单本身只是一个协议,它不因为一次发货就减少、也不因为一次付款就增加,它是描述未来资源转移的载体,而不是被消耗或获取的对象。我在一个项目里就见过把订单主表和订单明细都当作“资源”来管理的设计,结果一段时间后,库存数据出现了严重的不一致,因为订单状态和库存增减混在了一起,谁也分不清哪次变化是实际的货物流转,哪一次只是订单状态跳动。
另一个常见误区,是把“商品主数据”和“库存”混为一谈。在 REA 里,商品主数据是描述性数据,它定义了资源“是什么”,而库存才是真正的资源“有多少”。如果把它们放在同一张表里,那么“新增了一个商品品类”这种静态操作,也会被误当成资源增减事件,整个事件链就乱了。
2.2 事件(Event):直接改变资源现状的业务动作
事件是 REA 模型的核心,它表示的是一次发生在某个时间点、改变了资源状态的经济动作。核心词是“直接改变”。是不是每个业务动作都算事件?不一定。
在销售业务里,“下单”不算事件,因为下单不直接改变任何资源(库存还没动、钱也还没收);“发货”算事件,因为库存实实在在地减少了;“收款”算事件,因为企业的现金或应收账款实实在在地增加了;“退货”算事件,因为库存又增加了、退款也发生了。
这个标准在系统设计中有非常直接的指导意义。我在建模时有一条硬规矩:只要一个动作不会产生资源的增减,只改变“单据状态”,那它就不该出现在事件表里,只应该作为事件的背景信息或状态字段存在。这条规矩帮我过滤掉了大约三分之一业务方眼中的“业务动作”,让事件表保持了极高的业务纯度。
还有一点,事件必须包含时间信息,并且最好区分“业务发生时间”和“系统记录时间”。很多项目早期只记录了一个时间字段,后来做数据追溯时才发现,系统补单的情况很多,业务发生时间和数据录入时间差了几天,统计口径全都乱了。REA 模型天然要求事件是一个带时间戳的事实,所以我建议从第一天就设计两个时间字段。
2.3 主体(Agent):参与事件的个人、组织或系统
主体是参与经济事件的人或组织,包括企业内部的员工、部门,也包括外部的客户、供应商、物流公司。主体在事件中扮演什么角色,需要通过“角色”属性来区分。同一个客户,在一笔业务里是“买家”,在另一笔业务里是“退货申请人”,主体本身不变,但角色可以变。
这个设计看似简单,实践里很容易被忽略。最常见的问题,是把角色信息直接做成了主体表的字段,比如客户表里放一个“客户类型”:新客户、老客户、VIP。这个字段本质上是分析标签,不是参与事件的角色。如果跟着业务变化去改主体表的字段,表结构很快就会膨胀,查询逻辑也变得复杂。
我建议的建模方式,是让每个事件和主体的关联都带一个角色值。事件-主体的关联表,结构大概是:
CREATE TABLE event_agent ( event_id BIGINT NOT NULL, agent_id BIGINT NOT NULL, role VARCHAR(32) NOT NULL, -- BUYER, SELLER, WAREHOUSE_KEEPER, CARRIER... PRIMARY KEY (event_id, agent_id, role) );这样的好处是,同一个主体的所有参与历史都可以通过事件关联完整回溯,而不用维护多套主体档案。在后续做用户行为分析、风险控制的时候,这种设计可以直接支持“某个主体在过去一年参与的所有事件流”这类查询,非常顺手。
2.4 经济交换(Exchange)与双向性(Duality)
在三个基础实体之上,REA 还有一个隐含的、非常关键的概念:经济交换。一个完整的经济业务,往往由至少两个事件构成,这两个事件之间存在着相互对应的资源流动。比如销售业务中的“发货”和“收款”,一个流出商品,一个流入现金,这一对事件构成了经济交换。
这个“双向性”约束是 REA 模型的灵魂,也是它区别于普通事件日志的地方。它表达了业务的基本逻辑:企业不会无缘无故地把资源交给别人,每一次资源的流出,都会对应着一次资源的流入,或者一种债权的确认。在设计数据库时,需要在事件之间维护这个对应关系,而不是让两个事件孤立地存在。这也是后面对账、审计、异常检测的基础。
3. 以销售业务为例,完整推演一遍 REA 建模全过程
光讲概念不过瘾,我用一个最常见的销售业务来完整走一遍建模过程。假设我们现在要为一个玩具批发公司设计核心业务数据模型,业务链条是:客户下订单,仓库发货,财务收款,偶尔有退货。
3.1 第一步:识别业务事件,建立事件清单
先把所有会改变资源状态的业务动作挑出来。我一般直接问业务方:“你能列出所有让库存变多、变少,或者让钱变多、变少、变成欠款的操作吗?”按照答案,我们得到这样一批事件:
- 销售发货:库存减少,应收账款增加
- 采购入库:库存增加,应付账款增加
- 客户回款:现金增加,应收账款减少
- 支付货款:现金减少,应付账款减少
- 销售退货:库存增加,现金减少(或应付款增加)
“订单创建”没有出现在事件清单里。它只代表一种契约状态,会影响后续库存是否被锁定,但不会让资源真正发生变化。如果业务方坚持要把订单当作一个重要动作,我会把它建模为事件的一张“草稿副本”,放在单独的订单表里,但绝不会放到事件主表里。订单表的订单状态可以随意变化,但每个发货物、每个退回货,都必须对应到真实的事件记录,并且可以追溯到它是由哪个订单触发的。
3.2 第二步:为每个事件关联资源与主体
以“销售发货”为例,它的资源关联有两类:一是库存中的具体商品货位,二是因此产生的应收账款资源。这里有一个建模经验:资源要区分“存量型资源”和“增量型资源”。库存是存量型资源,事件发生后直接修改它的数量;应收账款则是在事件发生后才产生的增量型资源,它的生命周期是一条“从无到有,再到被核销”的轨迹。
在数据库层面,我不推荐把应收账款直接做成一张独立大表,因为它的增加和减少是由不同事件驱动的(发货增加应收,回款减少应收)。更稳妥的做法是,把手头所有事件关联的资源变化都记录成规范化的“事件-资源增减明细”,再通过物化视图实时算出资源的当前余额。这样的设计,能够满足“任意时点应收账款余额是多少”的查询需求,而且不需要像传统应收系统那样做复杂的冲销逻辑。
主体的关联相对直白。销售发货事件至少要关联两个主体:内部主体(仓库操作员)和外部主体(客户)。这两个主体通过角色字段区分。需要注意,系统使用的“当前登录用户”不一定是业务主体,有时业务人员会替客户下单,那么事件的实际主体是客户,操作员只是系统记录的创建人。这个坑我在很多团队里见过,业务数据里的“谁”经常被系统操作员污染。
3.3 第三步:确定关系基数与约束
资源与事件的关系大多数是“多对多”。一次销售发货包含多个商品,一个商品也会被多次发货;一次收款可能覆盖多个订单,一个订单也可能被多次收款。关系建模时,需要引入关联实体,比如发货明细、收款明细。
主体与事件的关系,事实层面是“事件对主体为一对多”,即一个事件有一个客户、一个操作员;但从主体的时间线上看,一个主体会参与多个事件,所以画出来的 ER 图通常是一张多对多的大网络。REA 模型在处理这种关系时有很大的宽容度,它允许你给关系加上时间、角色、数量、金额等属性,让关系本身成为可追溯的事实。这一点和传统数据库设计为了“精简”而砍掉关系表属性的做法非常不同。
双向性约束是这个阶段的重头戏。举例来说,销售发货事件与客户回款事件之间需要建立对应关系,记录“哪几次回款清偿了哪几次发货产生的应收”。我建议单独建一张“经济交换关联表”,关联发货事件和回款事件,并带上核销金额、核销时间。这样既保证了业务的完整闭环,也为后续做账龄分析、坏账预测提供了最底层的事实数据。
3.4 第四步:用新需求检验模型弹性
模型建完,一定要拿新需求去压测。比如,“客户退货”这个需求来了,它在模型里就是一个新事件类型:库存增加、现金流出(或应付增加)。不需要改核心表,只需要在事件表里新增事件类型枚举,并在资源增减明细里新增记录;“给客户折扣”这个需求,可以建模为“销售发货”事件的一个折扣属性,或者一个新的“折扣事件”,两种方案都可行,取决于折扣是当次决定还是事后结算。如果折扣金额会在后续回款时发生冲抵,我倾向单独建模一个“折扣事件”,再接回经济交换关联。
这套模型的弹性让很多开发同事比较认可:业务方提出一个新动作,团队的第一反应从“我们要不要加个类型字段”变成“这算什么事件、它改变了什么资源、涉及哪些主体”。方向一变,设计质量自然不一样。
4. 复式记账与 REA 的正面比较:一个管钱,一个管事
很多第一次接触 REA 的人会问:既然复式记账已经用了几百年,全球企业都在用,为什么还要学一套新模型?我的回答是:REA 不是用来替代复式记账的,它是用来替代“以记账凭证为唯一核心”的业务数据组织方式的。两者的侧重点完全不同。
复式记账的核心逻辑是“有借必有贷,借贷必相等”。它通过双分录保证账目的数值恒等,审计员可以顺着凭证把一个科目的增减追溯到原始单据。这种设计的优点非常明确:数值强一致、审计路径清晰、法规适应性强。缺点是:业务语义在进入凭证这一步就被压缩了。一张凭证上记的是“借应收账款,贷收入”,至于这批货的批次号、对应的销售合同、客诉处理进程,全都留在业务系统里,财务系统和业务系统之间因此出现了一道看不见的墙。
REA 的侧重点则是“完整记录经济事实”。发货就是一个事件,它连接着商品资源和客户主体;收款是另一个事件,它连接着现金资源和客户主体;两个事件之间的双向关联,构成了业务闭环。在这套模型里,财务会计所需的金额、账户、借贷方向都不必作为核心存储结构出现,它们可以从事件与资源的关联关系中推导出来:资源的增减方向决定借贷,增减金额决定分录。
我做个对比,方便你直观理解:
| 对比维度 | 复式记账模型 | REA 模型 |
|---|---|---|
| 核心存储 | 凭证与分录 | 事件与资源变化 |
| 业务语义 | 被压缩为科目和借贷 | 完整保留事件、资源、双方角色 |
| 数值校验 | 借贷金额必然平衡 | 通过事件前后资源守恒校验 |
| 审计线索 | 凭证号链式追溯 | 事件链加经济交换关系回溯 |
| 数据冗余 | 大量汇总与对账冗余 | 以明细事件为唯一事实来源 |
| 典型应用 | 财务核算、税务合规 | 业务中台、数据分析、事件溯源 |
从这里可以看到,银行、审计、税务这些场景依然离不开复式记账,这是制度和经济活动本身的要求。但在企业内部的数据架构里,如果所有业务数据都以“凭证+科目”为底座,会让很多与资金无关的业务语义被丢弃得干干净净。我见过一个制造企业,销售系统和财务系统之间所有的数据交换都靠一套“凭证接口”,每次财务月结,两边都为了“在哪张凭证上反应运费”“包装费该进哪个科目”吵得不可开交。这就是典型的模型错配:用管理“钱”的模型去管理“事”,自然处处别扭。
我自己的习惯是:财务相关模块仍然按会计准则出数,报表口径以凭证为准;但业务中台的核心表结构采用 REA 思路组织,把“业务事实”和“财务视图”明确分为两层。业务层关注事件与资源变化,财务层通过物化逻辑生成凭证,两端通过事件 ID 关联。这样既守住了财务合规的底线,又避免了业务语义被会计科目绑架。
5. 把 REA 真正落进数据库:表结构、约束和踩过的坑
理论说清楚了,接下来是落地。这一节我把一套可运行的 REA 风格核心表结构列出来,并结合实际项目经验,说明每个设计决策背后的原因。
5.1 基础表结构
资源、主体、事件三张主表,加上两张关联表,再加一张经济交换关联表,基本上就能覆盖绝大多数业务场景。
-- 资源表:一切有价值、会被事件增减的对象 CREATE TABLE resouce ( id BIGSERIAL PRIMARY KEY, resouce_code VARCHAR(64) NOT NULL UNIQUE, resouce_name VARCHAR(128) NOT NULL, resouce_type VARCHAR(32) NOT NULL, -- INVENTORY, CASH, RECEIVABLE, PAYABLE unit VARCHAR(16), status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE', created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL ); -- 主体表:参与事件的个人、组织或系统 CREATE TABLE agent ( id BIGSERIAL PRIMARY KEY, agent_code VARCHAR(64) NOT NULL UNIQUE, agent_name VARCHAR(128) NOT NULL, agent_type VARCHAR(32) NOT NULL, -- CUSTOMER, SUPPLIER, EMPLOYEE, PARTNER contact VARCHAR(64), status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE' ); -- 事件表:直接改变资源状态的经济动作 CREATE TABLE event ( id BIGSERIAL PRIMARY KEY, event_code VARCHAR(64) NOT NULL UNIQUE, event_type VARCHAR(32) NOT NULL, -- SALE_DELIVERY, PAYMENT_RECEIPT, RETURN... biz_time TIMESTAMPTZ NOT NULL, -- 业务发生时间 record_time TIMESTAMPTZ NOT NULL, -- 系统记录时间 description TEXT, reference_no VARCHAR(64), -- 业务单号,如订单号/合同号 created_by BIGINT ); -- 事件-资源关联表:记录资源数量的增减方向与金额 CREATE TABLE event_resouce ( id BIGSERIAL PRIMARY KEY, event_id BIGINT NOT NULL REFERENCES event(id), resouce_id BIGINT NOT NULL REFERENCES resouce(id), direction VARCHAR(4) NOT NULL, -- IN / OUT quantity DECIMAL(18,4) NOT NULL CHECK (quantity > 0), unit_price DECIMAL(18,4), amount DECIMAL(18,4), remaining_amount DECIMAL(18,4), -- 用于应收账款/应付账款的核销余额 note TEXT ); -- 事件-主体关联表:标记每个事件涉及的各方及其角色 CREATE TABLE event_agent ( id BIGSERIAL PRIMARY KEY, event_id BIGINT NOT NULL REFERENCES event(id), agent_id BIGINT NOT NULL REFERENCES agent(id), role VARCHAR(32) NOT NULL -- BUYER, SELLER, OPERATOR, CARRIER... ); -- 经济交换关联表:把两个互补事件关联成一个业务闭环 CREATE TABLE exchange_link ( id BIGSERIAL PRIMARY KEY, out_event_id BIGINT NOT NULL REFERENCES event(id), -- 资源流出事件 in_event_id BIGINT NOT NULL REFERENCES event(id), -- 资源流入事件 resouce_id BIGINT NOT NULL REFERENCES resouce(id), amount DECIMAL(18,4) NOT NULL, linked_at TIMESTAMPTZ NOT NULL, note TEXT );这套结构的核心思想是“明细即真相”。所有资源余额、往来余额、业务汇总,都可以通过查询事件关联明细计算出来,不需要额外的冗余字段。我在项目里甚至让报表团队直接基于这套明细写 SQL,再也不用担心多口径汇总对不上。
5.2 数据一致性约束:最重要的一条规则
很多第一次做 REA 落地的团队会担心一个问题:没有借贷平衡的校验,数据错了怎么办?我的答案是:用资源守恒替代借贷平衡。每次事件发生后,必须满足“减少的资源总量 + 新增的资源总量 = 0”。比如销售发货事件,必然有库存的 OUT 量等于库存的 IN 量(正数为 0 或退货补差),有应收款的增量等于货款的应计数。在数据库层面,可以通过事务和约束保证:
-- 示例:新增销售发货事件时,库存减少与应收增加必须同时写入 BEGIN; INSERT INTO event(event_code, event_type, biz_time, record_time) VALUES ('EVT20240607001', 'SALE_DELIVERY', NOW(), NOW()); INSERT INTO event_resouce(event_id, resouce_id, direction, quantity, amount) VALUES (currval('event_id_seq'), 101, 'OUT', 10, 5000.00); INSERT INTO event_resouce(event_id, resouce_id, direction, quantity, amount) VALUES (currval('event_id_seq'), 202, 'IN', 1, 5000.00); -- 应收增加 COMMIT;我强烈建议在应用层做这些校验,而不是把所有逻辑压给数据库。数据库的约束适合保证引用完整性和基础非空,但业务规则(“发货必须有两笔资源记录”)更适合在领域服务层统一执行。因为你会发现,业务规则的组合会随着时间变得越来越灵活,硬塞在数据库约束里,改起来成本非常高。
5.3 查询设计的取舍:从明细推余额
这套模型最容易被诟病的一点就是查询麻烦。如果每查一次余额都要扫描全部明细,系统性能会迅速下降。我的应对方案是,在明细表之上建立两层汇总:
第一层是“每日资源余额快照表”。每天定时跑批,按资源、按日汇总所有事件的净增减,更新余额。这一层主要解决业务报表和历史趋势查询,口径固定,性能极好。
第二层是“按资源维度实时累计”的物化视图。如果业务需要实时余额,我会在明细表上维护一个触发器,把变化实时同步到资源余额表。这一层适合“当前库存还剩多少”“当前应收余额多少”这类高频操作。
这本质上是一种 CQRS 的思路:写路径保持规范化的事件明细,读路径按业务需求建立不同的物化模型。我用这套结构支撑过一个每天几百万事件量的系统,查询最复杂的客户往来对账单,也能做到秒级响应。
5.4 实操中踩过的典型的坑
这里可以说说我在实际操作中遇到的问题,给后来者提个醒。
第一个坑是事件表里的“事件类型”越加越乱。业务部门今天要求加“临时借出”,明天要求加“盘盈”,后天又要求加“内部调拨”,开发觉得都是事件,就往枚举里加值。结果事件类型从最初十几个涨到上百个,查询越来越慢,业务语义也开始重叠。后来我定了一条规则:新事件必须能回答“改变了哪些资源”“涉及哪些主体”“和哪个事件构成交换”,三个问题缺一不可,否则就归入已有事件的属性扩展。这个规则把事件类型的增速控制住了,模型质量也稳定了。
第二个坑是时间字段的泛滥。设计时只有一个 biz_time,后来业务方要求“记录一下数据补录时间”,于是加了一个 op_time;再后来财务要求“凭证生成时间”,又加了一个 voucher_time;最后事件表里五六个时间字段,查问题的时候根本不知道哪个时间才是真正的业务事实。正确的做法是,只保留 biz_time 和 record_time 两个字段,其他所有“节点时间”全部作为事件的状态流转记录放到单独的状态变更表里。时间语义一旦混乱,任何数据分析都是在沙滩上盖楼。
第三个坑是经济交换关联漏维护。开发同学把发货事件和收款事件都建好了,但 exchange_link 表一直空着。结果对账的时候,应收账款余额和实际回款对不上。查了半天才发现,核销逻辑没有落到关联表里,而是各自在业务代码里做减法,两边数据一漂移,就会产生一堆逻辑事故。后来我把核销操作统一收口到 exchange_link 表,回款事件生成后必须调用核销服务去匹配待核销的应收资源,保证关联表永远是一条不落地的事实链。
6. 从老模型到新架构:REA 对事件溯源与数据建模的现实启示
很多人不知道,现在流行的“事件溯源(Event Sourcing)”架构,思想内核和 REA 高度相似:以不可变的事件流作为唯一事实来源,系统状态由事件流推导。REA 多了“资源”和“主体”的语义层,而事件溯源更偏基础设施层。如果把 REA 和事件溯源结合用,效果会非常有意思。
我在一个模拟项目中做过这样的探索:把“库存变更”做成领域事件,直接对应 REA 里的 event_resouce 记录;把“客户回款”做成另一个领域事件;然后通过一个投影(Projection)组件,把事件流汇总成资源余额表和应收余额表。新来的需求如果是“算出过去一年平均账期”,不用再造新表,只需要写一个投影逻辑,从事件流里消费数据即可。整套架构的核心表结构依然是那几张 REA 风格的表,只是事件数据的不可变性和重放能力被强化了。
还有一个很多人忽略的价值:REA 可以作为一种“建模沟通语言”来使用,而不只是数据结构。我给一个团队做培训时,用一张白板让业务方画出“一笔完整交易从开始到结束的所有动作”,然后我用“资源-事件-主体”三个标签给每个动作归类,不到半小时,一张清晰的数据流图就出来了。业务方自己也被惊讶到了——他们第一次发现,原来自己天天在处理的核心业务,本质上就是一条由资源增减和主体参与组成的事件链。
这件事给我最大的感触是:很多技术概念并不是新东西,而是换了一身现代的装扮又回到了我们面前。REA 提出的年代,数据库理论还在起步阶段,它没有被广泛应用,主要原因是当时的存储和计算成本太高,企业负担不起以完全明细化方式存储全部业务事件。今天不一样了,存储和计算都极其廉价,事件明细化、可追溯、可重放已经是数据架构的基本要求。这正好让 REA 沉淀了四十年的建模思想重新焕发了生命力。
如果要用一句话总结我的经验,那就是:设计核心业务数据模型时,多想想“这里记录的是不是一次真实发生过的事件”,少想“这里是不是需要一个字段”。事件对了,资源和主体自然清晰,表结构也会稳定得多。REA 不是什么神秘框架,它就是把“真实事件”这条主线放到了模型设计的正中央。我个人现在在做任何新系统设计时,都会先在白板上写一遍 REA 的三个词,再开始动手建表。这个习惯帮我少走了很多弯路,也希望对你有一点启发。