Goモジュールの構成
Goを初めて使用する開発者によくある質問に、「ファイルとフォルダのレイアウトという観点から、Goプロジェクトをどのように構成すれば良いのでしょうか?」というものがあります。このドキュメントの目的は、この質問に答えるのに役立つガイドラインを提供することです。このドキュメントを最大限に活用するには、チュートリアルとモジュールソースの管理を読んで、Goモジュールの基礎を理解しておいてください。
Goプロジェクトには、パッケージ、コマンドラインプログラム、またはその両方が含まれる場合があります。このガイドは、プロジェクトの種類別に構成されています。
基本パッケージ
基本的なGoパッケージは、プロジェクトのルートディレクトリにすべてのコードを配置します。プロジェクトは単一のモジュールで構成され、そのモジュールは単一のパッケージで構成されます。パッケージ名は、モジュール名の最後のパスコンポーネントと一致します。非常に単純なパッケージで、単一のGoファイルのみを必要とする場合、プロジェクト構造は次のようになります。
project-root-directory/
go.mod
modname.go
modname_test.go
[このドキュメント全体を通して、ファイル名/パッケージ名は完全に任意です]
このディレクトリがgithub.com/someuser/modname
のGitHubリポジトリにアップロードされていると仮定すると、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`が含まれていますが、これは単なる慣例です。メインファイルは`modname.go`(適切な`modname`の値の場合)またはその他の任意の名前にもできます。
このディレクトリがgithub.com/someuser/modname
のGitHubリポジトリにアップロードされていると仮定すると、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
サーバーリポジトリが、他のプロジェクトと共有するのに役立つパッケージを成長させる場合は、それらを個別のモジュールに分割することをお勧めします。