TL;DR
- Go のモジュールを使い、複数のパッケージ、internal パッケージ、インストールできるコマンドを含む最小限のプロジェクト構成を示す。
pkg/ディレクトリはたいていアンチパターンで、まずはシンプルに始めるのがよい。
内容
目的
Go のコードをどう構成するかは、初心者がよく抱く疑問である。この記事は、Web で見かける古くて複雑すぎる助言ではなく、最小限で最新の例を示す。扱うのは、複数のインポートできるパッケージ、internal パッケージ、インストールできるコマンドの 3 つである。
モジュール名
モジュール名は go.mod で決めるプロジェクトのパスで、GitHub のパスを使うのが一般的である。モジュール名は利用者のコードでのインポートパスの基になる。
プロジェクトの構成
├── LICENSE
├── README.md
├── config.go
├── go.mod
├── go.sum
├── clientlib
│ ├── lib.go
│ └── lib_test.go
├── cmd
│ ├── modlib-client
│ │ └── main.go
│ └── modlib-server
│ └── main.go
├── internal
│ └── auth
│ ├── auth.go
│ └── auth_test.go
└── serverlib
└── lib.go
go.mod はモジュールの定義ファイルである。最上位の config.go はモジュールのルートパッケージで、最上位にあるのでパッケージ名はモジュール名とみなされる。
clientlib や serverlib のようなサブディレクトリは、それぞれ別のインポートできるパッケージになる。ファイル名は何でもよく、複数のファイルからなるパッケージも多い。入れ子にしてもよく、インポートパスはディレクトリのパスに従う。
コマンド
cmd ディレクトリにはコマンドラインのプログラムを置き、それぞれ package main にする。利用者は go install で入れられる。
$ go install github.com/eliben/modlib/cmd/modlib-client@latest
パッケージどうしは、モジュールのフルパスを使った絶対パスでインポートする。
internal パッケージ
internal ディレクトリのパッケージは、同じモジュールの中からしかインポートできない。Go のツールが特別なパスとして扱うので、外のコードはインポートできない。後から API を公開するのは、公開した API を取り下げるよりずっと簡単なので、作者はパッケージをまず internal に置くことを勧めている。
pkg/ ディレクトリ
作者は pkg/ ディレクトリを、たいていの場合アンチパターンだとしている。ほとんどのプロジェクトには不要で、そこに置かれるコードはたいてい internal/ に置くべきものである。
記事は「何よりも、シンプルに始めよう」という原則で締めくくられている。なお、2023 年 9 月の追記によると、同じような指針が Go の公式ドキュメントにも載るようになった。
キーワード
- Go
- go.mod
- internal パッケージ
- Go モジュール