淺談六角架構中的 Port 與 Adpater
現今許多開發團隊表面上採用分層架構,但就我的實務上的經驗,我們終究會因為 Entity to Domain 的轉換邏輯而讓專案架構更貼近六角架構。
前提是大家都有乖乖寫單元測試的情況下哈,這篇文章就來聊一下,讓專案更貼近六角架構的過程。
以下為 六角架構的基本概念圖:
從三層架構到六角架構的演變
在軟體開發的演進過程中,三層架構(Three-tier Architecture)一直是最常見的架構模式之一。它將應用程式分為:
- 表現層(Presentation Layer):負責使用者互動和資料顯示
- 業務邏輯層(Business Logic Layer):實現業務規則和流程
- 資料存取層(Data Access Layer):處理資料的持久化操作
在傳統的三層架構中,Repository 模式被廣泛應用於資料存取層,用於封裝資料操作邏輯。然而,隨著專案複雜度增加和測試需求的提升,我們發現傳統的 Repository 模式存在一些限制。
三層架構中 Repository 的局限性
在三層架構中,Repository 通常直接返回資料庫實體(Entity),業務邏輯層直接使用這些實體。這種方式存在以下問題:
- 強耦合:業務邏輯層直接依賴於資料層的實體結構,當資料模型變化時,可能需要修改大量業務邏輯
- 邊界模糊:資料庫實體往往包含持久化相關的屬性和方法,這些與業務邏輯無關
- 測試困難:業務邏輯與資料存取的強耦合使得單元測試變得困難
- 職責混淆:Entity 同時扮演持久化模型和領域模型的角色,違反單一職責原則
為什麼要將 Repository 細分為 Repository 和 Adapter
我將 Repository 模式拆分為兩個關鍵部分:
- Adapter (聯繫):與外界的溝通層,其內部主要時做與 Table 的 CRUD 操作,並回傳 Entity 物件。
- Repository(實現):作為上圖中的 Port, 主要實作將 Adapter 回傳的 Entity 轉換為 Domain Object,並提供業務邏輯所需的操作。
回頭看以下上面的六角架構,我的 Repository 實際上就是 Port。
這種分離帶來以下關鍵優勢:
1. 依賴反轉原則的應用
通過 Adapter 的引入,我們實現了依賴反轉原則(Dependency Inversion Principle)。業務核心不再依賴具體的資料存取實現,而是依賴於 Adapter 介面, Repository 則是依賴於這些介面,形成依賴方向的反轉。
傳統三層架構:Business Logic Layer → Repository → Database
類六角架構: Business Logic Layer -> Repository → Adapter -> Database
2. 實體轉換的顯式化
最關鍵的是,這種架構明確區分了兩種不同的模型:
- Entity(實體):資料庫映射模型,關注持久化
- Domain Object(領域對象):業務邏輯模型,包含業務規則和行為
在 Repository 和領域模型之間,需要進行 Entity to Domain 的轉換,這個轉換過程是六角架構的關鍵部分。
Entity to Domain 轉換邏輯的重要性
為什麼這個轉換如此重要?
- 關注點分離:資料模型和業務模型各司其職,避免混雜
- 領域完整性:確保業務規則只存在於領域模型中,而不滲透到資料層
- 防腐層(Anti-corruption Layer):保護領域模型不受外部資料模型變化的影響
- 演進彈性:允許資料模型和領域模型獨立演進
我自己的經驗是,因為當初的 team 隱藏了轉換邏輯在 Repository 裡面,導致後來的團隊在修改 Entity 時,忘記了這些轉換邏輯,最後被開了幾張 tickets。
所以才需要單元測試
這些 Entity 到 Domain 的轉換邏輯雖然看似簡單,但隱藏著許多風險:
- 類型轉換錯誤:如將數字轉為字符串,布爾值轉為數字等過程可能出錯
- 業務規則體現:某些轉換可能包含業務規則的應用
- 完整性保證:確保所有必要字段都被正確轉換
- 邊緣情況處理:處理空值、特殊值等邊緣情況
單元測試的角色
編寫這些轉換邏輯的單元測試可以:
- 驗證正確性:確保轉換過程正確無誤
- 作為文件:測試用例本身成為說明轉換邏輯的文件
- 預防回歸:避免未來修改破壞已有的轉換邏輯
- 促進設計:通過測試先行(TDD)引導更好的轉換邏輯設計
六角架構如何自然促成這種分離
在實踐中,當團隊認真對待單元測試時,自然會發現傳統三層架構中的痛點,專案就會逐漸向六角架構靠攏:
- 測試需求驅動解耦:為了測試業務邏輯,團隊會尋找方法將其與資料存取分離
- 模擬外部依賴:單元測試需要模擬外部依賴,這自然導致介面的抽象化
- 關注點分離:測試不同層級的 code 促使開發者明確每層的職責
正如文章開頭所說,當團隊都有乖乖的寫單元測試時,架構自然會向六角架構演進。
因為我們的測試都與專案的領域有關,當測試一多也就代表領域事件多,領域事件一多自然專案也就會逐漸貼齊六角架構囉。
結論
從三層架構中的單一 Repository 演變為六角架構中的 Adapter 和 Repository 分離,不僅是架構設計上的進步,更是對軟體開發原則的更好實踐。
這種分離的核心價值在於:
- 明確了 Entity 和 Domain 模型的界限
- 使轉換邏輯變得顯式且可測試
- 降低了系統各部分之間的耦合
- 提高了系統的可維護性和靈活性
最終,這不僅僅是架構上的選擇,更是一種思維方式的轉變 - 從資料驅動的思維轉向領域驅動的思維。