Day32-10|13.4.1 5种数据访问模式:在封装、性能与一致性之间做架构取舍

对应教材:《系统架构设计师教程(第2版)》第13章“层次式架构设计理论与实践”13.4“数据访问层设计”中的 13.4.1“5种数据访问模式”。本文面向软考高级系统架构设计师,重点学习常见数据访问模式及其职责边界、适用条件、事务策略、性能影响和案例答题方法。

一、教材定位与学习目标

数据访问层是业务逻辑层与数据库、文件系统、缓存和其他数据源之间的隔离边界。它负责把业务层的查询和保存意图转换为具体的数据访问操作,同时隐藏连接管理、SQL、ORM、事务、映射和存储细节。数据访问层设计不合理,常见结果包括:业务代码到处拼接 SQL,数据库表结构泄漏到接口;每个模块都自行管理连接和事务;简单查询被复杂 ORM 拖慢;缓存和数据库数据不一致;批量任务占用在线事务资源。

教材所讲的五种数据访问模式,核心目的是让架构师理解不同抽象方式的优缺点,而不是死记模式名称。不同模式在对象粒度、数据粒度、事务范围、复用能力和性能上各有取舍。考试可能以“为某企业应用设计数据访问层”为背景,要求比较数据访问模式、选择适合的模式,或者分析直接访问数据库带来的问题。

今天的学习目标:

  1. 理解数据访问模式解决的主要问题和适用边界。
  2. 掌握数据访问对象、数据映射器、活动记录、表数据网关和行数据网关等典型思想。
  3. 根据领域复杂度、查询特点、事务边界和性能要求做选择。
  4. 设计连接、事务、缓存、批量和异常治理,避免数据层职责泄漏。
  5. 能够结合案例题和论文说明数据访问模式的架构决策。

二、数据访问层的基本职责

数据访问层通常负责连接和资源管理、查询与更新、对象和记录映射、事务参与、分页与批量、并发控制、数据源路由、缓存协作以及存储异常转换。它应向上提供稳定、面向业务或领域的访问抽象,向下适配关系数据库、NoSQL、文件、搜索引擎和远程数据服务。

数据访问层不应该决定业务流程和核心政策。例如“订单是否允许取消”属于业务规则;“如何根据索引查询订单”属于数据访问。数据访问层可以执行数据库约束和数据完整性检查,但不能把所有业务规则寄托在触发器和存储过程中,否则规则难以复用和测试。

三、五种数据访问模式概览

不同教材和资料对模式名称的翻译可能略有差异,考试答题时应同时说明核心思想。本文采用以下五类常见模式:

模式 核心粒度 主要思想 典型适用场景
行数据网关 一行记录 一个对象封装一行数据及其访问 简单表、CRUD、业务规则少
表数据网关 一张表 一个对象封装表级查询和更新 多记录操作、传统企业应用
活动记录 一行与领域对象结合 对象既表示数据又负责保存和业务行为 领域简单、对象与表较接近
数据映射器 独立映射层 领域对象与数据库模型彼此隔离 领域复杂、模型差异大
数据访问对象 访问职责独立 为某类数据或聚合提供访问接口 分层系统、服务化和多数据源

这五种模式并非互斥。在一个系统中,复杂核心域可以使用数据映射器和数据访问对象,简单配置表可以使用表数据网关,读模型可以使用专用查询对象。模式选择应以业务和质量属性为依据。

四、行数据网关

1. 基本思想

行数据网关把数据库中的一行记录封装为一个对象,提供读取、修改、插入和删除等操作。它通常与表结构比较接近,每个网关对象对应一条记录,适合简单的业务对象和 CRUD 场景。

2. 优点

行数据网关结构直观,代码容易理解,能够把字段访问和 SQL 集中起来,避免调用方直接拼接 SQL。对于业务规则少、表结构稳定的后台管理功能,开发速度较快。

3. 缺点

如果一次需要处理大量记录,为每一行创建对象和执行单独操作可能产生 N+1 查询和较高开销。若网关对象逐渐加入大量复杂业务规则,就会与活动记录或领域实体混合,边界不清。跨表业务也可能需要大量对象协作和事务控制。

4. 适用场景

