前后端的DTO数据同步
前后端DTO数据同步的定义、方案与实践
#resource / typescript
#type / concept
#status / growing
[!info] related notes
- 所属 MOC: 后端开发 MOC
- 相关概念: 接口规范, contracts-in-dailyuse, 前后端数据同步
- 相关实践: nodejs后端api测试搭建集成测试框架
前后端的DTO数据同步
一句话定义
DTO 数据同步是指在前后端分离项目中,确保前端 TypeScript 接口与后端 DTO 类在结构上保持一致,从而获得编译期类型安全、减少运行时数据不匹配的 bug。
核心机制 / 工作原理
为什么需要同步
- 后端改了 DTO 字段名/类型,前端不知道 -> 运行时报错
- 手动维护两端类型 -> 容易遗漏、不同步
- 没有类型检查 -> 前端拿到的数据和预期不一致时难以排查
主流方案
1. OpenAPI / Swagger 代码生成
后端生成 OpenAPI spec -> 工具自动生成前端 TypeScript 类型。
后端 DTO (@ApiModel) -> openapi.json -> openapi-typescript -> *.ts 接口
工具:openapi-typescript, swagger-codegen, openapi-generator
2. 共享类型包
前后端共享一个 npm 包,包含所有 DTO 定义。适用于 monorepo 或同语言栈(如 Node.js + React)。
packages/shared-types/src/order.dto.ts <- 前后端共同引用
3. API 契约测试
不生成类型,但通过测试确保 API 响应符合预期 schema(如 Pact, jest + zod schema 验证)。
最小例子
场景:后端 Java DTO -> 前端 TypeScript
// 后端 OrderDTO.java
public class OrderDTO {
private Long id;
private String customerName;
private List<OrderItemDTO> items;
private BigDecimal totalAmount;
}
通过 openapi-typescript 自动生成:
// 前端 order.d.ts(自动生成,不要手动编辑)
export interface OrderDTO {
id: number;
customerName: string;
items: OrderItemDTO[];
totalAmount: string; // BigDecimal 序列化为 string
}
前端直接使用:
const order: OrderDTO = await fetchOrder(id);
console.log(order.customerName); // 有类型提示
边界与常见误解
- “自动生成就万无一失” — 生成工具依赖后端正确注解。如果后端字段没加
@ApiModelProperty,生成的类型可能不完整。 - “Electron 项目也需要同步” — 在 Electron+Vue3 中,主进程和渲染进程使用相同的 domain 文件是同一个问题的变体。本质都是两端共享数据结构。
- BigDecimal -> string — 类型映射时要注意:Java 的
BigDecimal序列化后是字符串而非数字,前端如果当number用会丢失精度。 - 共享类型包 vs 代码生成 — 共享包适合 monorepo + 同语言;代码生成适合异构语言(Java + TS)。两者不互斥。
- API-first 开发 — 最理想的做法是先定义 OpenAPI spec,前后端同时生成类型,避免”先有后端再同步”的被动局面。