Skip to content

Latest commit

 

History

History
325 lines (219 loc) · 17.3 KB

README_RU.md

File metadata and controls

325 lines (219 loc) · 17.3 KB

Go Clean Template

Go Чистая Архитектура

Шаблон Чистой Архитектурой для приложений на Golang

Go Report Card License Release codecov

Обзор

Цель этого шаблона - показать принципы Чистой Архитектуры Роберта Мартина (дядюшки Боба):

  • как структурировать проект и не дать ему превратиться в спагетти-код
  • где хранить бизнес-логику, что бы она оставалась независимой, чистой и расширяемой
  • как не потерять контроль при росте проекта

Go-clean-template создан и поддерживается Evrone.

Содержание

Быстрый старт

Локальная разработка:

# Postgres, RabbitMQ
$ make compose-up
# Запуск приложения и миграций
$ make run

Интеграционные тесты (может быть использовано с CI):

# DB, app + migrations, integration tests
$ make compose-up-integration-test

Структура проекта

cmd/app/main.go

Инициализация конфигурации и логгера. Здесь вызывается основаная часть приложения из internal/app/app.go.

config

Конфигурация. Сначала читается config.yml, а затем переменные окружения. Переменные окружения имеют больший приоритет, чем конфигурация из yaml файла и при совпадении ключа, значение берется из переменных окружения. Структура конфигурации объявлена в файле config.go. Тег env-required: true указывает, что значение обязательно. (либо в yaml, либо в переменных среды).

Для конфигурации мы используем библиотеку cleanenv. Она простая и соответствует всем нашим требования.

Чтение конфигурации из yaml файла противоречит идеологии Twelve-Factor App, но на практике это более удобно, чем получение значений из ENV. Предполагается, что в yaml указываются значения по умолчанию, а переменные, чувствительные к безопасности задаются в переменных ENV.

docs

Документация Swagger. Генерируется автоматически с помощью библиотеки swag. Вам не нужно ничего редактировать вручную.

integration-test

Интеграционные тесты. Они запускаются в отдельном контейнере, рядом с контейнером приложения. Rest API удобно тестировать с помощью go-hit.

internal/app

Здесь находится только одна Run функция. Она размещена в файле app.go и является логическим продолжением функции main.

Здесь создаются все основные объекты. Внедрение Зависимостей происходит через конструктор "New ...". Эта позволяет слоировать приложение, делая бизнес-логику независимой от других слоев.

Далее, запускается сервер и ожидается сигнал в select для корректного завершения работы. Если app.go стал слишком большим, вы можете разделить его на несколько файлов.

Если зависимостей много, то для удобства можно использовать wire.

Файл migrate.go испольузется для автоматической миграции базы данных. Он включается в компиляцию только при указании тега migrate. Пример:

$ go run -tags migrate ./cmd/app

internal/controller

Слой хэндлеров сервера (MVC контроллеры). В шаблоне показана работа 2х серверов:

  • RPC (RabbitMQ as transport)
  • REST http (Gin framework)

Маршрутизаторы http сервера пишутся в едином стиле:

  • Хэндлеры сгруппируются по области применения (по общему критерию)
  • Для каждый группы создается свой маршрутизатор
  • Объект бизнес-логики передается в маршрутизатор, что бы быть доступным внутри хэндлеров

internal/controller/http/v1

Папка, как простой способ версионировать REST API. Для создания версии v2, нужно создать папку http/v2 с таким же содержимым. Добавить в файл internal/app/app.go строки:

handler := gin.New()
v1.NewRouter(handler, t)
v2.NewRouter(handler, t)

Вместо Gin можно использовать любой другой http фреймворк или стандартную net/http библиотеку.

В файле v1/router.go над хэндлером написаны комментарии для генерации документации через swagger swag.

internal/entity

Сущности бизнес-логики (модели). Могут быть использованы в любом слое. Также они могуть иметь методы, например, для валидации.

internal/usecase

Бизнес-логика.

  • Методы сгруппированы по области применения (по общему критерию)
  • У каждой группы своя отдельная структура
  • Один файл - одна структура

Репозитории, webapi, rpc и другие структуры передаются в слой бизнес-логики в связующем файле internal/app/app.go (смотрите Внедрение Зависимостей).

internal/usecase/repo

Репозиторий — это абстрактное хранилище (база данных), с которым взаимодействует бизнес-логика.

internal/usecase/webapi

Это абстрактное web API, с которым взаимодействует бизнес-логика. Например, это может быть внешний микросервис, к которому бизнес-логика обращается через REST API. Название пакета выбирается таким, что бы соответствовать его назначению.