适合字典、简单配置、单条记录管理和规则较少的后台页面。对于高并发批量写入、复杂报表和复杂领域聚合,应结合批量 SQL、查询模型或其他模式。

五、表数据网关

1. 基本思想

表数据网关以一张表或一个数据集合为访问单位,集中提供插入、更新、删除、按条件查询和批量操作。调用方不需要为每条记录创建独立网关对象,适合面向表的操作。

2. 优点

表数据网关有利于统一 SQL、分页、批量和连接管理,处理多行数据效率较好。传统企业应用和报表型系统中,许多操作本来就是以表或结果集为中心,采用该模式较为自然。

3. 缺点

表数据网关容易让业务代码以数据库结构为中心,复杂业务规则可能散落在调用方。一个网关承担过多查询和更新方法时,接口会膨胀;不同业务场景共享同一网关,还可能出现权限和事务边界不清。

4. 适用场景

适合简单管理系统、批量处理、数据交换和读多写少的查询服务。对于领域模型与数据库差异较大的系统,不宜让表网关直接成为业务对象的唯一抽象。

六、活动记录

1. 基本思想

活动记录把数据对象和数据访问行为放在同一个对象中。对象既包含字段和业务属性,也提供保存、删除、查找等方法。它常见于对象与数据库表结构接近、业务规则不太复杂的应用框架。

2. 优点

活动记录使用简单,开发者可以直接通过对象完成查询和保存,适合快速开发和 CRUD。对象具有一定的业务行为,比纯数据记录更容易理解。对于中小型管理系统,活动记录可以降低样板代码。

3. 缺点

活动记录将领域行为和持久化行为耦合,数据库模型变化容易影响业务对象。复杂聚合、多数据源、跨表一致性和高性能查询会让对象越来越臃肿。对象被序列化到接口时,还可能把保存方法和内部字段暴露出去。

4. 适用场景

适合领域简单、表对象一一对应、生命周期相对短的系统。复杂核心域可以局部使用活动记录承载简单对象,但应通过应用服务和接口模型防止持久化细节外泄。

七、数据映射器

1. 基本思想

数据映射器是独立于领域对象和数据库对象的层,负责在两种模型之间转换。领域对象不需要知道数据库表、SQL和连接;数据库对象也不承载完整业务行为。映射器可以处理字段转换、集合、值对象、继承、聚合和版本。

2. 优点

数据映射器能够较好地隔离领域模型和持久化模型,适合业务规则复杂、聚合边界清晰、数据库模型与领域模型差异大的系统。数据库可以替换、分库或优化,而不必把变化扩散到领域层。

3. 缺点

映射代码和配置较多,开发和调试成本高;复杂关系可能产生加载、级联、懒加载和性能问题。若映射器隐式执行大量查询,容易出现不可见的 N+1 问题。

4. 适用场景

适合核心交易、复杂规则、长生命周期和需要长期演进的领域。采用时必须设计加载边界、保存策略、并发版本和查询模型,不能只依赖 ORM 默认行为。

八、数据访问对象

1. 基本思想

数据访问对象(DAO)为某类数据、聚合或访问场景提供独立接口,把数据访问实现隐藏起来。业务层依赖 DAO 或仓储抽象,DAO 可以使用 SQL、ORM、远程服务、缓存或多种数据源完成访问。

2. 优点

DAO有利于分层、替换和测试。业务组件可以使用模拟DAO进行单元测试;数据访问实现可以根据场景使用关系数据库、搜索引擎、NoSQL或读副本。DAO还可以集中连接、查询、异常和事务参与。

3. 缺点

如果DAO只按表机械生成,可能只是对表网关换了一个名字;如果接口暴露大量数据库字段和通用查询参数,业务层仍然被存储模型绑架。DAO之间的事务协作、缓存一致性和跨数据源访问也需要额外治理。

4. 适用场景

适合分层企业应用、服务化系统、多数据源和需要测试替换的场景。复杂领域可以让DAO面向聚合或查询用例,简单场景则可以面向表或记录。

九、模式比较与选择方法

1. 按领域复杂度选择

业务规则少、数据结构简单时,表数据网关、行数据网关或活动记录可以快速交付;业务规则复杂、状态多、聚合和不变量重要时,优先考虑数据映射器、仓储和领域对象。

2. 按读写特点选择

