Media Do Tech Do Blog

株式会社メディアドゥのエンジニアによるブログです。

Goの標準プロジェクトレイアウトを読み解く

f:id:hrk02:20190214001953p:plain
The Go gopher was designed by Renée French and has a CC BY 3.0 license.

メディアドゥの沓名と奥野です。

今回は、弊社内での複数の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を例に見てみましょう。

github.com

f:id:masashi_kutsuna:20190116133136p:plain

main関数を定義する、シンプルなコードのみが配置されていることがわかります。下記のようなコードは、/cmd以外の別ディレクトリに配置することを検討しましょう。

  • 他のプロジェクトでの使用も許可するコード (/pkgディレクトリへの配置を推奨)
  • 他プロジェクトからの利用を禁止するコード (/internalディレクトリへの配置を推奨)

一般的には、/pkgディレクトリや/internalディレクトリから、コードをインポートして使うような小さなメイン処理を記載します。

/internal

該当プロジェクト内でのみ利用するアプリケーションコードあるいはライブラリコードを配置します。

ここに配置されたコードは、外部プロジェクトからのインポートを禁止します。

/pkg

別プロジェクトでも利用可能なコードを配置します。

具体例として、モニタリングシステムのprometheusを例に見てみます。

github.com

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の各プロジェクトの階層を整理するための参考になれば幸いです。

参考リンク

https://github.com/golang-standards/project-layout