DTO 与数据契约
DTO(Data Transfer Object,数据传输对象)是服务层定义请求/响应形状、隔离内部模型与外部接口的核心模式——它是一份显式的「数据契约」,声明「这个接口接受什么字段、返回什么形状」,在数据库实体与 API 调用者之间架起一层边界。没有 DTO,接口直接吐出 ORM 实体会导致三大灾难:①字段过度暴露(密码哈希、内部状态泄露给客户端)②强耦合(数据库加个字段,所有客户端都要改)③无校验(请求体任意塞,服务端不校验就落库)。DTO 把「接口形状」从代码里隐式约定提升为显式声明 + 可校验 + 可序列化控制的契约,是服务层设计的基础设施。
数据契约有两层:①形状契约(哪些字段、什么类型、必填还是可选)②校验契约(字段值的约束,如 email 格式、年龄范围、字符串长度)。实现校验有两大流派:装饰器流派(class-validator/joi,在类属性上挂装饰器,配合 NestJS 管线/Express 中间件自动校验)与 schema 流派(Zod/Valibot/Yup,用纯 JS 对象/函数链描述 schema,框架无关、类型推导友好)。两者本质都是「描述期望形状 → 校验输入 → 拒绝不合法」,但工程取舍不同:装饰器流派依赖类与反射(metadata),schema 流派依赖纯函数与类型推导。本叶讲清 DTO 的概念层(设计模式、请求/响应形状、序列化策略、向后兼容),并横向对比两种校验模式,帮你在不同框架与团队约定下做出选择。具体框架的管线/装饰器 API 归后端框架章,具体 schema 库(Zod/Valibot)的 API 归 JS 扩展库章。
评价
优点
- 显式契约:接口形状写进 DTO 类/Schema,新人一看就懂,文档即代码
- 字段控制:序列化时只暴露该暴露的字段,防密码/内部状态泄露
- 输入校验:契约即校验规则,非法请求在入口被拒,不污染业务逻辑
- 解耦:DTO 隔离 ORM 实体与接口,数据库改字段不强制改客户端
- 类型安全:TypeScript 下 DTO 既是运行时校验器也是编译期类型,运行时与编译期一致
缺点
- 样板代码:每个接口要定义请求/响应 DTO,字段多了显繁琐(schema 流派可缓解)
- 转换成本:实体 ↔ DTO 要手写映射(或用 mapper 库),增加一层间接
- 维护负担:接口演进时 DTO 要兼顾向后兼容,老客户端不能因新字段崩
- 两套世界:装饰器流派依赖类与反射,schema 流派依赖纯函数,团队要统一否则割裂
本叶地图
- 入门 —— DTO 定义、为什么不能直接吐实体、形状与校验两层契约、装饰器 vs schema 速览
- DTO 模式 —— DTO 设计模式、请求/响应形状设计、序列化策略(暴露控制)、实体 ↔ DTO 转换
- 校验模式对比 —— 装饰器流派(class-validator)vs schema 流派(Zod/Valibot)、向后兼容策略
- 参考 —— DTO 速查、两种校验模式对照、序列化注解清单、易错点