单条写入适合行级或实体访问,批量写入适合表级网关和批量命令;复杂报表适合专用查询对象、SQL或读模型,不应强行通过领域实体加载。读写分离时,要考虑数据同步和权限。

3. 按数据库与领域模型差异选择

两者接近时,活动记录简单有效;两者差异明显时,数据映射器提供更好的隔离;数据库只是多个数据源之一时,DAO或仓储可以对上层隐藏来源。

4. 按质量属性选择

性能敏感场景需要关注 SQL 可控性、批量、索引和网络往返;可维护性要求高的核心域需要稳定抽象和明确业务边界;可测试性要求高的系统需要依赖注入和接口替换;数据安全要求高时,需要在访问层统一权限过滤、脱敏和审计。

选择条件 较合适的模式 主要注意点
简单CRUD 活动记录或表数据网关 防止业务规则散落
单条业务对象 行数据网关 避免N+1和对象臃肿
复杂领域模型 数据映射器 + DAO/仓储 控制映射和加载边界
多数据源 DAO/仓储 统一异常、事务和路由
大批量处理 表级访问或专用批处理 与在线事务隔离
复杂报表 查询对象/读模型 权限、时效和索引

十、事务、缓存与并发治理

1. 事务

数据访问模式不能自行决定全部事务。应用服务通常确定业务用例的事务边界,数据访问组件参与事务。单个聚合内使用本地事务,跨系统操作采用事件、状态和补偿。DAO内部私自提交事务会破坏上层一致性控制。

2. 缓存

缓存可以位于DAO内部,也可以由独立缓存服务提供。必须定义缓存键、失效、更新时机、数据权限和故障降级。租户、用户和敏感数据不能因错误缓存键互相泄露。写入数据库后如何更新或删除缓存,要结合一致性要求选择旁路缓存、写穿、失效通知或异步更新。

3. 并发

实体更新可使用版本号和乐观锁,库存等高冲突资源可以使用队列或锁。批量更新要避免覆盖在线修改,查询和更新条件应明确。数据访问层返回版本冲突和约束失败时,应用层应转换为可理解的业务处理。

4. 连接和资源

连接池大小、超时、事务隔离、游标、批量大小和资源释放要统一治理。慢查询、锁等待、连接泄漏和失败重试应有监控。重试必须考虑 SQL 是否幂等,不能对所有数据库异常无条件重试。

十一、案例分析:企业合同管理系统

某合同系统包含合同录入、审批、签署、归档和全文检索。原系统采用活动记录,合同对象直接对应数据库表,审批规则和数据访问混在对象方法中;合同全文查询又通过同一对象访问关系库,导致列表慢、对象加载复杂,数据库升级时影响多个模块。

1. 问题分析

合同核心领域包含版本、审批状态、签署和归档不变量,与数据库表结构并不完全相同;全文检索属于读模型和搜索场景,不适合通过合同实体逐条加载;活动记录承担过多规则和访问职责,无法清晰控制事务和权限。

2. 目标方案

合同聚合采用领域对象和数据映射器,仓储负责按合同编号加载和保存聚合;应用服务负责审批、签署和归档用例;合同列表采用专用查询DAO,全文查询使用搜索索引;字典和简单配置继续使用表数据网关。关系数据库映射模型与领域模型分离,搜索数据通过合同变更事件异步更新。

3. 事务和一致性

合同状态和版本在本地事务中更新,签署平台调用采用状态机和幂等编号;合同更新后发布事件,搜索索引最终一致;审批动作校验用户、组织、合同版本和当前状态;查询DAO执行字段级和组织级权限过滤。

4. 迁移路线

先将合同审批和签署规则从活动记录中抽取为应用服务和领域对象,保持旧表结构不变;再引入映射层和仓储,迁移读写入口;随后建设查询DAO和搜索索引;最后清理活动记录中的重复规则和不再使用的访问方法。每阶段通过双读、数据对账和回滚开关控制风险。

5. 指标

通过合同审批规则重复率、列表响应时间、全文查询成功率、数据库变更影响模块数、状态非法变更数、搜索同步延迟和迁移期间数据对账差异评价效果。

十二、综合知识练习与详细答案

练习一:单项选择

适合复杂领域模型并隔离领域对象与数据库模型的数据访问模式是( )。

