組織 Go 模組
對於剛接觸 Go 的開發人員來說,一個常見的問題是「如何組織我的 Go 專案?」,這牽涉到檔案和資料夾的配置。本文件旨在提供一些準則,協助解答此問題。為充分利用本文件,請務必閱讀教學課程和管理模組來源,熟悉 Go 模組的基本知識。
Go 專案可以包含套件、命令列程式或兩者的組合。本指南會按專案類型進行整理。
基本套件
基本 Go 套件會將所有程式碼放在專案的根目錄中。專案包含一個模組,而模組包含一個套件。套件名稱與模組名稱的最後路徑元件相符。對於需要單一 Go 檔案的非常簡單套件,專案結構如下:
project-root-directory/
go.mod
modname.go
modname_test.go
[在本文檔中,檔案/套件名稱完全是任意的]
假設將此目錄上傳到 GitHub 儲存庫中,網址為 github.com/someuser/modname
,則 go.mod
檔案中的 module
行應該寫成 module github.com/someuser/modname
。
modname.go
中的程式碼使用以下方式宣告套件:
package modname
// ... package code here
然後,使用者可以在其 Go 程式碼中使用以下方式 import
來依賴此套件:
import "github.com/someuser/modname"
Go 套件可以分割成多個檔案,全部都位於同一個目錄中,例如:
project-root-directory/
go.mod
modname.go
modname_test.go
auth.go
auth_test.go
hash.go
hash_test.go
目錄中的所有檔案都宣告 package modname
。
基本命令
基本可執行程式(或命令列工具)的結構會根據其複雜度和程式碼大小而定。最簡單的程式可以包含一個定義了 func main
的單一 Go 檔案。較大的程式可以將其程式碼分割成多個檔案,全部都宣告 package main
project-root-directory/
go.mod
auth.go
auth_test.go
client.go
main.go
這裡的 main.go
檔案包含 func main
,但這只是一個慣例。「main」檔案也可以稱為 modname.go
(對於 modname
的適當值)或其他任何名稱。
假設將此目錄上傳到 GitHub 儲存庫中,網址為 github.com/someuser/modname
,則 go.mod
檔案中的 module
行應該寫成:
module github.com/someuser/modname
使用者應該可以使用以下方式在他們的機器上安裝它:
$ go install github.com/someuser/modname@latest
包含支援套件的套件或命令
較大型的套件或指令可能會受益於將部分功能拆分為支援套件。最初,建議將此類套件放入名為 internal
的目錄中;這會防止 其他模組依賴於我們不一定想要公開和支援外部使用的套件。由於其他專案無法從我們的 internal
目錄匯入程式碼,因此我們可以自由地重構其 API,並在不中斷外部使用者的情況下移動項目。套件的專案結構如下
project-root-directory/
internal/
auth/
auth.go
auth_test.go
hash/
hash.go
hash_test.go
go.mod
modname.go
modname_test.go
modname.go
檔案宣告 package modname
,auth.go
宣告 package auth
,以此類推。modname.go
可以如下匯入 auth
套件
import "github.com/someuser/modname/internal/auth"
在 internal
目錄中包含支援套件的指令配置非常類似,不同之處在於根目錄中的檔案宣告 package main
。
多個套件
一個模組可以包含多個可匯入的套件;每個套件都有自己的目錄,並且可以階層化結構。以下是範例專案結構
project-root-directory/
go.mod
modname.go
modname_test.go
auth/
auth.go
auth_test.go
token/
token.go
token_test.go
hash/
hash.go
internal/
trace/
trace.go
提醒您,我們假設 go.mod
中的 module
行表示
module github.com/someuser/modname
modname
套件位於根目錄中,宣告 package modname
,使用者可以使用下列方式匯入
import "github.com/someuser/modname"
使用者可以如下匯入子套件
import "github.com/someuser/modname/auth"
import "github.com/someuser/modname/auth/token"
import "github.com/someuser/modname/hash"
位於 internal/trace
中的套件 trace
無法在此模組外匯入。建議盡可能將套件保留在 internal
中。
多個指令
同一個儲存庫中的多個程式通常會有不同的目錄
project-root-directory/
go.mod
internal/
... shared internal packages
prog1/
main.go
prog2/
main.go
在每個目錄中,程式的 Go 檔案宣告 package main
。頂層 internal
目錄可以包含儲存庫中所有指令使用的共用套件。
使用者可以按以下方式安裝這些程式
$ go install github.com/someuser/modname/prog1@latest
$ go install github.com/someuser/modname/prog2@latest
一個常見的慣例是將所有指令放在儲存庫中的 cmd
目錄中;雖然在僅包含指令的儲存庫中並非絕對必要,但在同時包含指令和可匯入套件的混合儲存庫中非常有用,我們將在接下來討論。
同一個儲存庫中的套件和指令
有時儲存庫會同時提供可匯入的套件和具有相關功能的可安裝指令。以下是一個此類儲存庫的範例專案結構
project-root-directory/
go.mod
modname.go
modname_test.go
auth/
auth.go
auth_test.go
internal/
... internal packages
cmd/
prog1/
main.go
prog2/
main.go
假設這個模組稱為 github.com/someuser/modname
,使用者現在可以從中匯入套件
import "github.com/someuser/modname"
import "github.com/someuser/modname/auth"
並從中安裝程式
$ go install github.com/someuser/modname/cmd/prog1@latest
$ go install github.com/someuser/modname/cmd/prog2@latest
伺服器專案
Go 是一種用於實作伺服器的常見語言。此類專案的結構差異很大,因為伺服器開發有許多面向:協定(REST?gRPC?)、部署、前端檔案、容器化、腳本等等。我們將在此專注於使用 Go 編寫的專案部分。
伺服器專案通常不會有可供匯出的套件,因為伺服器通常是一個獨立的二進位檔(或一組二進位檔)。因此,建議將實作伺服器邏輯的 Go 套件放在 internal
目錄中。此外,由於專案可能還有許多包含非 Go 檔案的其他目錄,因此最好將所有 Go 指令放在一起放在 cmd
目錄中
project-root-directory/
go.mod
internal/
auth/
...
metrics/
...
model/
...
cmd/
api-server/
main.go
metrics-analyzer/
main.go
...
... the project's other directories with non-Go code
如果伺服器儲存庫的套件日後變得對其他專案有用,最好將它們拆分到不同的模組中。