介紹六角形架構
什麼是六角形架構?
六角形架構,又稱為Ports and Adapters,或廣義上的Clean Architecture,是由 Alistair Cockburn 提出的一種軟體架構模式。
它的核心思想非常簡單:
保護你的核心業務邏輯,使其與外部世界(例如 UI、資料庫、第三方服務)完全解耦。
舉例
想像你的應用程式是一個城堡。 城堡最核心、最寶貴的是國王和他的寶藏(也就是你的業務邏輯)。 城堡需要與外界溝通(例如接收糧食、派遣軍隊), 但你不想讓任何人隨便就闖進來。 於是,你在城堡上開了幾個固定的城門(Ports),並規定了所有進出的規則。 任何想跟城堡打交道的人,都必須透過這些城門,並使用你指定的交通工具(Adapters)。

對應程式碼
-
內部核心 (Inside / Domain):應用程式的核心業務邏輯。它包含了
Usecase(業務流程)和Domain(業務實體)。這部分程式碼是純粹的,它不應該知道任何外部技術的細節。它不知道資料是存在 MySQL 還是 MongoDB,也不知道請求是來自 HTTP API 還是命令列。 -
Ports:是內部核心與外部世界溝通的介面(Interface)。它定義了「需要做什麼」,但不關心「如何做」。
-
Adapters:是 Ports 的具體實現,是連接外部技術和我們核心業務的「橋樑」。
- Primary/Driving Adapters:驅動我們的應用程式執行。
- Secondary/Driven Adapters:被我們的應用程式所驅動。
最重要的原則:依賴關係倒轉
所有依賴關係都必須指向內部核心。
這意味著:
- Adapters(外部)可以依賴核心(內部)。
- 核心(內部)絕對不能依賴 Adapters(外部)。
好處
-
極高的可測試性:
我們可以輕易地為核心業務邏輯撰寫單元測試,因為它不依賴任何外部服務。我們只需要建立一個「假的」Adapter(Mock Adapter)來模擬資料庫行為,就可以獨立測試業務流程的正確性。
-
技術無關性與靈活性:
我們的核心業務邏輯與外部技術解耦。這意味著,如果我們未來想把 Web 框架從 Gin 換成 Echo,或者把資料庫從 MySQL 換成 PostgreSQL,我們需要做的僅僅是更換或新增一個 Adapter,而核心業務邏輯完全不需要改動。
-
清晰的職責分離:
架構強迫我們思考每一段程式碼的職責。
Controller只負責處理 HTTP 請求與回應,Usecase只負責業務流程,Repository只負責資料庫存取。
這讓程式碼更容易理解和維護。
頂層目錄結構概覽
這邊以一個專業的Go專案當範例
/
├── cmd/ # 應用程式進入點
├── deployments/ # 資料庫遷移等部署相關檔案
├── docs/ # 專案文件
├── internal/ # 專案私有程式碼 (核心)
├── pkg/ # 可供外部專案使用的共享庫
├── test/ # 測試輔助工具與資料
├── go.mod # Go 模組定義
└── Makefile # 開發任務自動化腳本
/cmd: 包含應用程式的main函式,是所有程式的起點。這裡的程式碼應該非常精簡,主要負責組裝和啟動應用程式。/deployments: 存放與部署相關的資源,最常見的就是資料庫遷移 (migration) 腳本。/docs: 你的專案說明書。所有架構決策、API 文件、操作指南都應存放在此。/internal: 專案的核心業務邏輯所在,也是我們接下來要深入探討的部分。此目錄下的程式碼無法被外部專案匯入,確保了業務邏輯的封閉性。/pkg: 如果你的專案中有些程式碼可以被其他專案安全地重用,就應該放在這裡。/test: 存放測試相關的輔助函式或測試資料 (test data)。
深入 internal:應用程式的心臟地帶
遵循 “Ports and Adapters” 架構思想,將其劃分為以下職責分明的子目錄:
internal/
├── application/ # DI 容器與應用程式生命週期
├── config/ # 設定管理
├── controller/ # Primary/Driving Adapters (HTTP Handlers)
├── database/ # Secondary/Driven Adapters (資料庫實作)
├── domain/ # 核心業務實體與規則
├── help/ # 輔助函式庫
├── http/ # HTTP 伺服器、路由與中介層
├── service/ # 外部服務或共享的基礎設施服務
└── usecase/ # 應用程式核心業務邏輯 (Use Cases)
用人體來比喻,逐一解析這些元件:
-
domain: 心臟。- 定義最核心的業務實體 (
User,Order) 和不變的業務規則。 - 純粹、乾淨,不依賴任何外部技術(沒有資料庫、沒有 API 框架)。
- 定義最核心的業務實體 (
-
usecase: 大腦。- 這是應用程式的業務邏輯中心。
- 編排
domain中的實體來完成具體的業務流程(例如:使用者登入、建立訂單)。 - 會定義所需的外部依賴埠(Ports),通常是介面(interfaces),但自己不關心這些介面的具體實作。
-
controller: 五官與神經末梢 (Primary/Driving Adapters)。- 應用程式接收指令的入口。
- 例如,HTTP Handlers 負責解析傳入的請求,驗證參數,然後呼叫對應的
usecase來執行任務,最後將結果格式化回傳給客戶端。
-
database: 手腳 (Secondary/Driven Adapters)。- 是外部依賴的具體實作。
- 負責實作
usecase中定義的資料儲存埠(例如UserRepository介面),提供與 MySQL、PostgreSQL 等資料庫互動的真實邏輯。
-
service: 工具箱。- 封裝那些與核心業務無關,但為應用程式提供基礎設施能力的服務。
- 例如:
- 密碼雜湊 (
password) - JWT 權杖處理 (
token) - 快取 (
cache) - 日誌記錄。
- 密碼雜湊 (
-
http: 神經系統。- 負責所有 HTTP 相關的基礎建設。
- server.go 用於設定和管理伺服器的生命週期;
- route.go 組織所有 API 路由,並將它們指向對應的 controller;
- middleware/ 則存放請求共用的處理邏輯,如日誌、認證、CORS 等。
- 負責所有 HTTP 相關的基礎建設。
-
application: 生命之火與組裝者。- application.go 是 DI (Dependency Injection) 容器,它在應用程式啟動時,將所有鬆散的元件(如 database 的實作、usecase 的邏輯)組裝在一起,注入到需要它們的地方,最終啟動整個應用程式。
-
config: 能量來源。- 負責從環境變數或設定檔中讀取應用程式所需的所有設定值
- 以結構化的方式提供給其他元件使用。
-
help: 輔助工具。- 提供專案內部使用的通用輔助函式,這些函式不屬於任何特定的業務領域。
這種精細的劃分,確保了每個套件的職責都非常單一,是大型專案能夠保持清晰、易於測試和維護的關鍵。