pkg/rabbitmq

RabbitMQ RPC паттерн:

  • Внутри RabbitMQ не используется маршрутизация
  • Используется fanout-обмен, к которому привязана одна эксклюзивная очередь - это наиболее производительная конфигурация
  • Переподключение при потере соединения

Внедрение Зависимостей

Для устранения зависимости бизнес-логики от внешних пакетов используется внедрение зависимостей.

Например, через конструктор "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) и легко писать юнит-тесты.

Мы не привязаны к конкретным реализациям и всегда можем заменить один компонент на другой. Если новый компонент реализует интерфейс, то в бизнес-логике ничего не нужно менять.

Чистая Архитектура

Ключевая идея

Программисты создают оптимальную архитектуру приложения после написания основной части кода.

Хорошая архитектура позволяет отклыдывать изменение как можно дольше.

Основной принцип

Инверсия зависимостей (та же, что и в SOLID) используется как принцип для внедрения зависимостей. Зависимости направлены от внешнего слоя к внутреннему. Благодаря этому бизнес-логика и сущности остаются независимыми от других частей системы.

Например, приложение можно разделить на два слоя - внутренний и внешний:

  1. Бизнес-логика (например, стандартная библиотека Go).
  2. Инструменты (базы данных, сервера, брокеры сообщений и другие библиотеки и фреймворки).

Чистая архитектура

Внутренний слой с бизнес-логикой должен быть чистым. Он обязан:

  • Не импортировать пакеты с внешних слоев.
  • Использовать только стандартную библиотеку.
  • Взаимодействовать с внешними слоями через интерфейсы (!).

Бизнес-логика не должна ничего знать о Postgres или о реализации web API. Бизнес-логика имеет интерфейс для взаимодейсвтия с абстрактной базой данных или абстрактным web API.

Внешний слой имеет ограничения:

  • Компоненты этого слоя не могут знать друг о друге и взаимодействать напрямую. Обращение друг к другу происходит через внутренний слой - слой бизнес-логики.
  • Вызовы во внутренний слой выполняются через интерфейсы (!).
  • Данные передаются в формате, удобном для бизнес-логики (структуры хранятся в internal/entity).

Например, нужно обратиться к базе данных из HTTP хэндера (в слое контроллер). База данных и HTTP находятся во внешнем слое. Они не знаю друг о друге ничего и не могут взаимодействовать напрямую. Взаимодействие будет происходить через слой бизнес-логики usecase:

    HTTP > usecase
           usecase > repository (Postgres)
           usecase < repository (Postgres)
    HTTP < usecase

Символы > и < показывают пересечения слоев через интерфейсы и направления. Это же показано на схеме:

Пример

Пример более сложного пути данных:

    HTTP > usecase
           usecase > repository
           usecase < repository
           usecase > webapi
           usecase < webapi
           usecase > RPC
           usecase < RPC
           usecase > repository
           usecase < repository
    HTTP < usecase

Слои

Пример

Терминология в Чистой Архитектуре

  • Entities (сущности) - это структуры, с которыми работает бизнес логика. Они располагаются в папке internal/entity. В терминологии MVC сущности - это модели.
  • Use Cases это бизнес-логика. Располагается в папке internal/usecase.

Слой, с которым бизнес-логика взаимодействует напрямую, обычно, называется инфраструктурным слоем. Это может быть репозиторий internal/usecase/repo, внешнее webapi internal/usecase/webapi, любой пакет или микросервис. В шаблоне пакеты infrastructure размещены внутри internal/usecase.

Вы можете выбирать, как называть точки входа, по своему усмотрению. Варианты такие:

  • controller (в нашем случае)
  • delivery
  • transport
  • gateways
  • entrypoints
  • primary
  • input

Дополнительные слои

В классической версии Чистой Архитектуры для создания больших монолитных приложений предложено 4 слоя.

В исходной версии внешний слой делится на два, которые также имеют инверсию зависимостей в другие слои и взаимодействуют через интерфесы.

Внутренний слой также делится на два (с использованием интерфейсов) в случае сложной логики.


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

Другие подходы

Кроме Чистой Архитектуры есть и другие подоходы:

  • Луковая Архитектура
  • Гексогональная (Порты и адаптеры также на неё похожа) Они обе основаны на на принципе инверсии зависимостей. Порты и адаптеры очень похожи на Чистую Архитектуру. Различия в основном заключаются в терминологии.

Похожие проекты

Дополнительная информация