Как структурировать проект на Golang: гайд от backend-разработчика
Всем привет, меня зовут Авксентий, я backend-разработчик в inDriver. Думаю, каждый начинающий разработчик сталкивался с проблемой, как правильно выстроить архитектуру и структуру проекта. Ведь организация кода проекта — постоянно развивающаяся проблема, а следование стандартной структуре сохраняет чистоту кода и повышает производительность команды.
Когда я начинал писать на Go, то потратил много времени на поиски стандартов структурирования проекта. В итоге так и не нашел официального и точного стандарта — либо информация была неполной, либо это было не то, что нужно. Я решил написать свой гайд на основе опыта. Он для начинающих разработчиков и посвящен тому, как структурировать проект на Golang.
Почему я решил написать эту статью
Правильно выстроенные архитектура и структура проекта в начале способствует безболезненной разработке, масштабированию и легкому внедрению новых разработчиков в проект. Конечно, плоскую структуру тоже можно использовать для маленьких проектов с одним main-файлом, но это непрактично для больших.
Я видел немало проектов, и из каждого взял что-то для себя. Рекомендую ознакомиться с этим проектом — многие ориентируется на него при создании структуры. В моем примере будет более компактная структура — мы детально разберем директорию /internal.
Конечно, метод не претендует на лучший или единственный способ ведения дел. Но, думаю, он хорошо подойдет для начала. Посмотрим на структуру корня проекта:
Корневая структура приложения
Директории
/cmd
Точка входа для нашего приложения. Имя директории для каждого приложения должно совпадать с именем исполняемого файла, который вы хотите собрать. Не стоит располагать в этой директории много кода. Самой распространенной практикой является использование маленькой main-функции, которая импортирует и вызывает весь необходимый код из директорий /internal и /pkg.
Структура директории /cmd
/internal
Сердце нашего приложения — всю внутреннюю логику приложения храним здесь. /internal не импортируем в других приложениях и библиотеках. Код, который написан тут, предназначен исключительно для внутреннего использования в рамках кодовой базы. С версии Go 1.4 определен механизм, который не позволяет импортировать пакеты вне данного проекта, если они находятся внутри /internal.
В /internal мы храним бизнес-логику проекта и работу с базами данными. В общем, всю логику, связанную с этим приложением. Выстроить структуру внутри /internal можно по-разному, в зависимости от архитектуры. Но я не буду сильно углубляться в нее, а поверхностно покажу, как это выглядит. Приведу пример трехуровневой архитектуры, когда приложение делится на 3 слоя:
Транспортный.
Бизнес.
Базы данных.
Логика должна быть выстроена так, чтобы слои иерархически обращались друг к другу сверху вниз и наоборот. Не допускается, чтобы один слой перескакивал через промежуточный (например, транспортный напрямую в базу данных) и нижний не общался с верхним (например, база данных ходит в транспортный слой).
Модель трехуровневой архитектуры
Транспортный слой:
Сетевой уровень приложения, на котором конечный пользователь взаимодействует с приложением. После обработки запроса вся собранная информация идет на слой ниже.
Бизнес-слой:
Как следует из названия, содержит бизнес-логику, которая поддерживает основные функции приложения. Если в логике затрагиваются базы данных, мы идем на слой ниже.
Слой базы данных:
Отвечает за взаимодействие с постоянными хранилищами, такими как базы данных, и прочую обработку информации, которая не связана с бизнесом. Например, чтение и запись в базе данных.
Директории /internal:
/app
Точка, где все наши зависимости и логика собираются и запускают приложение. Run-метод, который вызывается из /cmd./config
Инициализация общих конфигураций приложения, которые мы прописывали в корне проекта./database (слой базы данных)
Файлы содержат методы для взаимодействия с базами данных./models (слой базы данных)
Структуры таблиц баз данных./services (бизнес-слой)
Вся бизнес-логика приложения./transport (транспортный слой)
Здесь храним http-настройки сервера, хендлеры, порты и так далее.
Структура директории /internal
/pkg
Если в /internal мы хранили код, который не могли импортировать в других приложениях, то в /pkg храним библиотеки, используемые в сторонних приложениях. Это нужно, чтобы потом импортировать их в другой проект, а не дублировать код из проекта в проект. В общем, кастомные или общие библиотеки мы храним здесь.
Вы можете не использовать эту директорию, если проект совсем небольшой и добавление нового уровня вложенности не имеет практического смысла.
/configs
Статические конфигурации нашего приложения, связанные с процессом сборки приложения. Обычно это yaml-файлы.
/api
Документация по вашему API. Спецификации OpenAPI или Swagger, файлы JSON Schema, файлы определения протоколов.
/build
Файлы конфигурации для билда проекта, Docker-контейнера и так далее.
/deployments
Содержит файлы, связанные с развертыванием: плейбуки Ansible, манифесты Docker Compose, манифесты и настройки Kuberntes, диаграммы Helm.
/docs
Документирование кода — важная часть в начале проекта. Поэтому всю документацию кода и дизайна (в дополнение к автоматической документации Godoc) храним здесь.
README.md
Трудно ожидать, что кто-то захочет погрузиться в ваш код, если ему не предоставили общего описания проекта. Поэтому файл README тоже необходим.
Распространенные директории
Хочу показать распространенные директории, которые я не включил в свой проект. Вы можете ознакомиться с ними, и в случае необходимости добавить себе.
/scripts
Скрипты для сборки, установки, анализа и прочих операций над проектом. Они позволяют оставить основной Makefile небольшим и простым.
/testdata
Дополнительные внешние приложения и данные для тестирования. Вы можете организовывать структуру директории /test так, как вам угодно. Для больших проектов имеет смысл создавать вложенную директорию с данными для тестов.
/tools
Инструменты поддержки проекта. Отмечу, что эти инструменты могут импортировать код из директорий /pkg и /internal.
/assets
Другие ресурсы, необходимые для работы: например, картинки и логотипы.
/web
Эта директория понадобится, если вы реализуете веб-приложение. Здесь находятся специальные компоненты для веб-приложений: статические веб-ресурсы, серверные шаблоны и одностраничные приложения.
/migrations
Здесь все миграции, связанные с базами данными: например, SQL-файлы.
Вывод
Конечно, не надо строго следовать моей структуре. Можно взять часть и отредактировать все под себя. Но когда я начинал, мне не хватало такого подробного гайда. Так что, надеюсь, статья вам помогла!
На всякий случай, оставлю ссылку на свой публичный sample-проект на GitHub. Если у вас есть вопросы, задавайте их в комментариях.