A. 数据映射器 B. 直接表访问 C. 页面控制器 D. 客户端缓存

答案:A。数据映射器独立负责两种模型转换,能够降低数据库结构对领域模型的影响。

练习二:多项选择

选择数据访问模式时通常需要考虑( )。

A. 领域复杂度 B. 读写特点 C. 事务边界 D. 性能要求 E. 仅看开发人员熟悉程度

答案:A、B、C、D。人员熟悉程度可以作为约束,但不能作为唯一依据。

练习三:案例题

某系统所有业务模块都调用一个通用 DAO,DAO 提供任意表名、字段名和条件拼接功能。系统出现 SQL 注入、权限绕过、慢查询和业务规则散落。请分析问题并提出改进方案。

参考答案:通用DAO暴露了数据库结构和任意查询能力,业务层可以绕过权限和业务边界;字符串拼接带来SQL注入;查询缺少索引和场景约束导致慢查询;DAO不表达业务意图,规则只能散落在调用方。改进时应按业务能力、聚合或查询用例设计受控的数据访问接口,禁止任意表名、字段名和SQL拼接;使用参数化查询、字段白名单和租户过滤;将核心规则放入应用服务和领域对象;复杂报表采用专用查询DAO或读模型;统一事务、连接、超时、缓存和审计;通过代码评审、静态扫描、契约测试、性能测试和权限矩阵测试验证。

十三、案例题答题模板

遇到“数据访问模式、DAO、ORM、数据库访问层”类题目,可以按以下顺序作答:

  1. 先说明现状:业务代码直连数据库、模型泄漏、规则重复或性能不可控。
  2. 分析领域复杂度、读写特点、数据库与领域模型差异和一致性边界。
  3. 选择并组合合适模式,说明每种模式的职责、优点和限制。
  4. 设计受控接口、对象/记录映射、事务、并发、缓存和批量处理。
  5. 加入参数化查询、权限过滤、租户隔离、脱敏和审计。
  6. 对复杂查询采用专用查询对象或读模型,避免强行加载领域实体。
  7. 给出迁移、双读、对账、灰度和回滚措施,并用指标验证效果。

十四、论文写作素材

开头素材

“原有合同管理系统采用活动记录方式,领域规则、数据库访问和外部签署调用高度耦合,导致数据库结构变化影响范围大,复杂查询性能不足,且不同入口存在状态校验不一致。为提升系统的可维护性和性能,我参与重新设计数据访问层,根据核心领域和查询场景组合采用数据映射器、仓储、专用查询DAO和表数据网关。”

正文素材

可以重点写四点:第一,如何根据领域复杂度和读写特点选择不同数据访问模式,而不是全系统强制一种模式;第二,如何通过映射器和仓储隔离领域模型与数据库模型,保护聚合边界和事务;第三,如何采用查询DAO、批处理、缓存、索引和读模型解决性能问题;第四,如何统一连接、异常、权限、租户、审计、并发和迁移治理。要写出模式取舍和真实指标。

结尾素材

“实践表明,数据访问模式没有脱离场景的绝对优劣。简单功能追求开发效率,复杂核心域重视模型隔离和一致性,报表与批处理重视查询性能和资源隔离。只有把模式选择与业务边界、质量属性和治理机制结合起来,数据访问层才能真正降低系统的长期复杂度。”

十五、今日复盘

本节核心结论是:行数据网关和表数据网关偏向数据操作,活动记录把数据与访问结合,数据映射器强调领域与存储隔离,DAO提供受控访问边界;模式选择取决于领域复杂度、读写特点、事务和质量属性。

请回答:

  1. 五种数据访问模式分别解决什么问题?
  2. 活动记录为什么不适合所有复杂领域?
  3. 数据映射器如何隔离领域模型和数据库模型?
  4. DAO为什么不能设计成任意表、任意字段和任意条件查询?
  5. 报表查询和核心写模型为什么可以采用不同访问模式?
  6. 数据访问层如何处理事务、缓存、并发和安全?

请以合同、订单或库存系统为例,画出“应用服务—领域/聚合—仓储或DAO—映射器/网关—数据库或搜索引擎”的关系,并标注事务边界、缓存、权限、租户、并发版本和审计位置。完成后,你就掌握了 13.4.1 的核心内容。