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.gopackage 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.modmodule行には次のように記述されていると仮定します。

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

サーバーリポジトリが、他のプロジェクトと共有するのに役立つパッケージを成長させる場合は、それらを個別のモジュールに分割することをお勧めします。