Go 項目目錄結構指南
擁有一個井然有序的目錄結構非常重要,原因有幾個:
-
層次分離: 它將項目的不同部分——如業務邏輯、數據訪問和 API 處理——放在各自的文件夾中。這樣,開發人員可以專注於特定領域,而不會感到不知所措或困惑。
-
更好的組織: 通過整齊地整理你的代碼及其依賴項,較大的項目變得更容易導航。你總是知道在哪裏找到你需要的東西!
-
可重用性: 一個良好的結構讓你可以創建可以在項目不同部分使用的組件或包,從而節省時間和精力。
-
更易維護: 當每個人都遵循一致的文件夾設置時,查找和更新代碼變得簡單得多。這有助於項目的長期管理。
-
模塊化: 有效的目錄結構鼓勵模塊化設計,使得隨着項目的增長,添加新功能變得更加容易。
1. 常見的目錄結構
1.1 扁平結構
扁平目錄結構將所有文件直接存儲在根目錄中,沒有深層嵌套或多個子目錄。這種簡單性使得導航和管理變得容易,特別適用於小型或簡單的項目。
project/
├── main.go # 應用程序入口點
├── handler.go # HTTP 處理器
├── service.go # 業務邏輯
├── repository.go # 數據訪問
├── config.go # 配置設置
├── utils.go # 工具函數
├── go.mod # Go 模塊文件
└── go.sum # Go 模塊依賴文件
使用場景:
-
小型項目: 適合複雜度低的項目,允許開發者在少量文件中管理所有功能。
-
原型和最小可行產品: 非常適合快速構建原型或最小可行產品(MVP),便於快速迭代,無需整理文件夾。
-
命令行工具: 非常適合小型命令行應用程序,使得執行和測試變得簡單明瞭。
-
學習與實驗: 對於學習新編程語言或框架的初學者來說,這個目錄結構非常友好,因爲它簡化了文件組織的複雜性。
1.2 分層結構
在分層結構中,應用程序被組織成不同的層次,例如控制器(或處理程序)、服務、倉儲層和模型。每一層都有其特定的角色,使代碼更易於管理。
project
├── cmd # 與命令相關的文件
│ └── app # 應用程序入口點
│ └── main.go # 主應用程序邏輯
├── internal # 內部代碼庫
│ ├── handlers # HTTP 請求處理器(控制器)
│ │ └── user_handler.go # 用戶相關的處理器
│ ├── services # 業務邏輯(服務層)
│ │ └── user_service.go # 用戶相關的服務
│ ├── repositories # 數據訪問(倉儲層)
│ │ └── user_repo.go # 用戶相關的倉儲
│ └── models # 數據模型(實體)
│ └── user.go # 用戶模型
├── pkg # 通用的工具或輔助函數
├── configs # 配置文件
├── go.mod # Go 模塊定義文件
└── go.sum # Go 模塊校驗和文件
使用場景:
-
中型項目: 適合需要在不同功能和層之間進行清晰分離的應用。
-
關注點分離: 促進一種清晰的架構,其中處理程序僅負責管理請求,將業務邏輯委託給服務,將數據訪問委託給倉儲層。
-
單元測試: 每個層級都可以獨立進行單元測試,確保一個層級的更改不會影響其他層級,從而提高可維護性。
1.3 領域驅動設計
在領域驅動設計(DDD)中,應用程序被劃分爲領域或邊界上下文,每個領域擁有自己的層,包括模型、倉儲層和服務。這種方法有助於隔離邏輯,使代碼具有領域特定性,從而促進更好的組織和清晰度。
project
├── cmd # 與命令相關的文件
│ └── app # 應用程序入口點
│ └── main.go # 主應用程序邏輯
├── internal # 內部代碼庫
│ ├── user # 用戶領域
│ │ ├── handler.go # 用戶相關的處理器
│ │ ├── service.go # 用戶相關的服務
│ │ ├── repository.go # 用戶相關的倉儲
│ │ └── user.go # 用戶模型
│ └── product # 產品領域
│ ├── handler.go # 產品相關的處理器
│ ├── service.go # 產品相關的服務
│ └── repository.go # 產品相關的倉儲
├── pkg # 通用工具
├── configs # 配置文件
├── go.mod # Go 模塊定義文件
└── go.sum # Go 模塊校驗和文件
使用場景:
-
複雜項目: 最適合具有多個業務領域的應用,確保每個領域可以獨立演變而不影響其他領域。
-
解耦與可擴展性: 當應用程序的不同部分需要隔離以實現可擴展性和可維護性時,這一點非常有用。
-
模塊化: 促進模塊化,因爲每個領域可以單獨管理和開發,允許團隊在特定領域內工作而不產生衝突。
1.4 整潔架構
整潔架構將應用程序組織成不同的層,每一層的依賴關係都明確指向內層。這樣的結構確保核心業務邏輯獨立於外部系統,例如數據庫、框架或用戶界面。
project
├── cmd/ # 與命令相關的文件
│ └── app/ # 應用程序入口點
│ └── main.go # 主應用程序邏輯
├── internal # 內部代碼庫
│ ├── delivery/ # 外部層(HTTP 處理器、gRPC 等)
│ │ └── http/ # HTTP 交付層
│ │ └── user_handler.go # 用戶相關的 HTTP 處理器
│ ├── usecases/ # 用例(業務邏輯層)
│ │ └── user_service.go # 用戶相關的服務邏輯
│ ├── repository/ # 倉儲層(數據訪問、外部服務)
│ │ └── user_repo.go # 用戶相關的數據訪問邏輯
│ └── entities/ # 實體層(核心模型、領域對象)
│ └── user.go # 用戶模型
├── pkg/ # 通用工具或輔助函數
├── configs/ # 配置文件
├── go.mod # Go 模塊定義文件
└── go.sum # Go 模塊校驗和文件
關鍵原則:
-
核心實體: 領域模型是架構的核心,確保業務邏輯是主要關注點。
-
用例: 封裝特定於應用的業務規則,定義實體之間的交互方式。
-
交付層: 通過各種接口(API、命令行界面、gRPC)管理外部交互。
-
倉儲層: 處理數據訪問,允許與數據庫和第三方服務進行交互,而不與業務邏輯耦合。
使用場景:
-
大型可擴展項目: 適合需要隨着時間適應和擴展的應用程序,而不影響核心功能。
-
獨立測試: 便於在不依賴框架、數據庫或其他外部服務的情況下輕鬆測試核心邏輯,從而增強可維護性和靈活性。
1.5 模塊化結構
模塊化結構將應用程序組織成鬆散耦合、自包含的模塊,每個模塊都有自己的代碼庫。這種方法使每個模塊能夠專注於特定的功能或業務單元,從而提高了可重用性和可擴展性。
project/
├── user_module/ # 用戶模塊
│ ├── handler/ # 用戶相關請求的處理器
│ ├── service/ # 用戶操作的業務邏輯
│ ├── repository/ # 用戶數據的數據訪問層
│ ├── user.go # 用戶模型定義
│ ├── go.mod # 用戶模塊的 Go 模塊定義文件
│ └── go.sum # 用戶模塊的 Go 模塊校驗和文件
├── product_module/ # 產品模塊
│ ├── handler/ # 產品相關請求的處理器
│ ├── service/ # 產品操作的業務邏輯
│ ├── repository/ # 產品數據的數據訪問層
│ ├── product.go # 產品模型定義
│ ├── go.mod # 產品模塊的 Go 模塊定義文件
│ └── go.sum # 產品模塊的 Go 模塊校驗和文件
├── api_gateway/ # API 網關,用於管理不同服務
│ ├── main.go # API 網關的主入口
│ ├── go.mod # API 網關的 Go 模塊定義文件
│ └── go.sum # API 網關的 Go 模塊校驗和文件
└── configs/ # 共享配置文件
使用場景:
-
微服務: 適合基於微服務架構構建的應用程序,在這種架構中,不同的團隊可以獨立地在各自的模塊上工作。
-
解耦與可擴展性: 明確的關注點分離,使每個模塊能夠獨立開發、測試和部署。
-
獨立依賴管理: 每個模塊可以擁有自己的
go.mod
文件,便於單獨管理依賴關係,減少模塊之間的衝突。
1.6 基於功能的目錄結構
在基於功能的目錄結構中,應用程序的每個功能或特性都被視爲一個獨立的單元。與該功能相關的所有代碼,包括處理器、服務、數據倉庫等,都集中放在該功能的目錄下。這種方法增強了功能內部的內聚性,並促進了功能特定邏輯的封裝。
project/
├── cmd/
│ └── app/
│ └── main.go # 主應用邏輯
├── internal/
│ ├── user/ # 功能模塊:用戶
│ │ ├── handler/ # 用戶相關的 HTTP 處理器
│ │ ├── service/ # 用戶相關的業務邏輯
│ │ ├── repository/ # 用戶相關的數據訪問層
│ │ └── user.go # 用戶模型
│ ├── product/ # 功能模塊:產品
│ │ ├── handler/ # 產品相關的 HTTP 處理器
│ │ ├── service/ # 產品相關的業務邏輯
│ │ ├── repository/ # 產品相關的數據訪問層
│ │ └── product.go # 產品模型
│ ├── order/ # 功能模塊:訂單
│ │ ├── handler/ # 訂單相關的 HTTP 處理器
│ │ ├── service/ # 訂單相關的業務邏輯
│ │ ├── repository/ # 訂單相關的數據訪問層
│ │ └── order.go # 訂單模型
├── pkg/ # 公共工具或輔助功能
│ └── logger.go # 日誌工具
├── configs/ # 配置文件
├── go.mod # Go 模塊定義文件
└── go.sum # Go 模塊依賴校驗文件
特點:
-
封裝: 每個特性封裝了自己的邏輯、模型和層,最小化不必要的依賴。
-
可擴展性: 該結構可以隨着新功能的添加而輕鬆增長,只需創建新的功能目錄即可。
-
關注業務需求: 由於組織圍繞功能運作,因此與業務需求緊密對接,使開發團隊更易於理解。
-
高內聚性: 每個功能相關的代碼被歸類在一起,這使得維護和增強特定功能的代碼庫變得更加容易。
使用場景:
-
大型應用程序: 最適合由多個獨立功能組成的大型應用程序,例如用戶管理、產品目錄或訂單處理,這些功能可以邏輯上分開。
-
功能團隊: 特別適用於圍繞功能所有權組織的團隊,每個團隊負責從頭到尾管理一個功能,從而增強協作和責任感。
1.7 六邊形架構
六邊形架構,通常稱爲端口與適配器,是一種強調將核心業務邏輯與外部依賴分離的架構模式。在這種結構中,核心通過端口(即接口)與外部世界進行交互,而數據庫和網絡框架等外部系統則充當實現這些接口的適配器。
project/
├── cmd/
│ └── app/
│ └── main.go # 主應用邏輯
├── internal/
│ ├── core/ # 核心業務邏輯(實體和用例)
│ │ ├── user/ # 用戶功能模塊
│ │ │ ├── entity.go # 用戶實體(領域對象)
│ │ │ └── usecase.go # 業務邏輯(用例)
│ │ └── product/ # 產品功能模塊,結構相同
│ │ └── ... # 產品相關文件
│ ├── adapters/ # 實現外部系統接口的適配器
│ │ ├── database/ # 數據庫適配器(倉儲層)
│ │ │ └── user_repo.go # 用戶倉儲實現
│ │ ├── api/ # API 適配器(HTTP 處理器)
│ │ │ └── user_handler.go # 用戶相關的 HTTP 處理器
│ │ └── messaging/ # 消息系統(例如 Kafka)
│ │ └── user_event.go # 用戶事件消息處理
│ └── ports/ # 端口(接口)
│ ├── user_repository.go # 用戶倉儲接口
│ ├── user_service.go # 用戶服務接口
│ └── ... # 其他端口接口
├── pkg/ # 公共工具
├── configs/ # 配置文件
├── go.mod # Go 模塊定義文件
└── go.sum # Go 模塊依賴校驗文件
特點:
-
核心獨立性: 核心業務邏輯獨立於外部層,如框架、數據庫和 API。這種獨立性增強了可維護性和靈活性。
-
端口(接口): 端口定義了核心業務邏輯如何與外部系統交互,從而在覈心與其適配器之間建立清晰的契約。
-
適配器: 適配器(例如,數據庫、API 和消息傳遞的適配器)可以輕鬆更換,而不會影響核心邏輯。這有助於測試和重構,因爲核心可以與外部依賴項隔離進行測試。
使用場景:
-
解耦業務邏輯: 適用於業務邏輯必須與數據庫、API 或用戶界面框架等外部系統解耦的應用程序,確保靈活性和適應性。
-
變更外部系統的項目: 特別適用於需要靈活性的項目,因爲外部系統可能會隨着時間的推移而變化,從而使應用程序能夠適應,而無需對核心邏輯進行重大重寫。
1.8 Monorepo 結構
Monorepo 是一個包含多個服務或庫的單一代碼庫。這種結構通常在大型組織中使用,使得不同的項目可以獨立開發和部署,同時仍然位於一個統一的代碼庫中。
project/
├── services/ # 服務目錄
│ ├── user-service/ # 用戶服務
│ │ ├── cmd/ # 用戶服務主應用邏輯
│ │ │ └── main.go # 用戶服務的主應用邏輯
│ │ ├── internal/ # 用戶服務的內部代碼庫
│ │ │ ├── handler/ # 用戶特定的 HTTP 處理器
│ │ │ ├── service/ # 用戶特定的業務邏輯
│ │ │ ├── repository/ # 用戶特定的數據訪問
│ │ │ └── models/ # 用戶模型
│ │ ├── go.mod # 用戶服務 Go 模塊定義
│ │ └── go.sum # 用戶服務 Go 模塊校驗和文件
│ └── product-service/ # 產品服務
│ ├── cmd/ # 產品服務的命令相關文件
│ ├── internal/ # 產品服務的內部代碼庫
│ └── go.mod # 產品服務 Go 模塊定義
├── libs/ # 跨多個服務共享的庫
│ ├── logging/ # 日誌庫
│ ├── authentication/ # 認證庫
│ └── utils/ # 工具函數
├── go.mod # 頂層 Go 模塊定義
└── go.sum # 頂層 Go 模塊校驗和文件
特點:
-
多個服務: 每個服務(例如,
user-service
,product-service
)都是獨立的,能夠獨立運行,但它們都位於同一個存儲庫中。 -
共享庫: 常用的工具和庫(如日誌記錄和身份驗證)可以組織在一個
libs/
目錄中,從而允許在不同服務之間重用。 -
共享模塊: 該項目可以有一個單一的頂級
go.mod
文件用於單體倉庫,或者每個服務可以有自己的go.mod
文件,這取決於所需的模塊化程度。 -
代碼共享與一致性: 鼓勵共享代碼並在多個服務之間保持一致性,減少重複並確保標準化。
使用場景:
-
大型組織: 適合擁有多個團隊的大型組織,這些團隊在獨立服務上工作,同時需要共享庫和一致的實踐。
-
微服務架構: 特別適用於微服務架構或多服務系統,因爲它簡化了在單一代碼庫內的協作和協調。
1.9 CQRS(Command Query Responsibility Segregation)
在命令查詢責任分離(CQRS)中,命令和查詢操作被組織成獨立的模型。命令負責改變狀態(例如,創建或更新數據),而查詢則專注於讀取數據。這種結構增強了處理請求時的關注點分離。
project/
├── cmd/ # 命令目錄
│ └── app/ # 應用程序
│ └── main.go # 主應用邏輯
├── internal/ # 內部代碼庫
│ ├── commands/ # 命令(修改狀態)
│ │ ├── create_user.go # 創建用戶的命令
│ │ ├── update_user.go # 更新用戶的命令
│ │ └── delete_user.go # 刪除用戶的命令
│ ├── queries/ # 查詢(讀取數據)
│ │ └── get_user.go # 獲取用戶數據的查詢
│ ├── repositories/ # 數據訪問的倉庫
│ │ └── user_repo.go # 用戶倉庫
│ ├── models/ # 領域模型
│ │ └── user.go # 用戶領域模型
│ └── services/ # 業務邏輯
│ └── user_service.go # 用戶服務邏輯
├── configs/ # 配置文件
├── go.mod # Go 模塊定義
└── go.sum # Go 模塊校驗和文件
特點:
-
職責分離: 命令和查詢被劃分爲不同的模型,從而簡化了代碼庫的擴展和維護。
-
可擴展架構: 該結構特別適合需要單獨優化讀寫操作的應用,非常適合大規模系統。
-
解耦: 促進命令和查詢處理邏輯之間的明確分離,從而更容易管理這兩種操作。
使用場景:
-
不同的讀寫需求: 最適合需要以不同方式管理讀寫操作的應用,特別是在事件驅動架構或高性能應用中。
-
事件源和一致性: 對於需要事件源或具有複雜數據一致性要求的系統非常有用,能夠更清晰地處理狀態變化。
1.10 洋蔥架構
洋蔥架構強調通過將代碼組織成圍繞中心核心的同心圓(或層)來實現關注點分離。最內層代表業務邏輯和領域模型,而外層則包含外部依賴,如框架、用戶界面和數據庫。
project/
├── cmd/ # 命令目錄
│ └── your-app/ # 應用程序
│ └── main.go # 應用程序入口點
├── internal/ # 內部代碼庫
│ ├── domain/ # 核心業務邏輯和實體
│ │ ├── entity.go # 領域實體
│ │ └── service.go # 領域服務
│ ├── application/ # 應用服務
│ │ ├── usecase.go # 與領域交互的用例
│ └── infrastructure/ # 外部基礎設施
│ ├── persistence/ # 數據庫實現
│ │ ├── repository.go # 數據庫交互的倉庫
│ ├── api/ # Web API 層
│ │ └── handler.go # HTTP 處理器
│ └── ... # 其他外部服務(消息代理等)
├── pkg/ # 跨項目共享代碼(助手,工具)
│ └── shared/ # 共享代碼
├── configs/ # 配置文件(YAML,JSON 等)
│ └── config.yaml
└── go.mod # Go 模塊文件
特點:
-
分層方法: 該架構呈同心圓結構,最內層代表核心領域,外層則代表應用程序的接口和基礎設施。
-
依賴倒置: 核心領域邏輯與外部框架和技術保持獨立,促進可維護性和可測試性。
-
關注點分離: 每個層次都有明確的職責,有助於隔離變更並最小化副作用。
-
專注於業務邏輯: 核心業務邏輯處於中心位置,使應用程序能夠發展而不影響外部依賴。
-
可測試性: 由於核心邏輯與基礎設施解耦,因此更容易爲領域層和應用層編寫單元測試。
使用場景:
-
複雜業務領域: 適用於具有複雜業務規則的應用,這些應用需要明確的關注點分離。
-
長期項目: 適合那些預計會隨着時間發展而變化的項目,使開發人員能夠在不進行重大重寫的情況下適應業務需求的變化。
-
明確角色的團隊: 當不同的團隊負責應用程序的不同層次時(例如,業務邏輯與基礎設施),效果很好。
1.11 通用架構
通用結構適用於較簡單的項目或場景,在這些情況下,開發速度和易於理解被優先考慮,而不是嚴格的架構指南。它允許靈活性,但從長遠來看可能導致代碼的可維護性降低。
project/
├── cmd/ # 項目的主應用程序
│ └── myapp/ # myapp 可執行文件的目錄
│ └── main.go # 應用程序入口點
├── internal/ # 私有應用程序和庫代碼
│ ├── app/ # 特定應用程序代碼
│ │ └── myapp/ # myapp 的應用程序代碼
│ │ ├── handler.go # HTTP 處理器
│ │ └── service.go # 業務邏輯
│ ├── pkg/ # 共享的內部代碼(私有庫)
│ │ └── myprivlib/ # 內部使用的共享庫
│ ├── domain/ # 領域模型和服務
│ │ ├── entity.go # 領域實體
│ │ └── service.go # 領域服務
│ └── infrastructure/ # 外部基礎設施代碼
│ ├── persistence/ # 數據庫實現
│ │ └── repository.go # 數據庫交互的倉庫
│ ├── api/ # Web API 層
│ │ └── handler.go # API 處理器
│ └── messaging/ # 消息系統(如 Kafka)
│ └── producer.go # 消息生產者
├── pkg/ # 對外使用的庫代碼
│ └── mypubliclib/ # 對外的公共庫
├── vendor/ # 應用程序依賴
├── api/ # OpenAPI/Swagger 規範,JSON 架構文件
│ └── api_spec.yaml # API 規範文件
├── web/ # Web 應用程序組件
│ ├── static/ # 靜態資源(CSS,JS)
│ └── templates/ # 服務器端模板
├── configs/ # 配置文件
│ └── config.yaml # 默認配置
├── init/ # 系統初始化配置
│ └── myapp.service # Systemd 服務文件
├── scripts/ # 構建、安裝、分析腳本
│ ├── build.sh # 構建腳本
│ └── install.sh # 安裝腳本
├── build/ # 打包和 CI 配置
│ ├── package/ # 打包配置(Docker,AMI 等)
│ └── ci/ # CI 配置(Travis,CircleCI)
├── deployments/ # 部署配置和模板
│ └── kubernetes/ # Kubernetes 部署配置
├── test/ # 額外的外部測試應用和數據
│ └── data/ # 測試數據
├── docs/ # 設計和用戶文檔
│ └── architecture.md # 項目架構文檔
├── tools/ # 支持項目的工具
│ └── mytool/ # 示例工具目錄
├── examples/ # 應用程序和庫的示例
│ └── example_usage.go # 公共庫的示例用法
├── third_party/ # 外部輔助工具和實用程序
├── githooks/ # Git hooks
├── assets/ # 其他資產(圖片,LOGO)
├── website/ # 項目網站數據
│ └── index.html # 項目的首頁
├── go.mod # Go 模塊文件
└── go.sum # Go 模塊依賴文件
特點:
-
簡潔性: 一種簡單明瞭的結構,能夠組織代碼而不顯得過於複雜。
-
單層抽象: 常見結構可能不會像洋蔥架構或整潔架構那樣強制執行嚴格的分層,從而導致抽象層次較少。
-
最小化依賴管理: 減少管理依賴的開銷,使新來者更容易理解項目佈局。
-
靈活的代碼組織: 開發者可以根據項目需求組織代碼,而不是嚴格遵循架構指南,從而允許創造性的解決方案。
使用場景:
-
小型到中型項目: 適合較小的應用程序或項目,在這些項目中,複雜性是可控的,嚴格的關注點分離可能顯得過於繁瑣。
-
快速原型製作: 有助於在不需要維持嚴格架構框架的情況下快速迭代想法。
-
單開發者項目: 適合那些喜歡簡單、結構較少環境的個人開發者來構建應用程序。
-
學習與實驗: 適合初學者學習 Go 或那些嘗試新想法的人,因爲它允許探索而不被架構決策所困擾。
2. 結語
選擇合適的目錄結構至關重要,但是這取決於你當前的需求,比如程序大小,複雜性,團隊大小,可維護性等等。
通常,選擇一個滿足當前需求的結構就可以,隨着項目的發展或需求的變化可以進行重構。
但從實際工作的角度看,如果處在一個長期需求維護的項目團隊時,建議選擇可維護性,可擴展性,職責分離明確的目錄結構更好。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/8nuxeGruMZAC2nKIB0p8Kg