关于DTO、DO、BO、VO
一、参考文章
-
https://blog.csdn.net/lairikeqi/article/details/106081782
-
https://blog.csdn.net/2301_81298401/article/details/137342582
二、详解
这些都不是强制性要求,只是一个规范,比如对于一个比较小的系统,强制增加分层对象,可能各个分层对象之间的转换成了最繁琐的事情
典型分层架构使用对象:
Controller (Web层) ↓ VO/Request/Response
Service (业务层)↓ BO/DTO
Manager/Repository (数据访问层)↓ DO/Entity
Database (数据库层)
典型数据流转:
前端 Request/Response ↔ VO ↔ Controller↓Service ↔ DTO ↔ BO↓Repository ↔ DO ↔ Database
转换方法示例:
// VO ↔ DTO 转换
public class OrderConverter {public static OrderVO toVO(OrderDTO dto) { ... }public static OrderDTO toDTO(OrderVO vo) { ... }
}// DTO ↔ BO 转换
public class OrderBO {public static OrderBO fromDTO(OrderDTO dto) { ... }public OrderDTO toDTO() { ... }
}// BO ↔ DO 转换
public class OrderBO {public OrderDO toDO() { ... }public static OrderBO fromDO(OrderDO do) { ... }
}
1、VO (View Object) - 视图对象
概述:
VO (View Object),用于表示一个与前端进行交互的视图对象,它的作用是把某个指定页面(或组件)的所有数据封装起来。实际上,这里的 VO 只包含前端需要展示的数据,对于前端不需要的数据,比如数据创建和修改的时间等字段,出于减少传输数据量大小和保护数据库结构不外泄的目的,不应该在 VO 中体现出来
使用场景:
-
前端展示层:专门为前端界面设计的数据结构
-
API 响应:Controller 返回给前端的数据
-
页面渲染:JSP/Thymeleaf 等模板引擎使用
典型例子:
// 用户列表页面的 VO
public class UserListVO {private Long userId;private String userName;private String statusName; // 状态中文名private String createTimeStr; // 格式化后的时间private Boolean canEdit; // 是否可编辑(业务权限)private String avatarUrl; // 头像完整URL
}// controller层
public class xxxController {@PostMapping("/query")public Result<UserListVO> query(@RequestBody xxxParam param) {// 返回 VO 给前端}
}
2、DO (Data Object) - 数据对象
概述:
DO(Data Object) ,持久化对象,它跟持久层(Dao)的数据结构形成一一对应的映射关系。如果持久层是关系型数据库,那么数据库表中的每个字段就对应PO的一个属性,常是entity实体类。
使用场景:
-
数据库映射:与数据库表一一对应
-
ORM 框架:MyBatis/JPA 的实体映射
-
数据持久化:直接操作数据库的对象
典型例子:
@TableName("user")
public class UserDO {private Long id;private String name;private String email;// getters and setters
}
3、BO (Business Object) - 业务对象
概述:
BO(Business Object):业务对象,就是从现实世界中抽象出来的有形或无形的业务实体。
使用场景:
-
复杂业务逻辑:封装核心业务规则和计算
-
聚合根:DDD 中的聚合根对象
-
状态管理:管理复杂的业务状态
-
业务验证:包含业务验证逻辑
典型例子:
// 复杂业务逻辑的 BO
public class OrderBO {private Long orderId;private OrderStatus status;private List<OrderItemDTO> items;private CustomerDTO customer;// 业务方法public boolean canCancel() {return status == OrderStatus.PENDING && createTime.isAfter(LocalDateTime.now().minusHours(24));}public BigDecimal calculateTotalWithDiscount() {BigDecimal total = items.stream().map(OrderItemDTO::getSubtotal).reduce(BigDecimal.ZERO, BigDecimal::add);return applyCustomerDiscount(total, customer.getVipLevel());}public void processPayment(PaymentDTO payment) {validatePaymentAmount(payment);updateOrderStatus();sendNotification();}// 包含复杂的业务逻辑和数据转换public static BillingDetailBO of(...) { ... }public OrderDTO get() { ... }
}
4、DTO (Data Transfer Object) - 数据传输对象
概述:
DTO(Data Transfer Object),用于表示一个数据传输对象,DTO 通常用于展示层(Controller)和服务层(Service)之间的数据传输对象。DTO 与 VO 概念相似,并且通常情况下字段也基本一致。但 DTO 与 VO 又有一些不同,这个不同主要是设计理念上的,比如 API 服务需要使用的 DTO 就可能与 VO 存在差异。
使用场景:
-
跨层传输:Service 层之间的数据传递【主要】
-
跨服务调用:微服务之间的数据传输
-
RPC 调用:远程过程调用的参数和返回值
-
缓存对象:Redis 等缓存中存储的数据结构
典型例子:
public class UserDTO {private Long id;private String displayName;public UserDTO(UserBO userBO) {this.id = userBO.getUserDO().getId();this.displayName = userBO.getUserDO().getName();}
}// service 层中的传递
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public UserDTO getUserDetails(Long userId) {UserDO userDO = userRepository.findById(userId).orElse(null);UserBO userBO = new UserBO(userDO);UserDTO userDTO = new UserDTO(userBO);return userDTO;}
5、应用示例
Controller 层:
@RestController
public class OrderController {@PostMapping("/create")public Result<OrderVO> createOrder(@RequestBody CreateOrderRequest request) {// Request → DTO 转换OrderDTO orderDTO = convertToDTO(request);// 调用 ServiceOrderDTO result = orderService.createOrder(orderDTO);// DTO → VO 转换OrderVO orderVO = convertToVO(result);return Result.success(orderVO);}
}
Service 层:
@Service
public class OrderService {public OrderDTO createOrder(OrderDTO orderDTO) {// DTO → BO 转换(复杂业务逻辑)OrderBO orderBO = OrderBO.createFromDTO(orderDTO);// 业务逻辑处理orderBO.validateOrder();orderBO.calculatePrice();orderBO.applyPromotions();// BO → DO 转换OrderDO orderDO = orderBO.toDO();// 保存到数据库orderRepository.save(orderDO);// DO → DTO 返回return OrderDTO.fromDO(orderDO);}
}
Repository/Dao 层:
@Repository
public class OrderRepository {public OrderDO save(OrderDO orderDO) {// 直接操作数据库return orderMapper.insert(orderDO);}public List<OrderDO> findByPatientId(Long patientId) {// 返回 DO 对象return orderMapper.selectByPatientId(patientId);}
}
六、特殊场景的选择
1.简单CRUD场景
// 可以简化,跳过 BO
Controller → Service → DTO → Repository → DO
2.复杂业务场景
// 必须使用 BO 封装业务逻辑
Controller → VO → Service → DTO → BO → Repository → DO
3.微服务调用
// 服务间只传输 DTO
ServiceA → DTO → RPC → ServiceB
4.报表展示
// 重点使用 VO 进行数据格式化
Service → DTO → ReportVO (包含格式化数据)