Структура backend сервиса на языке Go
Сервисы Ensi на Go строятся на Goravel и сохраняют те же организационные цели, что и PHP-сервисы:
- группировка кода по предметной области (модулю), а не по техническому слою на весь сервис;
- классы/типы меньше и сильнее следуют SRP.
Новые сервисы создаются из backend-service-skeleton-go. Примеры прикладного кода есть в CMS-Go, Catalog Cache Go и OMS Go.
Ниже описаны общие границы слоёв. Состав инфраструктуры определяется задачами сервиса: например, OMS использует Kafka и внешние cron-команды, а Catalog Cache также использует Elasticsearch, очереди и scheduler. Наличие файлов в шаблоне само по себе не означает, что подсистему нужно включать.
Пошаговая настройка окружения, создание сервиса и первый тест описаны в первом запуске.
Практические руководства
| Задача | Руководство |
|---|---|
| Соблюсти принятые правила Go-кода | Стиль и правила |
Реализовать SQL :search | Фильтры, includes и пагинация |
| Создать, изменить или удалить ресурс | Изменяющие операции |
| Вызвать API другого сервиса | HTTP-клиенты |
| Загрузить, заменить или удалить файл | Файловые операции |
Окружение и инструменты
Команды Go, Artisan и инструментов проекта выполняют из корня сервиса через elc; для тестов передают --env=.env.testing.
elc mise trust
elc mise run tools:install
elc go build -o build/app
elc go test ./... --env=.env.testing
Подробности версий, установки клиентов и локального workspace — в работе с пакетами.
Общий обзор
app/
adapters/ # инфраструктура: БД-модели, Kafka, очередь, логи, метрики
common/ # общие HTTP-хелперы, search, files, initial-event, metrics
enums/ # enum'ы, сгенерированные из OpenAPI
errors/ # типизированные ошибки приложения
facades/ # facades Goravel
modules/ # бизнес-модули (аналог Domain + Http Modules в PHP)
providers/ # регистрация провайдеров и HTTP-клиентов
bootstrap/ # сборка приложения, провайдеры, команды, jobs, kafka
config/ # конфигурация приложения и зависимостей
database/ # миграции и сидеры
resources/openapi/ # OAS3 спецификация API
routes/ # web, API; gRPC подключается только при необходимости
tests/ # общие инструменты компонентного/OpenAPI тестирования
app/modules
Бизнес-логика и HTTP-слой модуля живут рядом в app/modules/{module}. Название модуля обычно во множественном числе и соответствует основной сущности: offers, products, brands.
Исключения:
common— сквозная функциональность сервиса (миграции сущностей, failed jobs, служебные команды);- инфраструктурные модули вроде
elastic/elastic_offers, если работа с индексом — отдельная предметная область; examples— демо-код для Kafka/queue/commands, не продакшен-логика.
app/modules/{module}
Типичный набор пакетов внутри модуля:
| Пакет | Назначение |
|---|---|
handlers | HTTP-контроллеры (точка входа API) |
actions | бизнес-действия с методом Execute |
queries | контракт search/filter/sort/include для списков |
mappers | преобразование adapters/db моделей в API DTO |
dto | request/response структуры |
tests | компонентные тесты эндпоинтов модуля |
commands | artisan-команды, относящиеся к модулю |
jobs | фоновые задачи очереди |
kafka | обработчики сообщений Kafka |
factories | фабрики запросов для тестов |
handlers
Handler принимает HTTP-запрос, валидирует его, вызывает action и формирует ответ. Он не содержит бизнес-логики и не ходит в БД напрямую. Ответ и ошибки собирают хелперами из app/common/http — см. HTTP-ответы и ошибки.
func (c *OffersController) Search(ctx http.Context) http.Response {
var req commondto.SearchRequest
if failResponse := commonhttp.Validate(ctx, &req); failResponse != nil {
return failResponse
}
result, err := actions.NewSearchOffersAction().Execute(ctx.Context(), req.Params())
if err != nil {
return commonhttp.Error(ctx, "ServerError", err)
}
return commonhttp.Page(ctx, result.Data, result.Pagination)
}
actions
Action — аналог Action-классов в PHP: осмысленное самостоятельное действие с публичным методом Execute. Action не должен знать про HTTP/Kafka-транспорт: на вход получает context.Context и типизированные параметры/DTO, на выход — результат или ошибку.
func (a *SearchOffersAction) Execute(ctx context.Context, params search.Params) (*commondto.SearchResult[dto.Offer], error) {
query := facades.Orm().WithContext(ctx).Query().Model(&db.Offer{})
data, pagination, err := search.Execute(query, queries.Offers(), params, mappers.OfferFromModel)
if err != nil {
return nil, err
}
return &commondto.SearchResult[dto.Offer]{Data: data, Pagination: pagination}, nil
}
queries
Для search-эндпоинтов модуль описывает публичный SQL-контракт: доступные includes, sorts и filters. Общий движок лежит в app/common/search.
mappers
Mappers отделяют ORM-модели (app/adapters/db) от API DTO. Это позволяет менять структуру хранения, не ломая контракт ответа, и учитывать include при сборке ответа.
dto
DTO — структуры запроса/ответа API. Общие search-структуры лежат в app/common/dto.
app/adapters
Инфраструктурный слой, общий для всего сервиса:
adapters/db— ORM-модели и фабрики моделей (аналог Eloquent Models из PHP Domain). Модели не раскладываются по модулям: одна таблица — один файл вadapters/db. Подробнее: работа с базой данных;adapters/kafka— клиент, registry, supervisor consumers/producers. Как пользоваться: работа с Kafka;adapters/queue,adapters/schedule— обвязка воркеров очереди и планировщика. Про очереди: работа с очередями;adapters/logging,adapters/prometheus,adapters/observability— логирование и метрики. Про логи: логирование; про Prometheus: метрики;
Бизнес-модули зависят от adapters, но adapters не должны зависеть от конкретного HTTP-слоя модулей.
app/common
Общие переиспользуемые компоненты без привязки к конкретному домену:
http— валидация, единообразные ответы и ошибки, middleware;search— выполнение search-запросов поsearch.Definition;dto, вспомогательные функции коллекций,metrics;files— преобразования файловых метаданных;initialevent— контекст инициирующего события.
Типизированные ошибки приложения живут отдельно в app/errors; HTTP-хелперы преобразуют их в публичные ответы.
Служебные HTTP-ручки вроде healthcheck/OpenAPI UI лежат в app/common/handlers и подключаются из routes/web.go.
routes
Роуты живут в корневой директории routes/:
api.go— точка входа API, вызывает сгенерированную регистрацию;api_gen.go— сгенерированные API-роуты (DO NOT EDIT);web.go— служебные маршруты (health, OpenAPI UI);
Сгенерированные роуты создают контроллеры из app/modules/{module}/handlers и вешают их на /api/v1/....
resources/openapi
Описание API в OAS3 хранится в коде сервиса и является источником генерации серверного кода и клиентов.
resources/openapi
├── index.yaml
├── common_parameters.yaml
├── offers
│ ├── offers_paths.yaml
│ └── schemas
│ └── offers.yaml
└── products
├── products_paths.yaml
└── schemas
└── products.yaml
resources/openapi/index.yaml — главный файл спецификации. Параметры генерации — в .ensi-openapi-gen.yaml в корне сервиса.
Генерация серверных заготовок — CLI ensi-gog server (подробнее: генератор ensi-gog):
elc ensi-gog server
elc mise run check:generated
Ожидаемые артефакты:
| Артефакт | Куда |
|---|---|
| API-роуты | routes/api_gen.go |
| Runtime action routes | app/common/http/actionrouter/actionrouter.go (при наличии таких URI) |
| Handlers | app/modules/{module}/handlers/ |
| DTO / FormRequest | app/modules/{module}/dto/ |
| Enum'ы | app/enums/ |
| Фабрики запросов | app/modules/{module}/factories/ |
| Тестовые заготовки | app/modules/{module}/tests/ |
bootstrap и providers
bootstrap/— сборка приложения: список провайдеров, регистрация commands/jobs/kafka/schedule/seeders;app/providers/— провайдеры сервиса, в т.ч.providers/clientsдля HTTP-клиентов к другим сервисам Ensi;config/иconfig/ensi_clients/— конфигурация приложения и внешних клиентов.
database
Миграции и сидеры Goravel:
database/migrations— миграции с регистрацией вbootstrap/migrations.go;database/seeders— сидеры с регистрацией вbootstrap/seeders.go.
Тесты
Подробнее об организации файлов и типичных сценариях — рекомендации к написанию автотестов на Go.
Общие инструменты OpenAPI/component-тестов лежат в tests/ в корне сервиса. Компонентные тесты модулей — рядом с кодом: app/modules/{module}/tests/{resource}/.
Запуск:
elc go test ./... --env=.env.testing
Соответствие PHP-структуре
| PHP (Laravel) | Go (Goravel) |
|---|---|
app/Domain/{Domain}/Actions | app/modules/{module}/actions |
app/Domain/{Domain}/Models | app/adapters/db |
app/Http/ApiV1/Modules/{Module}/Controllers | app/modules/{module}/handlers |
.../Requests + .../Resources | app/modules/{module}/dto + mappers |
.../Queries | app/modules/{module}/queries |
public/api-docs/v1 | resources/openapi |
app/Http/ApiV1/routes.php (generated) | routes/api_gen.go |