Go-clean-template: шаблон чистой архитектуры для сервисов на Go
Go-clean-template — шаблон для проектов на Golang, основанный на принципах чистой архитектуры Роберта («дядюшки Боба») Мартина. Вы можете клонировать шаблон и использовать его в качестве отправной точки для создания приложения на языке Go.
Чистая архитектура
Чистая архитектура — это способ организации кода сложных систем (в том числе потенциально сложных), который, как и большинство хороших сводов архитектурных принципов, способствует строгому разделению ответственности. Изначально предполагался для использования в монолитах, но так же подходит и к микросервисам.
Программное обеспечение разбивается на независимые функциональные компоненты, которые взаимодействуют друг с другом только определённым надёжным способом, при этом между ними передаётся только то состояние и те ресурсы, которыми необходимо обмениваться для выполнения поставленной задачи. Строгое разделение ответственности помогает минимизировать сложность каждого компонента, снижает вероятность появления ошибок, а если они возникли, облегчает их устранение, так как компонент-нарушитель можно легко идентифицировать. Разделение ответственности — ключ к соблюдению принципа «минимальных привилегий».
Чистая архитектура разделяет ответственность с помощью конвенций (правил), которые ясно дают понять, где в архитектуре сущность, а где компонент или часть компонента, и в какой степени каждый из них виден и доступен для других частей системы.
Если вы придерживаетесь принципов чистой архитектуры, то, например, высокоуровневые корпоративные бизнес-правила системы не будут иметь прямого доступа к структурам данных, передаваемых между API-сервером и мобильным приложением, и даже не будут знать о существовании таких деталей. Однако, если необходимо, их содержимое может передаваться через слои от низкоуровневых компонентов к вышестоящим чётко определёнными способами.
Как любая архитектурная основа, чистая архитектура сама по себе не убережёт от написания плохого кода, если вы не будете сознательно следовать её принципам на протяжении всего жизненного цикла приложения.Тем не менее, надеемся, что этот шаблон поможет вам правильно начать.
Краткое руководство
internal
и начать писать код. Предоставляемый Makefile
содержит ряд целей для сборки и запуска вашего приложения. Наиболее полезны следующие:Запуск с помощью Docker (запускает Postgres и RabbitMQ):
$ make compose-up
Запуск локально с миграциями:
$ make run
Запуск интеграционных тестов с помощью Docker:
$ make compose-up-integration-test
Структура проекта
Полную информацию вы найдете в README проекта, но вот некоторые особо интересные области:
cmd/app/main.go
Конфигурация и инициализация логгера, перед передачей управления в Run в файле internal/app/app.go
.
internal/app
Функция Run (которую вызывает Main) в app.go
выполняет инициализацию объектов и внедрение зависимостей (подробнее об этом ниже), запускает сервер и ждёт, когда наступит время для штатного завершения.
Файл migrate.go
используется для автоматической миграции базы данных. Он включается, если указан аргумент с тегом migrate. Например:
$ go run -tags migrate ./cmd/app
internal/controller
Уровень обработчиков серверов (контроллеры MVC). Шаблон включает в себя два сервера:
- RPC (RabbitMQ как транспорт)
- REST HTTP (фрейморк Gin)
Маршрутизаторы запросов написаны в том же стиле:
- обработчики группируются по области применения;
- для каждой группы создается своя структура маршрутизатора, методы которой обрабатывают пути;
- в структуру маршрутизатора вносится структура бизнес-логики, которая будет вызываться обработчиками.
internal/entity
Модели бизнес-логики, которые являются общими для системы и могут быть использованы в любом слое.
internal/usecase
Бизнес-логика:
- методы группируются по областям применения (на общей основе);
- каждая группа имеет свою собственную структуру;
- один файл — одна структура.
config/config.yml
Конфигурация приложения. Значения из файла конфигурации можно переопределить с помощью переменных окружения. Параметры конфигурации с тегом 'env-required: true'
в config.go
являются обязательными, и значение должно быть указано либо в config.yml
, либо в окружении, либо и там, и там.
docs/
Документация Swagger. Она автоматически генерируется библиотекой swag, поэтому нет смысла вносить в неё изменения, они всё равно не сохранятся.
integration-test/
Интеграционные тесты. Они запускаются как отдельный контейнер рядом с контейнером приложения. Rest API удобно тестировать с помощью go-hit.
Внедрение зависимости
Внедрение зависимостей ослабляет связь между компонентами и сводит к минимуму прямые зависимости.
Например, через конструктор New внедряем зависимость в структуру бизнес-логики. Это делает бизнес-логику независимой (и переносимой). Мы можем переопределить реализацию интерфейса, не внося изменений в пакет usecase.
package usecase
import (
// Nothing!
)
type Repository interface {
Get()
}
type UseCase struct {
repo Repository
}
func New(r Repository) *UseCase {
return &UseCase{
repo: r,
}
}
func (uc *UseCase) Do() {
uc.repo.Get()
}
Это также позволяет автоматически генерировать моки (например, с помощью mockery) и легко писать юнит-тесты.
Мы не привязаны к конкретным реализациям и всегда имеем возможность заменить один компонент на другой. Если новый компонент реализует интерфейс, то в бизнес-логике ничего менять не нужно.
Когда нужно управлять большим количеством внедрений, часто используют wire.
Альтернативные проекты
Конечно, наше решение не является единственным шаблоном чистой архитектуры для Go. Возможно, вам будет интересно изучить альтернативы и выбрать ту, которая вам больше подходит:
go-clean-arch
courses-backend
Мы работаем над проектами с открытым исходным кодом, потому что инициативны и понимаем, что нравится и нужно разработчикам. Если вам требуется обновить свой проект до последних версий технологического стека, свяжитесь с нами через форму ниже.