前后端的DTO数据同步

前后端DTO数据同步的定义、方案与实践

#resource / typescript #type / concept #status / growing

[!info] related notes

前后端的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,前后端同时生成类型,避免”先有后端再同步”的被动局面。
创建于 2025/1/1 更新于 2026/5/27