•
Kotlin 後端專案:為何 Service Layer 是不可或缺的一層?
14 分鐘閱讀 •
其實在學 Kotlin 的不是我,是另一位認識的網友。
我幫他 code review 之後發現他沒有做 service layer。我的問句是一種 使用 AI 來產生詳細指引 的提問法,當資深人員在產出指南給新人時非常適合這種提示詞技巧。
當 Controller 直接調用 Repository 時,雖然在簡單情境下看似便捷,但隨著應用程式複雜度的增加,會導致多種問題,例如業務邏輯分散、關注點混淆以及測試困難。引入 Service Layer 作為 Controller 和 Repository 之間的中介層,能夠有效地封裝業務邏輯、促進關注點分離,並簡化各層的職責,從而構建更健壯、更易於管理的應用程式。本報告將闡述不應直接在 Controller 中使用 Repository 的原因、Service Layer 帶來的好處,以及從現有架構遷移至包含 Service Layer 架構的具體步驟。
Controller、Service 與 Repository 的職責
在典型的三層式架構中,各層的職責如下:
Controller (控制層): 作為應用程式的入口點,負責接收客戶端的請求(例如 HTTP 請求),解析請求參數,並調用 Service Layer 處理業務邏輯。處理完成後,Controller 會將 Service Layer 返回的結果組裝成適當的回應(例如 JSON、HTML)返回給客戶端 123。Controller 應保持輕量,主要關注請求的接收與轉發,以及回應的呈現 45。
Service (服務層): 包含應用程式的核心業務邏輯 62。它協調對 Repository 的調用以進行數據持久化操作,執行業務規則、數據驗證、交易管理等 678。Service Layer 封裝了複雜的業務流程,並向上層(Controller)提供清晰定義的接口 6910。
Repository (倉儲層/持久層): 負責與數據儲存(例如資料庫)進行交互,執行數據的創建 (Create)、讀取 (Read)、更新 (Update) 和刪除 (Delete) 等 CRUD 操作 1611。Repository 抽象了數據訪問的細節,使得上層(通常是 Service Layer)無需關心具體的數據庫實現 112。
為何不建議在 Controller 中直接使用 Repository
儘管在某些極其簡單的 CRUD 操作中,Controller 直接調用 Repository 看似可以減少程式碼量,但從長遠來看,這種做法會帶來以下問題:
違反關注點分離 (Violation of Separation of Concerns): Controller 的主要職責是處理 HTTP 請求和回應流程,以及基本的輸入驗證 13。如果直接在 Controller 中調用 Repository,會將數據訪問邏輯甚至部分業務邏輯耦合到 Controller 中,導致 Controller 職責不清、變得臃腫,難以理解和維護 1413。
降低程式碼可重用性 (Reduced Code Reusability): Repository 通常設計為可被應用程式中多個部分(例如不同的 Service)重用的組件 13。如果將 Repository 直接注入到 Controller 中,其重用性會受到限制 1413。業務邏輯如果散落在多個 Controller 中,也難以被重用 4。
增加測試複雜度 (Increased Testing Complexity): 當 Controller 與 Repository 緊密耦合時,對 Controller 進行單元測試會變得更加困難 1413。測試 Controller 時,可能需要模擬 Repository 的行為,如果業務邏輯也混雜其中,測試案例會更複雜。
業務邏輯分散 (Scattered Business Logic): 如果沒有 Service Layer,業務邏輯可能會分散在各個 Controller 中,或者甚至部分洩漏到 Repository 層 1415。這使得業務規則難以集中管理和修改。
交易管理困難 (Difficulty in Transaction Management): 交易管理通常涉及多個數據庫操作的原子性。Service Layer 是實施交易管理的理想場所 6。如果 Controller 直接操作 Repository,交易邊界的劃分會變得不清晰或需要在 Controller 層處理,這並不符合其職責。
Service Layer 的優勢
引入 Service Layer 可以帶來諸多好處,有效解決上述問題:
封裝業務邏輯 (Encapsulation of Business Logic): Service Layer 是應用程式核心業務邏輯的集中地 468。所有業務規則、計算、流程協調都在這一層完成,使得業務邏輯更易於管理和修改 89。
提升程式碼可重用性 (Improved Code Reusability): 封裝在 Service 中的業務邏輯可以被多個 Controller 或其他 Service 重用,減少了程式碼重複 4138。
簡化 Controller (Simplified Controllers): Controller 的職責回歸到處理 HTTP 請求和回應,以及調用 Service 45。Controller 變得更輕量、更專注,易於理解和維護 416。
易於測試 (Easier Testing): Service Layer 可以獨立於 Controller 和 Repository 進行單元測試 413。由於業務邏輯集中在 Service 中,可以針對性地編寫測試案例,驗證業務規則的正確性 1317。
明確的交易管理 (Clear Transaction Management): Service Layer 是實施交易管理的理想位置 6。可以確保涉及多個數據庫操作的業務功能在一個事務中完成,保證數據的一致性 6。
促進關注點分離 (Promotion of Separation of Concerns): Service Layer 作為 Controller 和 Repository 之間的中介,清晰地劃分了表現邏輯、業務邏輯和數據訪問邏輯,使得整體架構更加清晰和模組化 4138。
提高可維護性和可擴展性 (Enhanced Maintainability and Scalability): 清晰的分層和職責劃分使得系統更容易維護 48。當需求變更或新增功能時,可以更容易地定位和修改相關程式碼,同時也方便系統的擴展 818。
從「Controller 中調用 Repository」遷移至「擁有 Service Layer」的 TODO 工作步驟
將現有的直接在 Controller 中調用 Repository 的 Kotlin 後端專案遷移到包含 Service Layer 的架構,可以遵循以下步驟:
分析現有 Controller:
- 識別 Controller 中直接調用 Repository 的部分。
- 找出 Controller 中包含的任何業務邏輯(例如數據轉換、條件判斷、多個 Repository 操作的協調等)。
定義 Service 接口 (Interface):
- 根據識別出的業務邏輯和 Controller 的需求,為每個主要的業務功能或實體定義 Service 接口。
- 接口應描述業務操作,而不是數據庫操作。
實作 Service 類別 (Implementation Class):
重構 Controller:
處理數據傳輸對象 (DTOs):
配置依賴注入 (Dependency Injection):
編寫單元測試:
- 為新的 Service Layer 編寫單元測試,驗證其業務邏輯的正確性。
- 更新或編寫 Controller 的單元測試,模擬 Service 的行為。
進行整合測試:
- 執行整合測試,確保 Controller、Service 和 Repository 之間的交互按預期工作。
程式碼審查與重構:
- 對遷移後的程式碼進行審查,確保其符合設計原則和最佳實踐。
- 進行必要的重構以提高程式碼品質。
逐步遷移 (Optional but Recommended for Large Projects):
- 對於大型專案,可以考慮逐步遷移,例如先選擇一個或幾個模組進行改造,驗證可行性後再擴展到整個專案。
Laravel中服务层(Service Layer)使用详解原创 - CSDN博客 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
Understanding the Service Layer in Software Architecture ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
Understanding the Service Layer in Software Architecture | by LadlaAqeel | Medium ↩ ↩2
Service Layer Pattern in Java: Enhancing Application Architecture with Robust Service Layers | Java Design Patterns ↩
Best Practices in Spring Boot: Avoid Injecting Repositories Directly into Controllers | by Vidushika Dasanayaka | Nerd For Tech | Medium ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
Best Practices in Spring Boot: Avoid Injecting Repositories ... ↩ ↩2 ↩3 ↩4
Should I always use service, or can I use the repositories ... ↩
SpringBoot的Controller,Service,Repository层的使用 - CSDN博客 ↩ ↩2 ↩3 ↩4 ↩5
Is it a bad practice that controller uses repository directly? ↩