当前位置: 首页 > news >正文

实用指南:Spring Boot:DTO、VO、BO、Entity 的正确工程化分层

在很多初级 Spring Boot 项目里,你经常会看到这样的写法:

  • Controller 接口直接用 Entity 做入参

  • Service 返回 Entity 给前端

  • 数据库字段一改,整个项目跟着震荡

  • 新人接手完全不知道 什么该在哪一层做

这种“全都写在一个类里”的开发方式,小项目还能凑合,一旦遇到中大型业务,分分钟失控。

企业级项目讲究的是 边界清晰、模型稳定、可扩展强。 而实现这些的基础设施,就是 DTO、VO、BO、Entity 的分层模型。

1、为什么必须分层?

可以用一句工程化黄金法则解释:“内部结构可以变,但外部接口必须稳定。”

DTO、VO、BO、Entity 的职责就是把“内部变化”隔离开,

让修改成本缩到最小:

  • 数据库变 → 仅修改 Entity
  • 业务字段变 → 修改 BO
  • API 入参变化 → 只影响 DTO
  • 返回字段调整 → 修改 VO,不影响内部逻辑

这样你就不会因为一个字段改了导致全链路爆炸。

2、四层模型的职责与关系

用一句话总结每个对象:

关系图:
在这里插入图片描述
它们不是四层孤立结构,而是纵向贯穿整个业务链路的模型体系。

3、四层模型的详细解释 + 完整代码示例

下面用一个“用户管理”模块来讲透。

1)DTO:数据传输对象
职责:只负责接收外部输入。 不要放业务逻辑、不要放数据库字段,越精简越安全。


publicclassUserCreateDTO{

private String username;

private String password;
private String phone;
}

特点:

  • 一定要加字段校验(例如 @NotBlank)
  • 不允许与数据库结构耦合
  • 只负责“接收请求参数”

2)Entity:数据库实体类
职责:与 数据库表结构完全一致。 即便表字段很丑,也不能随便删。


("t_user")
publicclassUserEntity{
private Long id;
private String username;
private String password;
private String phone;
private Date createTime;
private Date updateTime;
}

特点:

  • 仅用于“数据库映射”
  • 表结构变更 → 只改这个类
  • 不要在 Controller、VO、DTO 里使用它

3)BO:业务对象(Service 层的“大脑”)
很多项目忽略 BO,这是最致命的! BO 是业务逻辑的载体,让 Service 代码 可测、可读、可复用。


publicclassUserBO{
private Long id;
private String username;
private String phone;
// 业务特有字段
privateboolean newUser;
}

特点:

  • 包含业务逻辑需要的额外数据(但不存数据库)
  • 是 Entity 的升级版
  • 比 Entity 更干净,比 VO 更业务
  • 没有 BO 的项目,就像没有脑子的业务逻辑。

4)VO:前端视图对象(最终展示)
职责:决定前端看见什么。 业务字段需做处理:脱敏、格式化、组合字段等。


publicclassUserVO{
private Long id;
private String username;
// 脱敏手机号
private String phoneMasked;
}

特点:

  • 不返回原始敏感字段
  • 专门给前端展示
  • 可以组合多个业务来源

4、模型之间如何转换?(最关键的工程化规范)

为了避免 MVC 层之间乱传对象,转换必须统一。

1)推荐使用 MapStruct(高性能编译期转换)
示例:

(componentModel = "spring")
publicinterfaceUserConverter{
UserBO dtoToBO(UserCreateDTO dto);
UserEntity boToEntity(UserBO bo);
UserVO boToVO(UserBO bo);
}

使用:


private UserConverter converter;
public UserVO createUser(UserCreateDTO dto){
UserBO bo = converter.dtoToBO(dto);
// 业务处理
bo.setNewUser(true);
// 入库
UserEntity entity = converter.boToEntity(bo);
userMapper.insert(entity);
// 返回前端
return converter.boToVO(bo);
}

