メディアドゥの沓名と奥野です。
今回は、弊社内での複数のGoプロジェクトの構成を整理する際に参考にした情報をまとめます。
背景
最近、弊社内にてGoで書かれたプロジェクトが増えてきました。
今後もGoプロジェクトは増加する予定のため、プロジェクト間で共有したいコードや、フォルダ構成の統一化を今のうちに行おうと思い立ちました。
ここでは、企業向けに定義された「GitHub - golang-standards/project-layout: Standard Go Project Layout」を参考に、Goプロジェクトのディレクトリ構成のルールについてまとめていきたいと思います。
前提
ここで取り扱う内容は、あくまでGoのプロジェクトのディレクトリ構成についてであり、パッケージ階層については触れません。
Clean Architecture、DDDなど採用している設計ルールにしたがって、各ディレクトリ配下にパッケージを配置することをお勧めします。
Go標準プロジェクトレイアウト
はじめに
以下に示すプロジェクトレイアウトは公式なものではありませんが、いくつものメジャーなGoプロジェクトで取り入れられているレイアウトを参考に標準ルールとしてまとめたものです。
ここで取り扱うプロジェクトレイアウト標準の構成は、ある程度の規模かつ長期的運用を前提としているプロジェクト向けにまとめています。
シンプルなGoアプリケーションを作成するため、あるいは学習のためのプロジェクトには向きません。
Goコード配置先ディレクトリ
/cmd
メイン処理を持ったコードの配置先となるディレクトリです。
各アプリケーションのディレクトリ名は、必要な実行可能ファイルの名前と一致している必要があります(例:/cmd/myapp)。
また、/cmdディレクトリには多くのコードを記載しないようにします。
Kubernetesのバックアップ・リストアツール、Heptio Arkを例に見てみましょう。
main関数を定義する、シンプルなコードのみが配置されていることがわかります。下記のようなコードは、/cmd以外の別ディレクトリに配置することを検討しましょう。
一般的には、/pkgディレクトリや/internalディレクトリから、コードをインポートして使うような小さなメイン処理を記載します。
/internal
該当プロジェクト内でのみ利用するアプリケーションコードあるいはライブラリコードを配置します。
ここに配置されたコードは、外部プロジェクトからのインポートを禁止します。
/pkg
別プロジェクトでも利用可能なコードを配置します。
具体例として、モニタリングシステムのprometheusを例に見てみます。
prometheusチーム配下には30以上のリポジトリがありますが、そのうちのalertmanagerプロジェクト内のコードで、prometheusプロジェクトのpkg配下にあるコードを利用している例が見つけられます。
package parse import ( "fmt" "regexp" "strings" "github.com/prometheus/prometheus/pkg/labels" ) ...
(https://github.com/prometheus/alertmanager/blob/master/pkg/parse/parse.go)
/vendor
(depなどのツールや手動で管理される)依存するアプリケーションコード、ライブラリコードの配置先です。
自身のプロジェクトをライブラリとして提供する場合、vendorディレクトリをコミットしない様に注意しましょう。
サービスアプリケーションコード配置先ディレクトリ
/api
REST API仕様を定義するOpenAPI/Swagger yaml, jsonファイルあるいはプロトコル定義ファイルを配置します。
参考: https://github.com/golang-standards/project-layout/blob/master/api/README.md
Webアプリケーションコード配置先ディレクトリ
/web
html, js, cssなどの静的コンテンツやページテンプレート、SPA用コンポーネントなどを配置します。
リソース配置先ディレクトリ
/configs
デフォルト設定ファイルや設定ファイルテンプレートを配置します。
/init
systemd, upstart, sysvなどのシステム初期設定あるいはrunit, supervisord設定などを配置します。
/scripts
ビルド、インストールなどの作業用スクリプト配置先ディレクトリです。
参考: https://github.com/golang-standards/project-layout/blob/master/scripts/README.md
/build
(deb, rpm, pkgあるいはdockerなどの)パッケージングやCI用ファイルを配置します。
/deployments
コンテナオーケストレーションレイヤーへのデプロイ(docker-compse, kubernetes/helm etc...)あるいは、IaaS, PaaSなどへのデプロイ設定ファイルを配置します。
/test
外部テスト用コードやテストデータやの配置先です。
参考: https://github.com/golang-standards/project-layout/blob/master/test/README.md
その他ディレクトリ
/docs
仕様やマニュアル配置先ディレクトリです。 godocツールで生成したドキュメントなどもこの配下に置きます。
参考: https://github.com/golang-standards/project-layout/blob/master/docs/README.md
/tools
該当プロジェクトのためのサポートツールを配置します。ここに配置されるコードは/pkgや/internalディレクトリに配置されているコードをインポートできます。
参考: https://github.com/golang-standards/project-layout/blob/master/tools/README.md
/examples
使用例を示す、参照コードを配置します。
参考: https://github.com/golang-standards/project-layout/blob/master/examples/README.md
/third_party
フォークしたコードやサードパーティライブラリなどの外部サポートツール配置先ディレクトリです。
/githooks
Git Hookファイルの配置先です。
/assets
画像やロゴなどのプロジェクトで利用するその他リソースの配置先です。
/website
Githubページを利用しない場合、プロジェクトのWebサイトファイルを配置する先として利用します。
参考: https://github.com/golang-standards/project-layout/blob/master/website/README.md
srcディレクトリを作成すべきではない
いくつかのGoプロジェクトでsrcディレクトリを持っているものを見かけますが、Goの場合これは適切ではありません。
おそらくはJavaで開発してたエンジニアの習性だと思われますが、これはGoの世界では混乱を招きます。
具体的には、Goの場合はGOPATH環境変数で指定されているワークスペース配下にトップレベルのsrc, binおよびpkgディレクトリを持ちます。
そして、自身で作成したプロジェクトはそのsrcディレクトリ配下で管理されるため、さらにその下にsrcディレクトリを作成することは冗長となってしまいます。
例:/some/path/to/workspace/src/your_project/src/your_code.go
まとめ
Goの標準プロジェクトレイアウトの内容をベースに用意すべきディレクトリとそれぞれの役割について触れました。
皆様の組織内でも煩雑になりがちな、Goの各プロジェクトの階層を整理するための参考になれば幸いです。