REA建模实战:用资源-事件-参与者重构业务数据模型
做业务系统设计这些年我越来越觉得绝大多数数据模型的混乱不是源于开发能力不够而是从一开始就没有一套能扛住业务变化的底层观察框架。直到我把REA建模这套东西真正用进几个企业项目才对“资源-事件-参与者”这个三角结构的价值有了发自内心的认同。所谓REA就是Resources资源、Events事件、Agents参与者三个词的缩写最早出现在会计信息系统领域的学术讨论中用来回答“企业数据到底应该按照什么结构来组织”这个元问题。这篇内容我会从REA最基本的三个概念讲起用一套小型电商订单系统的完整建模案例把从业务分析到数据库落地的每一步拆给你看同时会把我实际踩过的一些坑和设计取舍一并整理出来。无论你是做ERP、供应链、财税系统还是只是想把某个业务模块的数据模型做得更抗变化这套思路都值得花点时间吃透。1. REA模型用三个词重组业务系统的底层逻辑1.1 REA模型到底解决什么问题做企业级应用的人应该都经历过这种场面财务部门要一套核算报表业务部门要一套流程管理两边提出来的字段名单完全对不上最后数据库里堆了一堆含义重复但命名不同的表。根子上的问题其实是我们在设计数据模型时缺少一个“通用语义层”——一个能同时被财务、业务、技术人员认可的概念框架。REA模型就是这种框架。REA模型最早源于会计信息系统领域的一次学术反思。在80年代初有研究者注意到传统借贷记账法的局限性借贷记账本质上是一套“账户等级”体系它关心的是金额如何平衡而不是业务到底发生了什么。研究者由此提出一个问题如果我们抛开会计科目直接从企业实际发生的经济活动出发来构建系统模型能不能得到更不容易过时、更通用的数据结构答案是肯定的。于是有了“资源-事件-参与者”这套本体框架。REA定义了一个公理式的观察方式企业的一切业务活动本质上都是参与者之间对资源的获取或让渡。凡是符合“资源被某个事件改变、事件由参与者执行”的场景都可以用REA来表达。这个框架不关心你用哪种数据库也不关心你是做销售还是做采购——它只定义“如何看业务”把“如何存数据”的实现留给后续设计。我刚接触REA时的感受是它把业务建模的出发点从“科目”和“表单”拉回到了“事实”。事件是事实资源是事实的标的参与者是事实的责任人。一旦想明白这个三角关系再去设计表结构、接口定义、领域模型思路会清晰非常多。1.2 三个基本实体资源、事件、参与者的三角关系REA把纷繁复杂的业务世界压缩成三个核心实体类型这是一个非常强的抽象。资源Resource指的是企业拥有或控制的、具有经济价值且可被交换的对象。现金、商品、原材料、设备、专利、服务都可以是资源。资源的共同特征是可以被唯一识别、可以被量化度量、可以在不同参与者之间转移。事件Event指的是实际发生的、引起资源状态变化或资源流转的业务活动。例如下订单、发货、收款、采购入库、支付货款这些都是事件。事件是REA模型里连接资源和参与者的中枢节点。参与者Agent指的是参与事件的人或组织包含两类内部参与者比如销售员、仓管员、审批主管外部参与者比如客户、供应商、物流公司。参与者的作用是为每个事件赋予“责任关系”——谁做了这件事、谁和这件事有关。三角关系的核心规则是一个事件至少涉及一个内部参与者如果这个事件涉及外部交换还至少涉及一个外部参与者一个事件必然导致至少一项资源的状态变化要么流入要么流出。这三条规则并不复杂但每一条都在约束数据模型的结构。打个生活化的比方把REA想象成拍电影。事件是“镜头”它记录某个时点发生了什么资源是“道具”拍摄中道具被使用、被消耗、被转移参与者是“演员和工作人员”每个镜头都有谁在场、谁负责。会计科目相当于电影字幕里的“场记单”只能告诉你第几场拍了什么却丢掉了很多现场的真实信息。REA要做的就是保留镜头本身。1.3 REA模型解决的核心矛盾我认为REA模型真正解决的核心矛盾是“业务变化速度”与“数据结构调整成本”之间的矛盾。传统基于科目和单据建模的系统每增加一种业务类型往往要增加表字段、调整统计逻辑、甚至改动核心表结构。而REA把模型的核心锚定在资源、事件、参与者三个基础概念上业务再怎么扩展这三个维度不会变。新业务进来无非是往事件表里增加一种事件类型而已。这种长期可复用性的价值在跨部门协作和异构系统集成的时候体现得尤其充分。我参与过一个把销售、采购、库存、财务四套系统做数据打通的模拟项目X当时就是靠把各系统的核心业务统一映射到REA模型上才让不同团队找到了共同的对话语言。财务说的“应收”在REA里就是“销售事件带来的资源索取权”库存说的“出库”就是“商品资源的流出事件”。语义对齐之后接口设计就顺了。这个理念和领域驱动设计中的聚合根思想有异曲同工之处都是强调核心业务对象要稳定外围变化被隔离。只不过REA明确给出了“哪些东西才是核心”的判断标准——资源、事件、参与者这三个词就是一套现成的筛选器。2. 深入拆解REA核心实体与关系规则2.1 资源比“库存”更广阔的语义很多人一看到“资源”就联想到仓库里的存货这是对REA最普遍的误解。资源在REA中的定义是能够被企业识别、控制并且具有交换价值的对象。它至少包括三类。第一是有形资产比如库存商品、固定资产、现金。第二是无形资产比如软件许可、品牌授权、专利。第三是服务型资源比如运输服务、售后维护服务。服务类资源虽然不常被独立对待但在以服务为核心的行业比如物流、咨询里它往往是REA建模的主战场。区分一个对象是否属于资源我常用三个问题自测它能否被唯一标识例如“客户满意度”很难唯一标识不适合当资源。它能否被量化度量例如“商誉”虽然有价值但很难精确度量通常不直接建模为资源。它能否在企业之间转移或交换例如“员工的技能”虽然有价值但如果无法在企业间交易就不适合作为REA资源实体。这里有一个设计上的细节值得注意资源实体应当保持“业务语义”的干净不要把系统层面的技术编号一股脑塞进来。比如为了让同一种商品支持按批次的库存追踪我们可能会在资源子类里拆出“批次”概念但这属于实现层面的设计不要一开始就混在REA模型里。先把业务语义理清楚再考虑技术落地的复杂度。2.2 事件业务事实的记录单元事件在REA中是最具描述力的实体它的本质是“发生了什么的记录”。要完整描述一个业务事实事件需要包含时间、涉及资源、参与者和具体的数量金额信息。在我看来事件设计得好坏直接决定整个REA模型的表达力。判断一个候选“事件”是否合格可以看两个重要特征。原子性是第一个特征。事件是一个不可再分的业务动作。比如“客户下单”是一个事件“订单审核通过”是另一个事件它们不能揉在一起。如果一个动作可以被拆成多个决策点就应该拆成多个事件。双向性是第二个特征。在完整的业务循环里资源流入和资源流出事件通常是成对出现的。例如“销售出库”是商品资源的流出事件对应“客户付款”是现金资源的流入事件“采购入库”是商品资源的流入事件对应“向供应商付款”是现金资源的流出事件。这对互补事件共同构成了一个完整的经济循环。在设计事件时有一个常见陷阱直接采用企业现有单据作为事件。比如企业有一张“销售发货单”可能同时包含了“确认订单”“出库”“发货”三个动作但实际上它们是三个独立事件。物理操作节点和业务决策点经常不是一回事这一点不处理好后面的系统就会被迫做很多后补操作。2.3 参与者角色化与内外分离参与者实体代表卷入事件的人或组织。它看似最简单却最容易在设计上埋雷。第一内部参与者与外部参与者必须分开建模。客户、供应商属于外部参与者它们不会因为企业内部组织调整而变化销售员、仓管员、审批员属于内部参与者它们会随着岗位流程的变化而变化。把两类参与者混在一张表里后续做权限、做统计时都会很别扭。第二参与者要尽量做“角色化”抽象而不是直接绑死具体的人。实务中一个员工可能既是销售合同的审批人又是某个商品的维护负责人一个客户可能既是销售事件的买方又是售后事件的报修方。如果直接把“人”作为参与者实体这些不同角色对应的权限和责任就纠缠不清。更稳的做法是定义“角色”作为关联实体让角色连接事件再把具体的人挂到角色上。这样做还有一个额外的好处当人员发生变动时只需要调整“人-角色”的映射事件相关的历史记录不需要动。对业务流程重组、权限变更频繁的企业来说这个设计能让系统寿命长很多。2.4 实体间关系的建模规则REA模型的关系规则可以用几条固定的线来描述。资源流关系事件导致资源的流入或流出。一条资源流关系连接一个事件和一个资源并包含数量、单价等信息。事件对关系一个完整的业务循环由两个互补的事件组成一个代表经济资源的流入另一个代表经济资源的流出。这是REA与普通E-R模型最核心的区别之一REA增加了“事件对”这种语义约束。参与者关系事件与内部参与者、外部参与者之间的关联。任何事件至少涉及一个内部参与者涉及外部交换时至少涉及一个外部参与者。有了这几条关系的约束REA模型就不是一张随意画出的E-R图而是一套有业务语义规则约束的本体描述。这意味着当你拿到一张REA模型图时即使不熟悉具体业务也能根据关系类型推断出大致的业务过程。3. 为什么用REA与传统记账和建模方式对照3.1 传统会计逻辑的两个先天局限传统会计系统是围绕“账户”组织的钱怎么流动都要转成借方和贷方。这种设计并非没有道理它保证了账务的平衡与可审计性但把它直接当成业务系统的主结构时两个局限就暴露出来。第一个局限是“广度缺陷”会计账户只记录金额不记录业务参与者的具体动作。比如一笔销售会计记录的是“主营业务收入增加、库存商品减少”但客户是谁、销售员是谁、订单什么时候成交的这些信息不会出现在会计分录里。可在真实的业务系统中这些往往是核心查询条件。第二个局限是“演进缺陷”会计科目是人为定义的分类体系不同公司有不同的科目表业务一变化科目就要跟着调整。基于科目的数据模型缺乏一个不随业务改变的长期底座导致系统经常要做结构性调整。REA之所以被提出来本质上就是想在科目之上给企业数据一个不变的概念底座。3.2 REA与E-R图的关系语义层次的差异很多做过数据建模的人会问REA模型和E-R模型到底什么关系它们并不在同一个层次。E-R模型实体-关系模型是一种通用的数据库建模工具它告诉你如何把现实世界中的“实体”和“关系”转化为表结构而REA是对E-R模型的一种“约束规则集”它进一步限定了实体和关系的类型及其业务含义。换句话说你用E-R工具可以画出客户订单、商品、库存、财务凭证等任何内容但E-R本身不告诉你哪些实体是资源、哪些是事件、参与者之间必须满足哪些业务规则。REA就是在E-R的基础上加了一层“业务本体约束”让模型天然带有业务语义。REA模型最终也会转化为E-R图或物理表结构但转化前的业务分析逻辑已经完全不同。我个人的体会是直接画E-R图的人往往先想有哪些表而用REA建模的人先想业务里有哪些事件、资源、参与者再考虑怎么落表。前者是“用表驱动业务”后者是“用业务驱动表”思路先后不同最终模型的可维护性差异非常大。3.3 用REA建模的实际收益从纯技术角度看REA建模带来的最直接收益是数据表的数量更少而且演进时更省力。以某个销售场景为例如果用传统方式建模你可能需要客户表、商品表、订单表、订单明细表、收款表、发货表等多张业务表。这些表之间虽然能关联但每张表都自带一套状态。而REA模型会把“订单”“收款”“发货”全部建模为事件围绕事件建立统一的时间线和状态机查询历史时只需要沿着事件序列推进即可。从业务角度看REA让数据分析的口径更容易对齐。财务、运营、仓管只要认同一套“事件”定义后续做经营分析的时候就不用反复争论“销售额到底应该以哪个表为准”。这种共识的价值在跨部门开会时尤为明显。还有一个我很看重的收益REA天然支持审计追溯。因为事件是不可篡改的业务事实记录配合参与者和资源流的双重标注任何一个资源状态变化都能逆向追溯到具体的业务动作和责任人。对满足合规审计要求的企业来说这几乎是刚需。4. 实操指南用REA设计一个电商订单系统4.1 场景与需求梳理为了把概念落到地上我设计了模拟项目X一个典型的小型B2C电商订单系统。业务需求包括客户可以浏览商品并下单仓库对订单进行发货客户在发货后确认收货并支付款项财务对收款进行记录需要支持订单状态的查询和销售报表的统计。在动手建任何表之前第一步是明确这个系统的核心业务循环。从REA视角看这个系统存在两组互补事件对。事件对一销售出库商品资源流出加客户付款资金资源流入。事件对二采购入库商品资源流入加向供应商付款资金资源流出。如果系统规模小、只做终端销售可以先不建模采购侧但完整的企业系统里这两组都是必需的。这里为了演示完整我把采购侧也纳入设计。4.2 第一步识别业务事件识别事件是REA建模最考验经验的一步。我先从业务流程中把所有“动作”列出来再逐个做原子性判断。比如客户下单这个场景可以拆分成客户提交订单订单事件、仓库审核并出库出库事件、物流发货发货事件、客户确认收货收货事件、客户付款收款事件、财务核对并开票开票事件。再比如采购场景提交采购申请、向供应商下单采购事件、供应商发货、仓库收货采购入库事件、财务向供应商付款付款事件。实际操作中我不会一开始就追求事件列表绝对正确而是先列全再根据“是否有独立的业务决策点”做裁剪。比如“客户提交订单”和“订单审核通过”是两个决策点就保留两个事件如果审核流程只是形式审批可以合并成一个“订单确认”事件。事件粒度的取舍在第5部分会详细展开。4.3 第二步标注资源与参与者事件识别完之后对每个事件标注它涉及到的资源类型和参与者。销售侧的事件标注结果订单事件资源为商品意向资源、应收款潜在资源参与者为客户、销售员。出库事件资源为商品流出参与者为仓管员、客户收货方。付款事件资源为资金流入参与者为客户、财务员。采购侧的事件标注结果采购事件资源为商品意向流入参与者为供应商、采购员。入库事件资源为商品流入参与者为供应商、仓管员。付款事件资源为资金流出参与者为供应商、财务员。标注过程中我发现资源在流入和流出两个方向上应该分别命名。例如“商品”资源在销售侧是流出、在采购侧是流入但资源实体本身不区分方向方向由事件类型表达。这一点很重要不要在资源表里加“流出/流入”字段方向是事件关系的一部分不是资源属性。4.4 第三步连接关系并标注基数接下来开始画REA关系图。这个过程的核心是标基数因为基数决定了后续物理表的外键设计。以销售出库与客户付款两个事件为例销售出库事件资源流出到商品资源一个出库事件对应多个商品明细基数 1:N。客户付款事件资源流入到资金资源一个收款事件对应一笔资金入账基数 1:1。销售出库事件到客户付款事件事件对一笔销售出库可以分多次付款也可能多笔销售合并一次结算因此基数 M:N。销售出库事件到客户参与者一个出库事件对应一个客户基数 N:1。销售出库事件到仓管员参与者一个出库事件对应一个仓管员基数 N:1。做完之后我还专门比对了一下“事件对”的基数是否贴合业务真实情况。很容易犯的错误是默认“一笔出库对应一笔收款”但现实中完全可能一笔出库在后期被分三次收款追回。标基数时一定要贴着实际业务走而不是抄模板。4.5 第四步映射到数据库表结构当REA关系图定稿后转成数据库表就是水到渠成的事。我通常遵循以下映射规则每个资源实体映射为一张资源表如商品表、资金账目表。每个事件实体映射为一张事件表如订单表、出库表、收款表。每个参与者实体映射为一张参与者表角色单独建一张角色表。事件与资源的关联如果需要携带数量、单价等属性就映射为关联表字段中包含事件ID、资源ID、数量、金额。事件对关系通常不在物理表中单独建关联表而是通过事件ID互相引用如销售订单关联出库单、出库单关联收款单来实现。下面给出一个简化的表结构设计以销售侧为例-- 资源表商品 CREATE TABLE product ( product_id BIGINT PRIMARY KEY, product_name VARCHAR(255) NOT NULL, unit_price DECIMAL(10,2) NOT NULL, is_active TINYINT DEFAULT 1 ); -- 参与者表客户外部参与者 CREATE TABLE customer ( customer_id BIGINT PRIMARY KEY, customer_name VARCHAR(255) NOT NULL, contact_info VARCHAR(500) ); -- 参与者表员工内部参与者 CREATE TABLE employee ( employee_id BIGINT PRIMARY KEY, employee_name VARCHAR(255) NOT NULL, role_id BIGINT ); -- 事件表销售订单 CREATE TABLE sales_order ( order_id BIGINT PRIMARY KEY, order_no VARCHAR(64) NOT NULL UNIQUE, order_time DATETIME NOT NULL, customer_id BIGINT NOT NULL, salesperson_id BIGINT NOT NULL, order_status INT NOT NULL DEFAULT 0 ); -- 关联表销售订单明细订单事件到商品资源 CREATE TABLE sales_order_item ( order_id BIGINT NOT NULL, product_id BIGINT NOT NULL, quantity INT NOT NULL, unit_price DECIMAL(10,2) NOT NULL, PRIMARY KEY (order_id, product_id) ); -- 事件表收款 CREATE TABLE payment ( payment_id BIGINT PRIMARY KEY, payment_no VARCHAR(64) NOT NULL UNIQUE, order_id BIGINT NOT NULL, payment_time DATETIME NOT NULL, amount DECIMAL(10,2) NOT NULL, payer_id BIGINT NOT NULL, receiver_id BIGINT NOT NULL );这张简化表里payment表通过order_id引用sales_order表就体现了“收款事件”与“销售订单事件”之间的事件对关系。实际项目中会补充分期付款相关字段比如payment_seq记录第几次分期。4.6 验证用REA模型回答业务问题表结构设计完成之后我一贯的做法是拿真实业务问题来验证模型是否完备。我会把团队的常见分析需求列成一张清单逐个检查能否用已设计的表回答。需求1本月销售额是多少通过sales_order的order_time加order_status筛出有效订单汇总订单金额即可。需求2某商品还有多少库存需要结合采购入库事件与销售出库事件用资源流量的总和计算。这也是REA资源流的天然能力库存等于流入总量减流出总量。需求3某个客户的历史交易记录通过customer_id关联sales_order和payment按时间排序即可。需求4某笔订单的回款进度通过payment表按order_id聚合对比sales_order金额即可。我用这个清单核对后发现基本不需要额外加表就能回答90%以上的业务问题。一旦出现无法回答的问题通常意味着某个资源或事件没有被建模进去这时我就知道模型存在缺口。4.7 模型迭代与扩展REA模型的可扩展性在这个电商系统的演化中体现得比较充分。比如系统后来增加了促销活动订阅、优惠券、积分抵扣等功能按照传统方式可能要加一堆状态字段和流程分支。但在REA里我只需要把“优惠券使用”“积分扣减”建模为新的资源变动事件挂接到对应的事件上即可核心的表结构完全不用动。再比如增加多仓库发货也不需要改动订单事件本身只需在出库事件上增加仓库维度的参与者引用。从这个角度看REA极其擅长隔离变化让模型在业务演进过程中始终保持清晰的结构这也是我后来在新项目里坚持先画REA图再写代码的原因。5. 避坑经验与常见问题速查5.1 五个常见建模误区误区一把单据直接当事件。前面提过单据往往包含多个业务决策点直接当事件会导致后续状态混乱。我自己的经验是每个“审核、打印、确认”按钮对应的都应该是独立事件而不是简单的字段更新。误区二把参与者设计成一张大表。客户、供应商、员工全在一张表里虽然省事但后续做权限模型时非常痛苦。灵活的做法是先区分内外参与者再针对内部参与者做角色化。误区三在资源表中写方向字段。比如某些设计会往商品表里加“is_incoming”“is_outgoing”这是把关系误放进了实体里。方向应该是事件关系属性资源本身没有方向。误区四忽略事件对关系。只顾着建事件表没有把“流入事件”和“流出事件”之间的互补关系表达清楚导致做不出准确的经营分析。事件对关系一般用关联ID或业务外键表达设计时必须刻意保留。误区五在REA模型里夹带实现细节。比如把缓存标志位、接口调用结果、临时状态都作为实体混进来。REA是业务模型不是技术实现模型混在一起会让模型的长期可用性大打折扣。5.2 事件粒度的三个判断维度事件粒度是最让初学者纠结的。我总结出三个判断维度。第一个维度是“决策点”。如果一个动作发生后责任人需要做出新的决定比如接受、拒绝、批准、确认这个动作就应该是一个独立事件。例如“订单审核通过”是一个决策点“发货”是另一个决策点。第二个维度是“信息粒度”。如果需要为某个动作单独记录一组属性比如发货包含快递单号和物流公司就应该将其拆分为独立事件。如果只是更新主事件的一个状态字段那就不要拆。第三个维度是“时间敏感性”。如果一个动作与另一个动作的发生时间可能相差很大就应该分事件。比如“下单”与“付款”之间可能隔两周必须分成两个事件。按这三个维度过滤一遍事件列表一般不会出大问题。如果事件还是拆得很碎往往说明业务本身需要更高的追踪粒度这种情况下多拆比少拆更稳妥。5.3 “状态”字段怎么处理在REA模型中最容易被问到的就是“状态字段放哪”。比如订单的状态待审核、已通过、已发货、已完成在很多传统设计里是订单表里的一个status字段。但在REA视角里状态不应该是一个单独的字段而应该由事件序列推导出来。具体做法是用订单对应的最新事件类型来推导状态。比如如果订单事件之后存在“审核通过”事件则该订单处于“已审核”状态如果之后又存在“出库”事件则订单推进到“已发货”状态。这样做的好处是状态的变化全部有事件记录作为依据可审计、可追溯不会出现“状态被直接改成已完成但没有操作记录”的尴尬局面。当然完全不用状态字段在性能上是不可行的很多系统还是会冗余一个状态字段方便快速查询。我的建议是数据库里可以冗余状态字段用于查询优化但状态值必须由事件序列同步生成不能接受前端直接改状态。用事件驱动状态更新保证数据一致性。5.4 与主流开发框架协同的经验把REA模型落到实际开发需要和具体的开发框架做衔接。用过几种主流方案我提炼几个要点。如果使用领域驱动设计风格REA的事件实体可以直接映射为领域事件参与者映射为聚合根的成员。一个聚合可以围绕“销售订单”这个事件展开资源流关联的商品明细作为聚合内容。但要注意不要把REA事件等同于领域驱动设计中的领域事件——REA事件是业务事实记录领域驱动设计中的事件更多关注边界间通信二者属于不同粒度。实际项目中我会用REA建模指导聚合划分再用领域驱动设计落地。如果使用关系型数据库加ORM框架建议按REA模型来设计表不要因为ORM链式查询方便就随意增加冗余表。每一个实体对应一张表事件对关系通过外键显式表达。这样即使团队后续换人只看表结构也能还原业务过程。如果系统需要做数据分析报表可以先把REA模型作为数据仓库星型模型的设计来源。资源、事件分别作为维表和事实表参与者作为维度属性。这样ETL的过程会非常自然。5.5 我的几条实操心得最后分享几条我亲测有效的经验。第一条先画REA图再画E-R图。REA图是业务的抽象E-R图是数据的整理两者之间不是替代关系而是先后关系。每次新项目开工我都会花一两个小时把核心业务映射到REA框架上这比任何需求评审都更能暴露业务盲区。第二条事件的时间戳一定要用业务时间而不是系统时间。系统时间不可靠且不具备业务语义。客户可以在3月1日提交订单到3月5日仓库才实际出库。如果用系统时间记录分析报表的日期口径就错了。第三条不要把客户、商品、订单等所有信息都塞进一个大而全的“主数据表”。REA的精神是分离关注点资源、事件、参与者彼此独立才能在业务变化时各自演进。我曾经见过一个系统把所有业务数据都挂在订单表上导致订单表膨胀到上百列后续每一次需求变更都牵一发动全身。这个教训值得每个做业务系统的人记在心里。我个人这几年最大的感受是REA建模带给我的不是一套固定模板而是一种“看见业务”的方式。以前画数据模型我第一反应是“要建哪几张表、表里有什么字段”现在第一反应是“这个业务里有几个关键事件、这些事件关联哪些资源和参与者”。看似只是思维顺序的变化做出来的系统长期可用性完全是两个级别。如果你正在做一个业务逻辑复杂、变化频繁的系统不妨先用REA把业务核心梳理一遍再回来设计表结构你大概率会和我一样少走不少弯路。最后再分享一个小技巧接手一个老项目时先试着用REA把现有表结构反向建模一遍你会发现很多表根本不属于资源、事件、参与者中的任何一类它们其实是“临时状态表”或“报表缓存表”。能识别出这些外围表你就知道哪些数据结构是核心、哪些是枝叶后续重构的优先级自然就清楚了。这个方法实测非常管用推荐给你们。