Недавно я реструктурировал свой ранний Go проект. Когда я менял структуру, вспомнил, что есть множество рекомендаций по структурированию, но нет единого стандарта для Golang проектов.

В других языка программирования, таких как Java, существует стандартная структура проекта, которой большинство программистов следует. В Python используемый фреймворк (Django или Flask) будет определять структуру проекта. Однако, Go находится на этапе, когда сообщество пока не выбрало стандартный подход.

Однако, есть множество рекомендаций. Но просматривая рекомендации, я понял, что они не полностью подходят для моего проекта.

В этой статье будет обсуждаться, как я в конечном итоге структурировал свой проект (который в итоге не сильно отличаются от рекомендаций) и стандартные практики.

Сделать код модульным с помощью хорошо разработанных пакетов

Прежде всего следует упомянуть, что любой код в приложении, который используется повторно, должен быть пакетом. О том, как структурировать пакеты и лучшие способы сделать это, достойно отдельно статьи. (У автора оригинальной статьи есть презентация по этой теме)

Я хотел бы сказать, что разбиение кода на пакеты содержит не только преимущества от переиспользования кода. С точки зрения структуры проекта это помогает сгруппировать код с общей целью вместе. Это помогает другим находить и менять код, что является важным атрибутом, особенно для проектов с открытым исходным кодом.

Пакеты также часто упрощают тестирование кода. Изолируя функциональность в пакете, мы можем протестировать эту функциональность с меньшим количеством зависимостей.

Когда я начинаю новый проект или реструктурирую существующий, обычно в начале записываю различные пакеты, которые мне необходимы. Иногда, я даже создаю базовую структуру пакетов, прежде чем писать код или заниматься рефакторингом.

Вынести точку входа из кода приложения

Другая хорошая практика, которую я видел в большинстве рекомендаций по структуре проекта - это разделение кода приложения и точки входа в программу. Под точкой входа я подрузамеваю main() функцию в пакете main.

В Go, как и в другиз языках, точкой входа является main() функция. Когда приложения запускается, это первая выполняемая функция и оченьзаманчиво поместить в эту функцию все основные функции приложения. Вместо того, чтобы поместить весь код в main пакет, лучшим решением будет создать app пакет.

Размещение основной логики приложения в собственном пакете дает несколько преимуществ. Мне больше всего нравится то, как это упрощает тестирование. Обычно для app пакета создаются функции Start() и Stop() или Shutdown(). При создании тестов наличие возможности запускать и останавливать основные функции приложения значительно упрощает написание тестов, которые выполняются в соответствии с основной логикой.

Ниже показан пример стуктуры пакета app.

package app

import (
	"fmt"
)

var ErrShutdown = fmt.Errorf("application was shutdown gracefully")

func Start(...) error {
	// Application runtime code goes here
}

func Shutdown() {
	// Shutdown contexts, listeners, and such
}

Вышеупомянутое является отличной рекомендацией при создании консольного приложения, которое является одновременно сервером и консольной утилитой (CLI). Вынося основной код в общий пакет, сервер и CLI смогут совместно исползовать пакет app.

Однако, данная рекомендация не отностится к очень простым CLI. Такие утилиты только запускаются, выполняют простые действия и завершают работу. Но даже для простых приложений мне нравится следовать рекомендации и создавать пакет app. Это упророщает групировку логики, что снижает барьеры для поддержания кодовой базы.


Решил не дописывать, потому что уже не актуально. Автор оригинального поста так же сделал обновленную версию1.


Оригинал:


wholly - полностью
write down - записываю
even - четный / даже
... as I go. - ... по ходу.
very tempting - очень заманчиво
Rather than - Вместо того / Скорее, чем
advantages - преимущества

Footnotes

  1. https://bencane.com/how-to-structure-a-golang-project-aad7095d70a