关于DTO、DO、BO、VO

一、参考文章

二、详解

这些都不是强制性要求,只是一个规范,比如对于一个比较小的系统,强制增加分层对象,可能各个分层对象之间的转换成了最繁琐的事情

典型分层架构使用对象:

复制代码
Controller (Web层) 
    ↓ VO/Request/Response
Service (业务层)
    ↓ BO/DTO
Manager/Repository (数据访问层)
    ↓ DO/Entity
Database (数据库层)

典型数据流转:

复制代码
前端 Request/Response ↔ VO ↔ Controller
                              ↓
                    Service ↔ DTO ↔ BO
                              ↓
              Repository ↔ DO ↔ Database

转换方法示例:

java 复制代码
// 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 等模板引擎使用

典型例子:

java 复制代码
// 用户列表页面的 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 的实体映射

  • 数据持久化:直接操作数据库的对象

典型例子:

java 复制代码
@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 中的聚合根对象

  • 状态管理:管理复杂的业务状态

  • 业务验证:包含业务验证逻辑

典型例子:

java 复制代码
// 复杂业务逻辑的 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 等缓存中存储的数据结构

典型例子:

java 复制代码
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 {
  @Autowired
  private 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 层:

java 复制代码
@RestController
public class OrderController {
    
    @PostMapping("/create")
    public Result<OrderVO> createOrder(@RequestBody CreateOrderRequest request) {
        // Request → DTO 转换
        OrderDTO orderDTO = convertToDTO(request);
        
        // 调用 Service
        OrderDTO result = orderService.createOrder(orderDTO);
        
        // DTO → VO 转换
        OrderVO orderVO = convertToVO(result);
        return Result.success(orderVO);
    }
}

Service 层:

java 复制代码
@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 层:

java 复制代码
@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 (包含格式化数据)
相关推荐
老华带你飞4 分钟前
工会管理|基于springboot 工会管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
自在极意功。4 分钟前
MyBatis配置文件详解:environments、transactionManager与dataSource全面解析
java·数据库·tomcat·mybatis
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ8 分钟前
配置springdoc swagger开关
java
Echo flower11 分钟前
Spring Boot WebFlux 实现流式数据传输与断点续传
java·spring boot·后端
没有bug.的程序员18 分钟前
微服务中的数据一致性困局
java·jvm·微服务·架构·wpf·电商
鸽鸽程序猿22 分钟前
【Redis】Java客户端使用Redis
java·redis·github
悦悦子a啊22 分钟前
使用 Java 集合类中的 LinkedList 模拟栈以此判断字符串是否是回文
java·开发语言
Lucky小小吴24 分钟前
java代码审计入门篇——Hello-Java-Sec(完结)
java·开发语言
一个想打拳的程序员26 分钟前
无需复杂配置!用%20docker-webtop%20打造跨设备通用%20Linux%20桌面,加载cpolar远程访问就这么简单
java·人工智能·docker·容器
一起养小猫28 分钟前
LeetCode100天Day2-验证回文串与接雨水
java·leetcode