Недавно я реструктурировал свой ранний 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.
Оригинал:
- https://bencane.com/2020/12/29/how-to-structure-a-golang-cli-project/
- https://medium.com/swlh/how-to-structure-a-go-command-line-project-788c318a1d8c
wholly - полностью
write down - записываю
even - четный / даже
... as I go. - ... по ходу.
very tempting - очень заманчиво
Rather than - Вместо того / Скорее, чем
advantages - преимущества