如何在UML类图中展示Data Transfer Object(DTO)?附示例
UML类图中DTO的正确展示方法
DTO的核心定位是跨边界(跨层、跨服务、跨进程)传输的纯数据载体,没有业务逻辑,建模时只要把这个核心特征体现清楚,就不会出大问题。
核心建模规则
- 必须给DTO类加
<<DTO>>构造型标记,放在类名上方的构造型位置,和领域实体、值对象、配置类等其他类做明确区分,这是最直观的识别标识 - 类成员只保留数据相关内容:可以定义私有字段、无参/全参构造方法、属性get/set方法,绝对不能出现业务计算、流程判断、数据校验这类业务逻辑方法;如果是框架强制要求实现的序列化接口(比如Java的
Serializable),可以保留实现关系,其他无关接口一律不要加 - 关联关系统一用**依赖(虚线箭头)**和上下游类连接:DTO是请求/响应维度的瞬时对象,没有长期被持有的生命周期,不要画组合、聚合、双向关联这类强绑定关系
- 嵌套DTO直接按实际包含关系关联,标注清楚多重性即可,比如分页包装DTO和内部列表项DTO是1对多的关系,直接标
1对0..*就行;带泛型的包装DTO可以直接用UML泛型标记类名~泛型参数~展示
实际建模示例
下面是用户管理业务场景下的DTO建模参考,覆盖了请求DTO、响应DTO、继承、泛型包装、嵌套、跨层调用的完整关系:
classDiagram class Serializable { <<interface>> } class PageQueryDTO { <<DTO>> - Integer pageNum - Integer pageSize + PageQueryDTO() + getPageNum() Integer + setPageNum(Integer pageNum) void + getPageSize() Integer + setPageSize(Integer pageSize) void } class UserQueryDTO { <<DTO>> - String keyword - Integer status + UserQueryDTO() + getKeyword() String + setKeyword(String keyword) void + getStatus() Integer + setStatus(Integer status) void } class UserInfoDTO { <<DTO>> - Long userId - String username - String email - LocalDateTime createTime + UserInfoDTO() + getUserId() Long + setUserId(Long userId) void + getUsername() String + setUsername(String username) void + getEmail() String + setEmail(String email) void + getCreateTime() LocalDateTime + setCreateTime(LocalDateTime createTime) void } class PageResultDTO~T~ { <<DTO>> - Long total - Integer pageNum - Integer pageSize - List~T~ records + PageResultDTO() + getTotal() Long + setTotal(Long total) void + getPageNum() Integer + setPageNum(Integer pageNum) void + getPageSize() Integer + setPageSize(Integer pageSize) void + getRecords() List~T~ + setRecords(List~T~ records) void } class UserController { <<controller>> + queryUserList(UserQueryDTO query) PageResultDTO~UserInfoDTO~ } class UserService { <<service>> + listUser(PageQueryDTO page, UserQueryDTO query) PageResultDTO~UserInfoDTO~ } class OpenApiClient { <<client>> + fetchUserList(UserQueryDTO query) PageResultDTO~UserInfoDTO~ } PageQueryDTO <|-- UserQueryDTO PageResultDTO *-- "0..*" UserInfoDTO Serializable <|.. PageQueryDTO Serializable <|.. UserQueryDTO Serializable <|.. UserInfoDTO Serializable <|.. PageResultDTO UserController ..> UserQueryDTO UserController ..> PageResultDTO UserController ..> UserService UserService ..> PageQueryDTO UserService ..> UserQueryDTO UserService ..> PageResultDTO OpenApiClient ..> UserQueryDTO OpenApiClient ..> PageResultDTO
这个示例完全对齐实际开发规范:所有DTO都有明确构造型标记,没有多余业务方法,和上下游的控制层、服务层、外部客户端关系统一用依赖表示,泛型、继承、嵌套关系都做了明确标注,可以直接套用到业务建模里。
常见建模误区
- 不要给DTO加业务方法:比如不要在
UserInfoDTO里写邮箱格式校验、权限判断这类逻辑,这类逻辑要放到业务层或者专门的校验组件里,DTO只负责存数据 - 不要把DTO和领域实体画成等价类:二者可能字段高度重合,但职责完全不同——领域实体带业务行为,DTO只做传输,必须分开建模
- 不要给DTO加强生命周期关联:DTO随着请求/响应结束就会被销毁,不存在被某个服务长期持有、和服务同生命周期的情况,别画组合、聚合这类强关联
内容的提问来源于stack exchange,提问作者abus
相关产品推荐
相关产品推荐

