在前面的文章中,我們討論了選擇六角形架構的「Why」。
現在,是時候深入探討「What」和「How」了:
一個清晰、可維護的 Go 專案,其內部結構應該是什麼樣子?
本文將為專案繪製一份精確的結構地圖,不僅深入 internal
目錄的核心,也涵蓋專案的全貌。
一個專業的 Go 專案不僅僅只有 internal
。讓我們先從鳥瞰視角開始:
/
├── cmd/ # 應用程式進入點
├── deployments/ # 資料庫遷移等部署相關檔案
├── docs/ # 專案文件
├── internal/ # 專案私有程式碼 (核心)
├── pkg/ # 可供外部專案使用的共享庫
├── test/ # 測試輔助工具與資料
├── go.mod # Go 模組定義
└── Makefile # 開發任務自動化腳本
/cmd
: 包含應用程式的 main
函式,是所有程式的起點。這裡的程式碼應該非常精簡,主要負責組裝和啟動應用程式。/deployments
: 存放與部署相關的資源,最常見的就是資料庫遷移 (migration) 腳本。/docs
: 你的專案說明書。所有架構決策、API 文件、操作指南都應存放在此。/internal
: 專案的核心業務邏輯所在,也是我們接下來要深入探討的部分。此目錄下的程式碼無法被外部專案匯入,確保了業務邏輯的封閉性。/pkg
: 如果你的專案中有些程式碼可以被其他專案安全地重用,就應該放在這裡。/test
: 存放測試相關的輔助函式或測試資料 (test data)。internal
:應用程式的心臟地帶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 等。
application
: 生命之火與組裝者。application.go
是 DI (Dependency Injection) 容器,它在應用程式啟動時,將所有鬆散的元件(如 database
的實作、usecase
的邏輯)組裝在一起,注入到需要它們的地方,最終啟動整個應用程式。
config
: 能量來源。負責從環境變數或設定檔中讀取應用程式所需的所有設定值,並以結構化的方式提供給其他元件使用。
help
: 輔助工具。提供專案內部使用的通用輔助函式,這些函式不屬於任何特定的業務領域。
這種精細的劃分,確保了每個套件的職責都非常單一,是大型專案能夠保持清晰、易於測試和維護的關鍵。