Перейти к основному содержимому

Структура backend сервиса на языке Go

Сервисы Ensi на Go строятся на Goravel и сохраняют те же организационные цели, что и PHP-сервисы:

  1. группировка кода по предметной области (модулю), а не по техническому слою на весь сервис;
  2. классы/типы меньше и сильнее следуют SRP.

Референсная реализация — catalog-cache-go. Ниже описана структура, которой следует придерживаться в новых Go-сервисах.

Общий обзор

app/
adapters/ # инфраструктура: БД-модели, Kafka, очередь, логи, метрики
common/ # общие HTTP-хелперы, search, ошибки, metrics
enums/ # enum'ы, сгенерированные из OpenAPI
facades/ # facades Goravel
modules/ # бизнес-модули (аналог Domain + Http Modules в PHP)
providers/ # регистрация провайдеров и HTTP-клиентов
bootstrap/ # сборка приложения, провайдеры, команды, jobs, kafka
config/ # конфигурация приложения и зависимостей
database/ # миграции и сидеры
resources/openapi/ # OAS3 спецификация API
routes/ # web, API и gRPC routes
tests/ # общие инструменты компонентного/OpenAPI тестирования

app/modules

Бизнес-логика и HTTP-слой модуля живут рядом в app/modules/{module}. Название модуля обычно во множественном числе и соответствует основной сущности: offers, products, brands.

Исключения:

  • common — сквозная функциональность сервиса (миграции сущностей, failed jobs, служебные команды);
  • инфраструктурные модули вроде elastic / elastic_offers, если работа с индексом — отдельная предметная область;
  • examples — демо-код для Kafka/queue/commands, не продакшен-логика.

app/modules/{module}

Типичный набор пакетов внутри модуля:

ПакетНазначение
handlersHTTP-контроллеры (точка входа API)
actionsбизнес-действия с методом Execute
queriesконтракт search/filter/sort/include для списков
mappersпреобразование adapters/db моделей в API DTO
dtorequest/response структуры
testsкомпонентные тесты эндпоинтов модуля
commandsartisan-команды, относящиеся к модулю
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)
// ...
}

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-запросов по queries.Definition;
  • dto, collections, metrics, ошибки приложения.

Служебные 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):

ensi-gog server

Ожидаемые артефакты:

АртефактКуда
API-роутыroutes/api_gen.go
Handlersapp/modules/{module}/handlers/
DTO / FormRequestapp/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
  • database/seeders

Тесты

Подробнее об организации файлов и типичных сценариях — рекомендации к написанию автотестов на Go.

Общие инструменты OpenAPI/component-тестов лежат в tests/ в корне сервиса. Компонентные тесты модулей — рядом с кодом: app/modules/{module}/tests/{resource}/.

Запуск:

elc go test ./... --env=.env.testing

Соответствие PHP-структуре

PHP (Laravel)Go (Goravel)
app/Domain/{Domain}/Actionsapp/modules/{module}/actions
app/Domain/{Domain}/Modelsapp/adapters/db
app/Http/ApiV1/Modules/{Module}/Controllersapp/modules/{module}/handlers
.../Requests + .../Resourcesapp/modules/{module}/dto + mappers
.../Queriesapp/modules/{module}/queries
public/api-docs/v1resources/openapi
app/Http/ApiV1/routes.php (generated)routes/api_gen.go