Spring Boot 專案結構與組織方式
概述
Spring Boot 應用程式開發依賴 Spring Framework 的註解 (Annotation) 自動找尋載入各個功能元件與定義設定。由於 Spring 框架會自動掃描,所以專案目錄結構反而沒有特別規定或制式結構。
重要原則
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication 註解是多個註解的組合,預設所有的 @Controller、@Service、@Repository 等 Spring 相關的註解只能使用在與主程式類別同目錄或子目錄下,才能被 Spring 掃描到。
專案結構組織方式
在專案開發管理上,通常有兩種主要的組織方式:
1. 按功能組織 (By Feature) - 推薦
在這種方法中,所有與特定功能相關的類別都放在同一個包中。
com.example.demo
├── DemoApplication.java
├── config/
│ ├── SecurityConfig.java
│ └── DatabaseConfig.java
├── customer/
│ ├── Customer.java
│ ├── CustomerController.java
│ ├── CustomerService.java
│ ├── CustomerRepository.java
│ └── dto/
│ ├── CustomerRequest.java
│ └── CustomerResponse.java
├── order/
│ ├── Order.java
│ ├── OrderController.java
│ ├── OrderService.java
│ ├── OrderRepository.java
│ └── dto/
│ ├── OrderRequest.java
│ └── OrderResponse.java
└── common/
├── exception/
│ ├── GlobalExceptionHandler.java
│ └── BusinessException.java
└── util/
└── DateUtils.java
優點:
- 易於維護:找到需要修改的類別很容易
- 模組化:刪除特定子包即可移除整個功能
- 測試友好:測試和重構更容易
- 獨立部署:功能可以單獨發佈
- 團隊協作:不同團隊成員可以專注於不同功能模組
2. 按層組織 (By Layer)
另一種方式是按層級放置類別,即所有控制器放在 controllers 包中,服務放在 services 包中。
com.example.demo
├── DemoApplication.java
├── controller/
│ ├── CustomerController.java
│ └── OrderController.java
├── service/
│ ├── CustomerService.java
│ └── OrderService.java
├── repository/
│ ├── CustomerRepository.java
│ └── OrderRepository.java
├── model/
│ ├── Customer.java
│ └── Order.java
├── dto/
│ ├── CustomerRequest.java
│ ├── CustomerResponse.java
│ ├── OrderRequest.java
│ └── OrderResponse.java
└── config/
├── SecurityConfig.java
└── DatabaseConfig.java
缺點:
- 難以模組化:功能或模組無法單獨發佈
- 維護困難:難以定位與特定功能相關的類別
- 重構複雜:對特定功能進行重構困難,因為相關類別分散在各層
實際專案結構範例
小型專案結構
src/main/java/com/example/demo/
├── DemoApplication.java
├── config/
│ ├── SecurityConfig.java
│ └── WebConfig.java
├── controller/
│ └── HomeController.java
├── service/
│ └── UserService.java
├── repository/
│ └── UserRepository.java
├── model/
│ └── User.java
└── dto/
├── UserRequest.java
└── UserResponse.java
中大型專案結構 (推薦)
src/main/java/com/example/ecommerce/
├── EcommerceApplication.java
├── config/
│ ├── SecurityConfig.java
│ ├── DatabaseConfig.java
│ └── CacheConfig.java
├── user/
│ ├── User.java
│ ├── UserController.java
│ ├── UserService.java
│ ├── UserRepository.java
│ └── dto/
│ ├── UserRegistrationRequest.java
│ ├── UserProfileResponse.java
│ └── UserUpdateRequest.java
├── product/
│ ├── Product.java
│ ├── ProductController.java
│ ├── ProductService.java
│ ├── ProductRepository.java
│ ├── category/
│ │ ├── Category.java
│ │ ├── CategoryService.java
│ │ └── CategoryRepository.java
│ └── dto/
│ ├── ProductRequest.java
│ └── ProductResponse.java
├── order/
│ ├── Order.java
│ ├── OrderItem.java
│ ├── OrderController.java
│ ├── OrderService.java
│ ├── OrderRepository.java
│ ├── payment/
│ │ ├── PaymentService.java
│ │ └── PaymentProvider.java
│ └── dto/
│ ├── OrderRequest.java
│ └── OrderResponse.java
└── common/
├── exception/
│ ├── GlobalExceptionHandler.java
│ ├── BusinessException.java
│ └── ResourceNotFoundException.java
├── util/
│ ├── DateUtils.java
│ └── ValidationUtils.java
└── constant/
└── AppConstants.java
資源檔案結構
src/main/resources/
├── application.yml # 主配置檔
├── application-dev.yml # 開發環境配置
├── application-prod.yml # 生產環境配置
├── application-test.yml # 測試環境配置
├── db/
│ ├── migration/
│ │ ├── V1__Create_user_table.sql
│ │ └── V2__Create_product_table.sql
│ └── data/
│ └── test-data.sql
├── static/ # 靜態資源
│ ├── css/
│ ├── js/
│ └── images/
├── templates/ # 模板檔案
│ ├── index.html
│ └── error.html
└── messages/ # 國際化資源
├── messages.properties
├── messages_en.properties
└── messages_zh_TW.properties
測試結構
src/test/java/com/example/demo/
├── DemoApplicationTests.java
├── user/
│ ├── UserControllerTest.java
│ ├── UserServiceTest.java
│ └── UserRepositoryTest.java
├── product/
│ ├── ProductControllerTest.java
│ └── ProductServiceTest.java
└── integration/
├── UserIntegrationTest.java
└── ProductIntegrationTest.java
最佳實踐建議
1. 命名約定
- 包名:使用小寫、單數、簡潔的名稱
- 類別名:使用 PascalCase,加上適當的後綴
- Controller:
UserController - Service:
UserService - Repository:
UserRepository - DTO:
UserRequest,UserResponse
- Controller:
2. 分層架構
// Controller 層 - 處理 HTTP 請求
@RestController
@RequestMapping("/api/users")
public class UserController {
// 僅處理 HTTP 相關邏輯
}
// Service 層 - 業務邏輯
@Service
@Transactional
public class UserService {
// 業務邏輯處理
}
// Repository 層 - 資料存取
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 資料存取方法
}
3. 配置管理
// 將配置集中管理
@Configuration
@EnableConfigurationProperties({DatabaseProperties.class, CacheProperties.class})
public class AppConfig {
// 配置 Bean
}
@ConfigurationProperties(prefix = "app.database")
@Data
public class DatabaseProperties {
private String url;
private String username;
private String password;
}
4. 異常處理
// 全局異常處理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
// 處理業務異常
}
}
// 自訂異常
public class BusinessException extends RuntimeException {
private final String errorCode;
// 異常實作
}
總結
相對於其他框架常見的 controller/model/middleware 等目錄結構,在 Spring Boot 下,若功能耦合性低,依各功能需求來設計專案目錄結構是更適合的方式。
選擇建議:
- 小型專案 (少於 10 個類別):可以使用按層組織
- 中大型專案:強烈建議使用按功能組織
- 微服務架構:每個服務都應該按功能組織
這樣的組織方式能夠提高代碼的可維護性、可測試性和團隊協作效率。