3 мин.

Как структурировать Go CLI проект

Как структурировать Go CLI проект

Недавно я реструктурировал свой ранний 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. Это упророщает групировку логики, что снижает барьеры для поддержания кодовой базы.

Что тогда делает пакет main?


Решил не дописывать, потому что уже не актуально.


Оригинал: How to Structure a Go Command-Line Project


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