---
# System prepended metadata

title: Spring 實務物件命名慣例
tags: [Spring Boot]

---

---
tags: Spring Boot

---
# Spring 實務物件命名慣例

```mermaid
flowchart LR
  n1["External"]
  n5["Database"]
  subgraph s1["Server"]
    n2["Controller"]
    n3["Service"]
    n4["Repository"]
  end
  
  n5@{ shape: db}

  n1["External"] -- XXXRequest --> n2
  n2 -- XXXResponse --> n1
  n2 <-- XXXDTO --> n3
  n3 <-- XXXEntity --> n4
  n4 <-- XXXEntity --> n5["Database"]
```

## 1. **外部 (External) -> Controller**

- **實務命名**：
  - 通常是 `XxxRequest` 或 `XxxReq`，例如 `CreateUserReq`, `UpdateOrderRequest`。
  - 小型專案可能直接使用簡單名稱，如 `UserRequest`，但大型專案會更具體，例如 `RegisterUserRequest`, `UpdateUserProfileRequest`。
  - 如果使用 OpenAPI/Swagger 生成 API，命名可能與 API 文件中的 schema 一致，例如 `UserCreationSchema`。
  - 欄位名稱通常與前端 JSON 結構一致，遵循 駝峰命名(camelCase)（如 `userName`, `orderId`），但某些團隊可能使用 蛇行命名(snake_case)（如 `user_name`）以符合前端規範。
- **實務細節**：
  - 物件通常包含驗證註解（如 `@NotBlank`, `@Email`），例如：

       ```java
       public class CreateUserRequest {
           @NotBlank
           private String userName;
           @Email
           private String email;
       }
       ```

  - **挑戰**：
    - 如果前端和後端命名規範不一致，可能需要額外的映射邏輯（例如使用 MapStruct）。
    - 過多的 Request DTO 可能導致類別爆炸，某些團隊會重用 DTO（例如 `UserRequest` 用於創建和更新）。
- **實務建議**：
  - 保持命名清晰，反映 API 的意圖（例如 `RegisterUserRequest` 而非泛用的 `UserRequest`）。
  - 如果專案規模大，使用工具（如 MapStruct 或 Lombok）減少 樣板程式碼(boilerplate code)。

---

## 2. **Controller -> Service**

- **實務命名**：
  - 常見名稱包括 `XxxDTO`（如 `UserDTO`, `OrderDTO`）或 `XxxCommand`（如 `CreateUserCommand`）。
  - 某些團隊直接傳遞 Request 物件（例如 `CreateUserRequest`），特別是在簡單場景中。
  - 如果業務邏輯複雜，可能使用更具體的名稱，例如 `UserRegistrationDTO`, `OrderFulfillmentCommand`。
- **實務細節**：
  - Controller 通常會將 Request DTO 轉換為 Service 層的 DTO，這一步可能涉及欄位映射或資料轉換。
  - 範例：

       ```java
       UserDTO userDTO = new UserDTO(request.getUserName(), request.getEmail());
       userService.createUser(userDTO);
       ```

  - 在 CQRS（Command Query Responsibility Segregation）架構中，常用 `XxxCommand` 來表示寫入操作，例如 `CreateUserCommand`。
  - 小型專案可能直接傳遞原始資料（例如 `String userName, String email`），但這在維護性上較差。
- **挑戰**：
  - 如果 Request DTO 和 Service DTO 差異不大，團隊可能傾向於重用物件，但這可能導致層次耦合。
  - 過多的 DTO 類別可能增加維護成本，特別是在快速迭代的專案中。
- **實務建議**：
  - 使用工具（如 MapStruct）自動化 DTO 之間的映射。
  - 在簡單場景中，可以考慮重用 Request DTO，但需確保層次職責分離。
  - 如果業務邏輯複雜，建議使用專用的 Command 物件來表達意圖。

---

## 3. **Service -> Repository**

- **實務命名**：
  - 大多數情況下直接使用 **Entity**，例如 `User`, `Order`, `Product`。
  - 如果需要傳遞非 Entity 的資料，可能使用 `XxxDTO`（如 `UserDTO`）或 `XxxVO`（Value Object，如 `UserVO`）。
  - 某些團隊可能使用簡化的物件，例如 `UserCriteria`（用於查詢條件）或 `UserSummary`（用於聚合結果）。
- **實務細節**：
  - Entity 是最常見的傳遞物件，直接對應資料庫表結構，例如：

       ```java
       User user = new User(dto.getUserName(), dto.getEmail());
       userRepository.save(user);
       ```

  - 如果查詢結果需要特殊結構（例如聯表查詢），可能返回 DTO，例如：

       ```java
       public class UserSummaryDTO {
           private Long id;
           private String userName;
       }
       ```

  - 在高併發或複雜場景中，可能使用專門的查詢物件（如 `UserQuery`）來傳遞查詢參數。
- **挑戰**：
  - Entity 過於複雜（例如包含大量欄位）可能導致性能問題，特別是在序列化或查詢時。
  - 如果 Service 層需要聚合多個 Entity 的資料，DTO 的設計和映射會變得繁瑣。
- **實務建議**：
  - 優先使用 Entity 進行 CRUD 操作，但在複雜查詢或聚合時使用 DTO。
  - 使用 JPA 的 `@Query` 或 QueryDSL 來簡化查詢邏輯，並返回專用的 DTO。
  - 避免在 Service 層直接操作底層資料庫結構（例如 SQL 結果集），保持 Repository 層的封裝。

---

## 4. **Repository -> Database**

- **實務命名**：
  - 幾乎總是 **Entity**，名稱與資料庫表名對應，例如 `User`, `Order`, `Product`。
  - 如果表名有前綴（如 `tbl_users`），實體名稱通常去掉前綴（`User`）。
  - 欄位名稱與資料庫欄位對應，通常使用 camelCase（例如 `userId` 對應 `user_id`）。
- **實務細節**：
  - Entity 使用 JPA 註解（如 `@Entity`, `@Table`, `@Column`）映射到資料庫，例如：

       ```java
       @Entity
       @Table(name = "users")
       public class User {
           @Id
           @GeneratedValue
           private Long id;
           @Column(name = "user_name")
           private String userName;
       }
       ```

  - 在某些場景中，可能使用 `@Embeddable` 物件來表示複合欄位（例如地址資訊）。
  - 如果資料庫結構複雜，可能使用 `@SecondaryTable` 或 `@JoinTable` 來處理多表映射。
- **挑戰**：
  - 如果資料庫結構與業務邏輯不一致（例如遺留系統），Entity 的設計可能變得複雜。
  - 過多的 Entity 關聯（例如 `@OneToMany`, `@ManyToMany`）可能導致性能問題（如 N+1 查詢）。
- **實務建議**：
  - 保持 Entity 簡單，僅包含必要的欄位和關聯。
  - 使用 `@BatchSize` 或 `@Fetch` 優化關聯查詢性能。
  - 如果資料庫結構複雜，考慮使用 View 或 DTO 來簡化資料訪問。

---

## 實務中的常見模式與注意事項

1. **DTO 重用與簡化**：
   - 小型專案可能會重用 DTO（例如 `UserDTO` 用於 Controller 和 Service），以減少類別數量。
   - 但在大型專案中，層次分離更重要，每層通常有專用的 DTO。

2. **工具與框架**：
   - **MapStruct**：用於自動映射 Request DTO、Service DTO 和 Entity，減少手動轉換的程式碼。
   - **Lombok**：使用 `@Data`, `@Builder` 等註解減少 boilerplate 程式碼。
   - **Swagger/OpenAPI**：用於生成 Request DTO 和 API 文件，確保命名一致。

3. **命名規範的統一**：
   - 團隊通常會制定命名規範（例如 `XxxRequest`, `XxxDTO`, `XxxEntity`），並記錄在專案 Wiki 或 Coding Guideline 中。
   - 常見規範：
     - Request：`XxxRequest` 或 `XxxReq`。
     - DTO：`XxxDTO` 或 `XxxVo`。
     - Entity：`Xxx`（無後綴，直接表達資源）。
     - Command：`XxxCommand`（用於 CQRS 或事件驅動架構）。

4. **微服務中的特殊情況**：
   - 在微服務架構中，物件可能需要跨服務傳遞，通常使用 `XxxApiModel` 或 `XxxContract` 來表示 API 契約。
   - 跨服務的 DTO 可能需要版本控制（例如 `UserV1DTO`, `UserV2DTO`）。

5. **性能與維護性**：
   - 過多的 DTO 可能增加維護成本，需權衡層次分離與程式碼簡潔。
   - 在高性能場景中，可能使用原生查詢或投影（Projection）來直接返回 DTO，繞過 Entity。

---

## 實務範例

假設一個用戶註冊場景的物件流：

1. **外部請求**：

   ```java
   public class RegisterUserRequest {
       @NotBlank
       private String userName;
       @Email
       private String email;
       @Size(min = 8)
       private String password;
   }
   ```

2. **Controller -> Service**：

   ```java
   public class UserRegistrationDTO {
       private String userName;
       private String email;
       private String passwordHash;
   }
   ```

   - Controller 使用 MapStruct 將 `RegisterUserRequest` 轉為 `UserRegistrationDTO`，並進行密碼加密。
3. **Service -> Repository**：

   ```java
   @Entity
   @Table(name = "users")
   public class User {
       @Id
       @GeneratedValue
       private Long id;
       @Column(name = "user_name")
       private String userName;
       private String email;
       private String passwordHash;
   }
   ```

4. **Repository -> Database**：
   - `User` 實體直接映射到 `users` 表。

---

## 總結

實務中，物件命名通常遵循理論慣例（`XxxRequest`, `XxxDTO`, `XxxEntity`），但會根據專案需求進行調整。關鍵在於：

- **清晰性**：命名應反映物件的職責和上下文。
- **一致性**：遵循團隊規範，確保程式碼可讀性。
- **平衡性**：在層次分離和程式碼簡潔之間找到平衡。
- **工具支援**：使用 MapStruct、Lombok 等工具減少重複工作。
