18.11 记录集

用内存中的表格数据表示一组记录,便于与数据库和数据感知界面交换。

学习目标

  • 能解释记录集如何把查询结果表示成可交换的内存表格,并识别它与领域对象的边界
  • 能编写 TypeScript 记录集,保留列模式、空值和类型信息,并实现安全的筛选与转换
  • 能根据查询复杂度、行为归属、批量交换和演化成本,判断何时采用或拒绝记录集

为什么 18.11 记录集 值得单独学习

用内存中的表格数据表示一组记录,便于与数据库和数据感知界面交换。18.11 记录集的核心不是套用某个框架 API,而是回答:如何在数据库查询、应用服务和数据界面之间传递稳定的行列结果,同时避免把基础设施形状误当成领域对象。在订单系统基础设施替换中,如果无法说清责任、状态和失败由谁承担,即使查询能返回,架构决定也没有完成。

的价值不是让所有数据都停留在无行为的行列里,而是在简单查询、报表和批量交换中保持结构清晰。本页以 2024 年中文版公开目录限定 18.11 记录集 的范围,并依据 Martin Fowler 的作者图书页和模式目录独立重写。它不复现原书正文、插图或代码;目录只决定“要讲什么”,这里的案例、实验、判断题和答案均为本课程原创。

先建立直觉:问题、机制与代价

18.11 记录集面对的具体压力是“查询结果跨层传递、批量处理、界面绑定和对象映射成本”。它采用的机制可以概括为:以列模式和行集合承载结果,让消费者按列读取或筛选。机制带来的收益必须与弱行为表达、列名耦合、空值歧义和数据复制成本同时记录,否则学习者只会得到一个没有拒绝条件的模式名称。

对 18.11 记录集,优先比较的替代路线是:领域模型、专用 DTO、流式游标或直接的查询投影。只有在需要批量交换且行结构本身就是边界契约时,才值得引入记录集;若结果需要丰富不变量和协作行为,就应转为 ,不能据此选择 18.11 记录集。

目录单元到教学证据

18.11 记录集

18.11 记录集 的学习边界里,18.11 记录集不是待背诵的目录词,而是用来检查“查询边界”是否把数据责任交给正确消费者。对订单系统基础设施替换,学习者要记录列模式、空值和行为归属的可观察变化,并说明它何时支持或否定 18.11 记录集。

专属设计案例:订单报表的查询结果交换

把订单报表的查询结果交换切成“数据库查询 → 记录集列模式 → 应用筛选 → 表格界面”四个观察点。18.11 记录集的设计草案必须写出谁拥有列定义、谁处理空值、失败怎样传播,以及依赖方向、对象语义、配置、测试隔离、表示转换中哪个指标最先提示当前方案不再适用。

设计记录采用五个可换行字段:单元键为 poeaa24-pattern-51-record-set;模式族为 base;裁决是“能保持列模式、空值和类型信息,批量处理记录,并说明何时对象模型更适合复杂行为”;观测项包括依赖方向、对象语义、配置、测试隔离、表示转换;拒绝条件是“记录行开始承担复杂业务不变量,或列模式无法作为边界契约维护”。

配置不是生产框架语法,而是一张评审卡。对 18.11 记录集的任何实现都要能把运行证据重新映射到这张卡;如果更换 ORM、Web 框架或部署平台后无法回答同一组问题,说明决定依赖的是工具偶然行为而不是模式语义。

结构解剖

Record Set:查询结果的内存表格数据库SELECT * FROM orders查询RecordSetid | total | status42 | 597 | paid43 | 120 | open行/列结构 · 内存中Table ModuleUI 数据绑定定位:数据库与领域之间的中间形态。适合简单 CRUD 和报表;复杂领域逻辑应转为 Domain Model。Record Set 是查询结果的内存行/列表格,可被 Table Module 直接消费
Record Set 是数据库查询结果的内存表示,行/列结构可被 Table Module 或 UI 直接消费, 是数据库与领域之间的中间形态。

选择与拒绝矩阵

评审问题选择 18.11 记录集 的证据应拒绝或改用其他方案的信号
责任查询边界拥有列模式,消费者只读取约定字段多个消费者各自猜测列名和空值含义
变化报表字段可批量演化并有版本说明行结构被当成公开领域对象,改列影响所有行为
失败缺列、类型错误和查询失败可分别观测空值或缺列被静默转换成零值
替代已与 DTO、领域模型和流式结果比较内存成本复杂行为持续堆在行对象的工具方法里

代码实践:保留列模式的订单记录集

订单报表需要把查询结果交给表格界面,但不应让界面自行猜测列名和空值。应在查询边界定义,并在转换时拒绝不完整行。

type OrderRow = {
  id: string;
  totalCents: number;
  status: "paid" | "open" | "cancelled";
  paidAt: string | null;
};
 
type OrderRecordSet = {
  columns: string[];
  rows: OrderRow[];
};
 
function buildOrderRecordSet(rows: OrderRow[]): OrderRecordSet {
  return {
    columns: ["id", "totalCents", "status", "paidAt"],
    rows,
  };
}
 
function selectPaidRows(recordSet: OrderRecordSet): OrderRow[] {
  const requiredColumns = ["id", "totalCents", "status", "paidAt"];
  const hasSchema = requiredColumns.every((column) =>
    recordSet.columns.includes(column),
  );
  if (!hasSchema) throw new Error("order record set schema mismatch");
  return recordSet.rows.filter((row) => row.status === "paid");
}
 
const orders = buildOrderRecordSet([
  { id: "order-7", totalCents: 59700, status: "paid", paidAt: "2026-08-07" },
  { id: "order-8", totalCents: 12000, status: "open", paidAt: null },
]);
 
const paidOrders = selectPaidRows(orders);

这段代码把列名、状态和可空时间明确成边界契约,报表可以批量筛选而不用创建完整领域对象。若订单行开始需要退款、权限或状态转移等不变量,就应把它们映射到领域模型,而不是继续往记录集中堆行为。

常见误区

可验证练习

练习

问题 1:判断边界。 一个订单行需要执行退款前检查库存和权限,它仍适合直接放在记录集中吗?

问题 2:处理列演化。 数据库把 totalCents 改名为 amountCents,报表界面应该自行兼容两个名字吗?

问题 3:选择替代方案。 一个结果集只有三列,但会被多个接口分页、缓存和导出,是否应立刻构造完整领域对象?

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

记录集

把一批同构查询行及其列信息封装成内存表格、供多个边界交换的结果表示。

列模式

记录集中列名、类型、可空性和版本组成的可验证结构约束。

领域模型

围绕业务行为、不变量和协作关系组织的对象模型,而不是只保存查询列的结构。

本章小结

掌握 18.11 记录集的标志不是记住定义,而是能在订单报表的查询结果交换中解释“数据库查询 → 记录集列模式 → 应用筛选 → 表格界面”的责任链,利用依赖方向、对象语义、配置、测试隔离、表示转换作出可证伪的选择,并在记录行开始承担复杂不变量或列模式无法维护时明确拒绝。

前后导航

来源与改写范围

资料与写作方式声明

本章以Martin Fowler《企业应用架构模式》与公开模式目录权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…