Задать вопрос

Как правильно структурировать проект в Go?

Я пишу свой фреймворк для Go (для своих нужд в первую очередь, но собираюсь выложить его в Гит с открытым кодом). За 2 года, что я программирую, я успел поработать с PHP, Nodejs и Go. Когда я писал большой проект на Nodejs, я использовал nest, так как иначе реализовать такой проект очень сложно (он будет не поддерживаемым). Сейчас я использую Go и меня смущает, что нет (или я плохо искал) единого фреймворка, который бы говорил, как писать код.
До того как я расскажу о проблеме, я хочу рассмотреть два подхода к организации кода. Давайте представим, что у нас есть задача реализовать серверную часть для приложения школы. У нас есть 2 сущности — уроки и студенты.
В nest принято писать как-то так (опустим глобальные папки, index файлы и так далее, я хочу сделать акцент именно на сущностях)
структура на nest

students
-dto
--create-student.dto.ts
-student.controller.ts
-student.module.ts
-student.service.ts
-student.model.ts

lesson
-dto
--create-lesson.dto.ts
-lesson.controller.ts
-lesson.module.ts
-lesson.service.ts
-lesson.model.ts

app.module.ts


Решение на Go принято писать как-то так:
структура на Go

entities
-student.go
-lesson.go
handler
-student.go
-lesson.go
-router.go
service
-student.go
-lesson.go
-service.go
repository
-student.go
-lesson.go
-repository.go


Для тех, кто не знаком с Go в файлах repository.go и service.go лежат интерфейсы для репозиториев и сервисов соответственно. В файле router.go описываются все endpoint, подключаются middlewares.
Теперь мы видим 2 структуры приложения. В nest акцент идёт на сущности, в то время как в Go акцент идёт на handlers, services и repositories. Так же в Go принято описывать интерфейсы для последних. В то время как в nest из модуля student мы можем взаимодействовать только с тем, что положили в student, в Go мы из handler для student можем вызвать функцию из service для lesson. Вот в этом и заключается проблема. В go мы складываем все handlers в один пакет и внутри handler мы имеем пакет service, в котором лежат все service. Этот подход после nest мне показался странным. Если вы внимательно прочитаете структуры, то обнаружите, что в nest работа с базой происходит в service.
Я не спорю, что могу писать кривой код, но у меня service student.go обычно содержал функции в которых 2 строки: 1 - валидация, что пришли правильные данные, вторая - вызов соответствующей функции из repository.
Подводя итоги, я хочу сказать, что структура проекта на Go имеет странные особенности - вызов "лишних" функций и странный уровень абстракции - service. Мне нужно выбрать, под какую структуру строить фреймворк. Так как пишу фреймворк я на Go, звучит логичным выбрать структуру Go проекта, с другой стороны для полноценного проекта (не микросервиса) она имеет весьма большие минусы (акцент на первом). Буду рад, если поправите меня в комментариях. Какую бы структуру (может свою) вы бы мне посоветовали?
  • Вопрос задан
  • 2168 просмотров
Подписаться 7 Простой 3 комментария
Пригласить эксперта
Ответы на вопрос 3
uvelichitel
@uvelichitel Куратор тега Go
habrahabr.ru/users/uvelichitel
Вам в комментариях уже подсказали https://github.com/golang-standards/project-layout... Но этот документ не имеет официальной поддержки или методической силы. Это только волонтёрская попытка унификации.
Подход команды разработчиков языка виден в сырцах компилятора и стандартной библиотеки.
Рекомендованный и однозначно принятый сообществом layout не утверждён.
Особые свойства есть у папок internal и vendor. Эти папки при сборке рассматриваются тулчейном go иначе чем другие. Особые свойства этих папок отражены в официальной документации.
Ответ написан
Комментировать
yellow79
@yellow79
Senior Software Engineer
вызов "лишних" функций и странный уровень абстракции - service

Странно, но я почему-то решил, что это было ваше решение, писать именно так. Если решение вам кажется странным, то вам не кажется.

Как бы сделал я, если бы мне нужно было реализовать пример с уроками и студентами. 3 основных пакета, main, student, lesson. Так же, наверняка появился бы какой-нибудь пакет specs, в котором бы лежали структуры данных, которые API-шка принимает на вход и возвращает на выходе, у принимаемых структур так же были описаны методы для валидации.

Пакеты student и lesson это скорее всего были приватные пакеты для данного проекта, то есть они бы лежали в internal. Суть каждого пакета сводится к тому, что в нём описаны публичные функции для получения/создания данных в/из базы. Каждая функция принимает на вход указатель на коннект к базе. В эти же пакеты можно положить структуры, которые будут приниматься/возвращаться функциями пакета, так называемые DTO. Функции пакетов удобно тестировать, просто передав на вход замоканую базу.

В пакете main 2 структуры studentGroup и lessonGroup в которых был бы указатель на коннект к базе, а так же все http методы для работы с данной сущностью. Каждый метод вычитывает данные из запроса, валидирует их, вызывает нужные функции из пакетов, описанных выше и после пишет результат. Причём никто не мешает использовать функции обоих пакетов при необходимости. Так же в пакете main идёт создание подключения к базе, создание http.Handler, в котором идёт создание studentGroup, lessonGroup а так же привязка их методов к нужным http методам и url-ам. Функции структур удобно тестировать, просто передав при создании замоканую базу, или, если угодно, то можно передать настоящую базу в рамках более серьёзного тестирования.

Всё банально просто и прямолинейно, без лишних абстракций и интерфейсов, как завещал дядюшка Дэйв =)
Ответ написан
@calculator212
смущает, что нет (или я плохо искал) единого фреймворка, который бы говорил, как писать код.
Берите исходники gorilla/mux или gin(любые другие) и можете их использовать как пример.

Решение на Go принято писать как-то так:
Вообще не далеко не всегда так пишут

Go мы из handler для student можем вызвать функцию из service для lesson. Вот в этом и заключается проблема. В go мы складываем все handlers в один пакет и внутри handler мы имеем пакет service, в котором лежат все service. Этот
Так можно делать но это необязательно, можно делать хэндлеры в разных пакетах.
странный уровень абстракции - service.
Поэтому его стоит использовать только тогда, когда это нужно а не просто ради лишнего слоя абстракции.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы