---
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 等工具減少重複工作。