В посте описан базовый макет для проектов приложений Go. Это не официальный стандарт, определенный основной командой разработчиков Go; однако это набор общих исторических и новых шаблонов макета проектов в экосистеме Go. Некоторые из этих шаблонов более популярны, чем другие. Он также имеет ряд небольших улучшений и несколько вспомогательных каталогов, общих для любого достаточно большого реального приложения.
Если вы пытаетесь изучить Go или строите PoC-проект или игрушечный проект для себя, этот макет проекта будет излишним. Начните с чего-нибудь действительно простого (одного файла main.go более чем достаточно). По мере роста вашего проекта имейте в виду, что важно убедиться, что ваш код хорошо структурирован, иначе вы получите беспорядочный код с множеством скрытых зависимостей и глобальным состоянием. Когда над проектом будет работать больше людей, вам понадобится еще больше структуры. Вот когда важно представить общий способ управления пакетами/библиотеками. Когда у вас есть проект с открытым исходным кодом или когда вы знаете, что другие проекты импортируют код из репозитория вашего проекта, тогда важно иметь частные (private) (также известные как internal) пакеты и код. Клонируйте репозиторий, сохраните то, что вам нужно, а все остальное удалите! То, что оно есть, не означает, что вам нужно использовать все это. Ни один из этих шаблонов не используется в каждом отдельном проекте. Даже шаблон vendor не универсален.
С Go 1.14 модули Go наконец-то готовы к производственному использованию. Используйте модули Go, если у вас нет особой причины не использовать их, и если вы это сделаете, вам не нужно беспокоиться о $GOPATH и о том, где вы разместите свой проект. Базовый файл go.mod в репозитории предполагает, что ваш проект размещен на GitHub, но это не является обязательным требованием. Путь к модулю может быть любым.
Этот макет проекта намеренно общий и не пытается навязывать конкретную структуру пакета Go.
Каталоги Go
/cmd
Основные (main) приложения для этого проекта.
Имя каталога для каждого приложения должно соответствовать имени исполняемого файла, который вы хотите иметь (например, /cmd/myapp).
Не помещайте много кода в каталог приложения. Если вы думаете, что код можно импортировать и использовать в других проектах, он должен находиться в каталоге /pkg. Если код нельзя использовать повторно или вы не хотите, чтобы другие использовали его повторно, поместите этот код в каталог /internal. Вы будете удивлены тем, что сделают другие, поэтому четко выражайте свои намерения!
Обычно есть небольшая функция main, которая импортирует и вызывает код из каталогов /internal и /pkg и ничего больше.
Примеры:
- https://github.com/vmware-tanzu/velero/tree/master/cmd (просто очень маленькая функция
main
со всем остальным в пакетах) - https://github.com/moby/moby/tree/master/cmd
- https://github.com/prometheus/prometheus/tree/main/cmd
- https://github.com/influxdata/influxdb/tree/master/cmd
- https://github.com/kubernetes/kubernetes/tree/master/cmd
- https://github.com/dapr/dapr/tree/master/cmd
/internal
Частное приложение и код библиотеки. Это код, который вы не хотите, чтобы другие импортировали в свои приложения или библиотеки. Обратите внимание, что этот шаблон макета применяется самим компилятором Go. Вы не ограничены внутренним каталогом верхнего уровня. У вас может быть более одного внутреннего каталога на любом уровне дерева вашего проекта.
При желании вы можете добавить немного дополнительной структуры к своим внутренним пакетам, чтобы разделить общий и не общий внутренний код. Это не обязательно (особенно для небольших проектов), но хорошо иметь визуальные подсказки, показывающие предполагаемое использование пакета. Фактический код вашего приложения может находиться в каталоге /internal/app (например, /internal/app/myapp), а код, совместно используемый этими приложениями, - в каталоге /internal/pkg (например, /internal/pkg/myprivlib).
Примеры:
- https://github.com/hashicorp/terraform/tree/main/internal
- https://github.com/influxdata/influxdb/tree/master/internal
- https://github.com/perkeep/perkeep/tree/master/internal
- https://github.com/jaegertracing/jaeger/tree/master/internal
- https://github.com/moby/moby/tree/master/internal
- https://github.com/satellity/satellity/tree/master/internal
/pkg
Код библиотеки, который можно использовать внешними приложениями (например, /pkg/mypubliclib). Другие проекты будут импортировать эти библиотеки, ожидая, что они будут работать, поэтому подумайте дважды, прежде чем что-то здесь помещать) Обратите внимание, что internal каталог - лучший способ гарантировать, что ваши частные пакеты не будут импортированы, потому что это выполняется Go. Каталог /pkg по-прежнему является хорошим способом явным образом сообщить, что код в этом каталоге безопасен для использования другими.
Это также способ сгруппировать код Go в одном месте, когда ваш корневой каталог содержит множество компонентов и каталогов, не относящихся к Go, что упрощает запуск различных инструментов Go.
Примеры:
- https://github.com/prometheus/prometheus/tree/main/pkg
- https://github.com/jaegertracing/jaeger/tree/master/pkg
- https://github.com/istio/istio/tree/master/pkg
- https://github.com/GoogleContainerTools/kaniko/tree/master/pkg
- https://github.com/google/gvisor/tree/master/pkg
- https://github.com/google/syzkaller/tree/master/pkg
- https://github.com/minio/minio/tree/master/pkg
- https://github.com/vmware-tanzu/velero/tree/master/pkg
- https://github.com/argoproj/argo/tree/master/pkg
- https://github.com/vmware-tanzu/sonobuoy/tree/master/pkg
- https://github.com/helm/helm/tree/main/pkg
- https://github.com/kubernetes/kubernetes/tree/master/pkg
- https://github.com/kubernetes/kops/tree/master/pkg
- https://github.com/moby/moby/tree/master/pkg
- https://github.com/grafana/grafana/tree/master/pkg
- https://github.com/influxdata/influxdb/tree/master/pkg
- https://github.com/cockroachdb/cockroach/tree/master/pkg
- https://github.com/go-delve/delve/tree/master/pkg
- https://github.com/etcd-io/etcd/tree/master/pkg
- https://github.com/jesseduffield/lazygit/tree/master/pkg
- https://github.com/gopasspw/gopass/tree/master/pkg
- https://github.com/sosedoff/pgweb/tree/master/pkg
- https://github.com/GoogleContainerTools/skaffold/tree/master/pkg
- https://github.com/knative/serving/tree/main/pkg
- https://github.com/grafana/loki/tree/master/pkg
- https://github.com/bloomberg/goldpinger/tree/master/pkg
- https://github.com/jenkins-x/jx/tree/master/pkg
- https://github.com/DataDog/datadog-agent/tree/master/pkg
- https://github.com/dapr/dapr/tree/master/pkg
- https://github.com/cortexproject/cortex/tree/master/pkg
- https://github.com/dexidp/dex/tree/master/pkg
- https://github.com/pusher/oauth2_proxy/tree/master/pkg
- https://github.com/pdfcpu/pdfcpu/tree/master/pkg
- https://github.com/weaveworks/kured/tree/main/pkg
- https://github.com/weaveworks/footloose/tree/master/pkg
- https://github.com/weaveworks/ignite/tree/master/pkg
- https://github.com/tmrts/boilr/tree/master/pkg
- https://github.com/kata-containers/runtime/tree/master/pkg
- https://github.com/okteto/okteto/tree/master/pkg
Каталог /pkg, это распространенный шаблон макета, но он не является общепринятым, и некоторые в сообществе Go не рекомендуют его.
Это нормально его не использовать, если проект вашего приложения действительно мал и где дополнительный уровень вложенности не добавляет особой ценности (если вы действительно этого не хотите). Подумайте об этом, когда он становится достаточно большим и ваш корневой каталог становится очень загруженным (особенно если у вас много компонентов приложений, отличных от Go).
/vendor
Зависимости приложений (управляемые вручную или с помощью вашего любимого инструмента управления зависимостями, такого как новая встроенная функция модулей Go). Команда go mod vendor создаст для вас каталог /vendor. Обратите внимание, что вам может потребоваться добавить флаг -mod=vendor в вашу команду сборки go, если вы не используете Go 1.14, где он включен по умолчанию.
Не фиксируйте зависимости своего приложения, если вы создаете библиотеку.
Обратите внимание, что начиная с версии 1.13 Go также включает функцию прокси-сервера модуля (по умолчанию используется https://proxy.golang.org в качестве прокси-сервера модуля). Если это так, то каталог vendor вам вообще не понадобится.
Каталоги приложений-служб
/api
Спецификации OpenAPI/Swagger, файлы схемы JSON, файлы определения протокола.
Примеры:
- https://github.com/kubernetes/kubernetes/tree/master/api
- https://github.com/moby/moby/tree/master/api
Каталоги веб-приложений
/web
Компоненты, специфичные для веб-приложений: статические веб-ресурсы, серверные шаблоны и SPA.
Общие каталоги приложений
/configs
Шаблоны файлов конфигурации или конфигурации по умолчанию.
Поместите сюда файлы шаблона confd или consul-template.
/init
Конфигурации системного init (systemd, upstart, sysv) и диспетчера/супервизора процессов (runit, supervisord).
/scripts
Скрипты для выполнения различных операций сборки, установки, анализа и т. д.
Эти сценарии сохраняют Makefile корневого уровня маленьким и простым (например, https://github.com/hashicorp/terraform/blob/main/Makefile).
Примеры:
- https://github.com/helm/helm/tree/main/scripts
- https://github.com/cockroachdb/cockroach/tree/master/scripts
- https://github.com/hashicorp/terraform/tree/main/scripts
/build
Сборка и непрерывная интеграция.
Поместите конфигурации и сценарии вашего облака (AMI), контейнера (Docker), ОС (deb, rpm, pkg) в каталог /build/package.
Поместите свои конфигурации и сценарии CI (travis, circle, drone) в каталог /build/ci. Обратите внимание, что некоторые инструменты CI (например, Travis CI) очень разборчивы в расположении своих файлов конфигурации. Попробуйте поместить файлы конфигурации в каталог /build/ci, связав их с местом, где их ожидают инструменты CI (если возможно).
/deployments
Конфигурации и шаблоны развертывания IaaS, PaaS, системы оркестрации и контейнеров (docker-compose, kubernetes/helm, mesos, terraform, bosh). Обратите внимание, что в некоторых репозиториях (особенно в приложениях, развернутых с помощью kubernetes) этот каталог называется /deploy.
/test
Дополнительные внешние тестовые приложения и тестовые данные. Не стесняйтесь структурировать каталог /test как хотите. Для более крупных проектов имеет смысл создать подкаталог данных. Например, у вас может быть /test/data или /test/testdata, если вам нужно, чтобы Go игнорировал содержимое этого каталога. Обратите внимание, что Go также игнорирует каталоги или файлы, начинающиеся с "." или "_", чтобы у вас было больше гибкости в том, как вы называете каталог тестовых данных.
Пример:
- https://github.com/openshift/origin/tree/master/test (тестовые данные находятся в подкаталоге
/testdata
)
Другие каталоги
/docs
Дизайнерские и пользовательские документы (в дополнение к документации, созданной с помощью godoc).
Примеры:
- https://github.com/gohugoio/hugo/tree/master/docs
- https://github.com/openshift/origin/tree/master/docs
- https://github.com/dapr/dapr/tree/master/docs
/tools
Вспомогательные инструменты для этого проекта. Обратите внимание, что эти инструменты могут импортировать код из каталогов /pkg и /internal.
Примеры:
- https://github.com/istio/istio/tree/master/tools
- https://github.com/openshift/origin/tree/master/tools
- https://github.com/dapr/dapr/tree/master/tools
/examples
Примеры для ваших приложений и/или публичных библиотек.
Примеры:
- https://github.com/nats-io/nats.go/tree/master/examples
- https://github.com/docker-slim/docker-slim/tree/master/examples
- https://github.com/gohugoio/hugo/tree/master/examples
- https://github.com/hashicorp/packer/tree/master/examples
/third_party
Внешние вспомогательные инструменты, код fork'ов и другие сторонние утилиты (например, Swagger UI).
/githooks
Хуки Git.
/assets
Другие ресурсы для вашего репозитория (изображения, логотипы и т. д.).
/website
Это место для размещения данных веб-сайта вашего проекта, если вы не используете GitHub pages.
Пример:
Каталоги, которых лучше не иметь
/src
В некоторых проектах Go есть папка src, но обычно это происходит, когда разработчики приходят из мира Java, где это общий шаблон. Если вы можете помочь себе, постарайтесь не применять этот шаблон Java. Вы действительно не хотите, чтобы ваш код Go или проекты Go выглядели как Java.
Не путайте каталог /src уровня проекта с каталогом /src, который Go использует для своих рабочих пространств. Переменная среды $GOPATH указывает на вашу (текущую) рабочую область (по умолчанию она указывает на $HOME/ go в системах, отличных от Windows). Это рабочее пространство включает каталоги верхнего уровня /pkg, /bin и /src. Ваш фактический проект оказывается подкаталогом в /src, поэтому, если у вас есть каталог /src в вашем проекте, путь к проекту будет выглядеть так: /some/path/to/workspace/src/your_project/src/your_code.go. Обратите внимание, что в Go 1.11 можно иметь свой проект за пределами GOPATH, но это еще не означает, что использовать этот шаблон макета - хорошая идея.
Читайте также:
- Создание модуля в Golang
- Как писать Go код (с использованием модулей)
- Управление зависимостями в Golang