先日バズってた 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.gomodnameの適切な値に対して)とか、他の名前にすることもできる。

このディレクトリが 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.gomodname パッケージを宣言し、 auth.goauth パッケージを宣言する:

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以外のコードを含むプロジェクトの他のディレクトリ

サーバーのリポジトリに、他のプロジェクトと共有するのに便利なパッケージが増えてきたら、それらを別のモジュールに分割するのが一番です。


Refs