好处:

  • 不需要手写 set/get(避免调试地狱)
  • 性能比 BeanUtils 高很多
  • 规范清晰

5、完整的分层调用链(可直接用于项目模板)

Controller
->接收DTO
->调用Service
Service
->DTO->BO
->业务处理(核心逻辑)
->BO->Entity
->持久化
->返回BO→VO
Repository
->操作Entity→DB

控制器中只写:

("/create")
public Result<UserVO> create(  UserCreateDTO dto){return Result.success(userService.createUser(dto));}

6、常见错误示范(请务必避免)

❌ 1. 用 Entity 做入参
public Result addUser(@RequestBody UserEntity entity)
风险:表字段一变,整个接口崩!

❌ 2. Service 返回 Entity 给前端
容易把密码、状态字段直接暴露。

❌ 3. 没有 BO,复杂业务揉到 Service 里
代码大面积重复,风险极高。

❌ 4. 不做字段脱敏
手机号、身份证、邮箱暴露给前端,等着挨打。

总结

DTO、VO、BO、Entity 有着严格的职责:

  • DTO:输入边界
  • VO:输出边界
  • BO:业务边界
  • Entity:数据边界

写对了项目会变得:

  • 清晰
  • 稳定
  • 可扩展
  • 易维护

写错了项目会:

  • 混乱
  • 难以维护
  • 对象到处乱飞
  • 改一个字段牵一发动全身
http://www.jsqmd.com/news/145082/

相关文章:

  • 基于springboot二手物品交易平台系统(源码+lw+部署文档+讲解等)
  • 虚拟机操作系统选择指南(2025)
  • 权威报告与专家共识加持,五大专家推荐宝宝敏感肌纸尿裤品牌助力宝宝远离干红痒 - 速递信息
  • DDD笔记 | 领域驱动设计(DDD)实战
  • STM32F103 学习笔记-21-串口通信(第1节)-串口通信协议简介
  • 快速幂
  • 学长亲荐8个AI论文软件,研究生论文写作不再难!
  • nt!MiInitializeLoadedModuleList分析和全局变量nt!PsLoadedModuleList初始化和LoaderBlock->LoadOrderListHead的关系非常重要
  • 私有知识库:数字时代的知识守护者
  • 【课程设计/毕业设计】基于Java的网上宠物店管理系统基于java的宠物用品店系统【附源码、数据库、万字文档】
  • 【mac如何连接redis】很好用的一款Redis客户端
  • Java计算机毕设之基于Java的网上宠物店管理系统宠物种类管理、宠物信息管理、食品类型管理(完整前后端代码+说明文档+LW,调试定制等)
  • transformer-explainer
  • 安装FunASR
  • 【开题答辩全过程】以 河金新生报到管理APP为例,包含答辩的问题和答案
  • 编译安装Freeswitch 1.10.12
  • 虚拟桌面是什么?Windows 自带的高效办公与摸鱼神器
  • 汽车领域智能体构建全解析—腾讯云黑客松Agent应用创新挑战赛微信公众号赛道实战复盘
  • 英伟达圣诞偷袭,200亿美元收购Groq
  • 鸿蒙开发入门:从环境搭建到第一个ArkTS应用,30分钟上手
  • 【计算机毕业设计案例】基于springboot的课程互助学习系统“资源共享 - 协作学习 - 互助答疑(程序+文档+讲解+定制)
  • [从程序员到架构师] 微服务场景实战 - 注册发现
  • 安装nvm管理node版本
  • 查找两个带头节点单链表的共同后缀起始位置
  • ‌测试代码覆盖率:Jacoco配置详解
  • 8.2.1-内核级支持的分布式存储ceph
  • 自动化测试覆盖率:达到90%+的实战体系构建
  • 鸿蒙6核心功能实战:手把手教你开发分布式协同小应用
  • SQL性能瓶颈破局:Explain分析+实战优化全攻略
  • 负熵流就是财富,财富就是负熵流