先日バズってた Go のプロジェクトディレクトリの正式見解というか本家の推奨記事.
気になってたので、読んでみた
以下翻訳結果
ファイルやフォルダのレイアウトに関して、Go を初めて使う開発者によくある質問は「Go プロジェクトをどのように整理すればいいのか」というものです。このドキュメントの目的は、この質問に答えるためのガイドラインを提供することです。このドキュメントを最大限に活用するために、チュートリアルを読み、モジュールソースを管理することで、Goモジュールの基本に精通していることを確認してください。
Go プロジェクトには、パッケージ、コマンドライン・プログラム、またはその 2 つの組み合わせがあります。このガイドはプロジェクトの種類別に構成されています。
基本パッケージ
基本的なGoパッケージは、すべてのコードがプロジェクトのルートディレクトリにあります。プロジェクトは1つのモジュールで構成され、1つのパッケージで構成されます。パッケージ名はモジュール名の最後のパスコンポーネントと一致します。単一のGoファイルを必要とする非常に単純なパッケージの場合、プロジェクトの構造は次のようになります:
project-root-directory/
go.mod
modname.go
modname_test.go
[この文書中、ファイル名/パッケージ名は完全に任意です]。
このディレクトリが github.com/someuser/modname
のGitHubリポジトリにアップロードされていると仮定すると、 go.mod
ファイルのモジュール行には、 module github.com/someuser/modname
と書かれているはずです。
modname.go
のコードでパッケージを宣言します:
package modname
// ... パッケージのコードはここ
ユーザーは、Goのコードでこのパッケージをインポートすることで、このパッケージに依存することができます:
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
ディレクトリ内のファイルはすべてmodnameパッケージを宣言している。
基本コマンド
基本的な実行可能プログラム(またはコマンドラインツール)は、その複雑さとコードサイズに応じて構成されます。最も単純なプログラムは、 func main
が定義された1つのGoファイルで構成されます。より大きなプログラムでは、コードが複数のファイルに分割され、すべて main
パッケージを宣言します:
project-root-directory/
go.mod
auth.go
auth_test.go
client.go
main.go
ここで main.go
ファイルには func main
が含まれているが、これは単なる慣例である。main
ファイルは、modname.go
(modname
の適切な値に対して)とか、他の名前にすることもできる。
このディレクトリが github.com/someuser/modname
のGitHubリポジトリにアップロードされていると仮定すると、 go.mod
ファイルのモジュール行にはこう書かれているはずだ:
module github.com/someuser/modname
そして、ユーザーはこれを自分のマシンにインストールできるはずだ:
go install github.com/someuser/modname@latest
サポートパッケージがあるパッケージやコマンド
大規模なパッケージやコマンドは、いくつかの機能をサポートパッケージに分割することで恩恵を受けるかもしれません。最初に、このようなパッケージは、 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
は modname
パッケージを宣言し、 auth.go
は auth
パッケージを宣言する:
import "github.com/someuser/modname/internal/auth"
内部ディレクトリにサポート・パッケージを持つコマンドのレイアウトは、ルート・ディレクトリのファイルが main
パッケージを宣言することを除けば、非常によく似ている。
複数のパッケージ
1つのモジュールは複数のインポート可能なパッケージで構成することができます。各パッケージは独自のディレクトリを持ち、階層構造を持つことができます。各パッケージは独自のディレクトリを持ち、階層構造を持つことができます:
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 github.com/someuser/modname
modname
パッケージはルート・ディレクトリに存在し、 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/
... 共有内部パッケージ
prog1/
main.go
prog2
main.go
各ディレクトリでは、プログラムの Go ファイルがパッケージの main
を宣言します。トップレベルの内部ディレクトリには、リポジトリ内のすべてのコマンドで使用される共有パッケージを含めることができます。
ユーザーはこれらのプログラムを以下のようにインストールできます:
$ 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/
... 内部パッケージ
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パッケージは内部ディレクトリに置くことをお勧めします。さらに、プロジェクトには Go 以外のファイルを含むディレクトリがたくさんあるはずなので、すべての Go コマンドを cmd
ディレクトリにまとめておくとよいでしょう:
project-root-directory/
go.mod
internal/
auth/
...
metrics/
...
model/
...
cmd/
api-server/
main.go
metrics-analyzer/
main.go
...
...Go以外のコードを含むプロジェクトの他のディレクトリ
サーバーのリポジトリに、他のプロジェクトと共有するのに便利なパッケージが増えてきたら、それらを別のモジュールに分割するのが一番です。