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が含まれていますが、これは単なる慣例です。「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

サーバーリポジトリが他のプロジェクトと共有するのに役立つパッケージを増やした場合、それらを別のモジュールに分割するのが最善です。