📚 深入理解 Java 中的 VO、BO、PO、DTO、DO:領域分層設計的核心概念

在 Java 企業級開發中,尤其是基於 分層架構(如 MVC、DDD)的項目中,我們經常會遇到各種以 “O” 結尾的對象:VO、BO、PO、DTO、DO。它們看似相似,實則各司其職,是保障系統高內聚、低耦合、可維護性的重要設計手段。

本文將帶你全面解析這五種對象的定義、用途、區別與最佳實踐,助你寫出更清晰、更專業的代碼。


🔍 一、先看一張圖:分層架構中的對象流轉

前端 UI
   ↓ (VO)
Web 層 (Controller)
   ↓ (DTO)
Service 層 (BO / DO)
   ↓ (DO)
持久層 (DAO / Mapper)
   ↓ (PO)
數據庫 (Table)

✅ 每一層使用不同的對象進行數據傳遞,避免“貧血模型”和“污染式傳遞”。


🧩 二、五大對象詳解

1️⃣ VO(View Object)—— 視圖對象

  • 全稱:View Object
  • 職責:用於展示層,將數據以特定格式返回給前端(如 JSON、HTML)。
  • 特點
  • 字段結構完全匹配前端需求;
  • 可能包含計算字段、拼接字段、狀態標籤等;
  • 通常不直接映射數據庫表。
✅ 示例:用户詳情頁 VO
public class UserVO {
    private String userName;
    private String userLevel; // "VIP1", "普通用户"
    private String lastLoginTimeStr; // 格式化時間:"2025-03-20 14:30"
    private Boolean isOnline;
    // getter/setter
}

💡 前端需要的是“可讀字符串”而非原始時間戳,VO 正是用來做這種轉換的。


2️⃣ DTO(Data Transfer Object)—— 數據傳輸對象

  • 全稱:Data Transfer Object
  • 職責:用於跨層或跨服務傳輸數據,減少網絡調用次數。
  • 特點
  • 通常用於 Controller ↔ Service微服務之間
  • 包含多個實體的部分字段,避免暴露完整模型;
  • 是“扁平化”的數據結構,常用於 API 接口。
✅ 示例:創建用户請求 DTO
public class CreateUserDTO {
    private String username;
    private String password;
    private String email;
    private List<String> roleCodes;

    // 校驗註解
    @NotBlank(message = "用户名不能為空")
    public String getUsername() { return username; }
}

⚠️ DTO 通常配合 @Valid 使用,實現參數校驗前置。


3️⃣ DO(Domain Object)—— 領域對象

  • 全稱:Domain Object
  • 職責:代表業務領域的核心模型,承載業務邏輯
  • 特點
  • 來自 DDD(領域驅動設計) 的概念;
  • 包含行為(方法)而不僅僅是數據;
  • 是業務規則的載體。
✅ 示例:訂單 DO(含業務方法)
public class OrderDO {
    private Long id;
    private BigDecimal amount;
    private String status; // INIT, PAID, SHIPPED
    private LocalDateTime payTime;

    // 業務方法
    public void pay() {
        if (!"INIT".equals(status)) {
            throw new BusinessException("訂單不可支付");
        }
        this.status = "PAID";
        this.payTime = LocalDateTime.now();
    }

    public boolean isOverdue() {
        return payTime != null && 
               payTime.isBefore(LocalDateTime.now().minusHours(24));
    }
}

💡 DO 是“富模型”,強調“數據 + 行為”一體化,區別於貧血的 PO。


4️⃣ BO(Business Object)—— 業務對象

  • 全稱:Business Object
  • 職責:封裝複雜業務邏輯,可能是多個 DO 的組合。
  • 特點
  • 常用於 Service 層內部處理;
  • 可能包含計算結果、上下文信息、臨時狀態;
  • 不一定持久化。
✅ 示例:訂單結算 BO
public class CheckoutBO {
    private OrderDO order;
    private List<ItemDO> items;
    private CouponDO coupon;
    private BigDecimal totalAmount;
    private BigDecimal discountAmount;
    private BigDecimal finalAmount;
    // getters and setters
}

💡 BO 是“業務中間態”,用於在 Service 內部傳遞複雜上下文。


5️⃣ PO(Persistent Object)—— 持久化對象

  • 全稱:Persistent Object
  • 職責:與數據庫表結構一一對應,用於 ORM 框架(如 MyBatis、JPA)。
  • 特點
  • 字段名通常與數據庫列名一致(可通過註解映射);
  • 通常是“貧血模型”(只有 getter/setter);
  • 直接參與 CRUD 操作。
✅ 示例:用户 PO
@TableName("t_user")
public class UserPO {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String user_name;
    private String password;
    private Integer age;
    private LocalDateTime create_time;
    // getter/setter
}

⚠️ PO 是“數據庫契約”,不應暴露給前端或用於業務計算。


🔄 三、對象之間的轉換關係

轉換方向

工具推薦

PO → DO

MapStruct、Dozer、BeanUtils

DO → BO

構造器、Builder 模式

BO → DTO

MapStruct(推薦)、ModelMapper

DTO → VO

直接返回或簡單轉換

✅ 強烈推薦使用 MapStruct 實現 POJO 之間的映射,編譯期生成代碼,性能高且類型安全

示例:MapStruct 映射接口
@Mapper
public interface UserConvert {
    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);

    UserDO po2Do(UserPO po);
    UserVO do2Vo(UserDO do);
}

🧱 四、五種對象的對比總結

對象

全稱

所在層

是否持久化

是否含業務邏輯

典型場景

VO

View Object

Web 層



返回前端 JSON

DTO

Data Transfer Object

Web/Service



API 請求/響應

BO

Business Object

Service 層


⭕(上下文)

複雜業務中間態

DO

Domain Object

Domain 層

⭕(邏輯上)


核心業務模型

PO

Persistent Object

DAO 層



數據庫 ORM 映射

口訣記憶

  • VO 給前端看
  • DTO 傳接口
  • BO 做計算
  • DO 寫邏輯
  • PO 連數據庫

✅ 五、最佳實踐建議

  1. 不要濫用對象
    小項目可簡化,如直接用 DO 返回前端(但不推薦)。
  2. 命名規範統一
    如:UserVOUserDTOUserDO,避免混淆。
  3. 禁止跨層“跳躍”使用對象
  • ❌ 不要讓 PO 直接返回給前端;
  • ❌ 不要在 Controller 中操作 DO 的業務方法;
  • ✅ 每層只依賴相鄰層的對象。
  1. 使用 Lombok 減少樣板代碼
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserVO {
    private String userName;
    private String userLevel;
}
  1. 結合 Spring 分層註解
@RestController
public class UserController {
    private final UserService userService;

    @PostMapping("/users")
    public Result<UserVO> createUser(@Valid @RequestBody CreateUserDTO dto) {
        UserVO vo = userService.create(dto);
        return Result.success(vo);
    }
}

🚀 六、為什麼需要這麼多“O”?

這些對象的本質是 關注點分離(Separation of Concerns) 的體現:

  • VO 關注“如何展示”;
  • DTO 關注“如何傳輸”;
  • BO 關注“如何組合”;
  • DO 關注“如何建模”;
  • PO 關注“如何存儲”。

通過合理使用這些對象,你可以構建出:

  • ✅ 更清晰的代碼結構
  • ✅ 更強的可維護性
  • ✅ 更好的擴展性