Архитектура сгенерированного проекта¶
Описание структуры и паттернов сгенерированных Go-проектов.
Общая архитектура¶
Сгенерированный проект следует принципам слоистой архитектуры с чётким разделением ответственности. Зависимости направлены только сверху вниз — транспортный слой обращается к сервису, сервис — к данным.
flowchart TB
NET_IN((Network)) -.-> TRANSPORT
subgraph TRANSPORT["Transport Layer"]
direction LR
API["<b>API</b><br/>Authorise · Helpers<br/>Handlers"]
CLI["<b>CLI</b><br/>Authorise<br/>Handlers"]
WORKERS["<b>Workers</b><br/>Jobs"]
BOTS["<b>Bots</b><br/>Authorise · Helpers<br/>Handlers"]
end
TRANSPORT --> SERVICE["<b>Service</b>"]
SERVICE --> DATA
subgraph DATA["Data Layer"]
direction LR
CLIENTS[Clients]
MODELS[Models]
DRIVERS[Drivers]
end
MODELS --> DB_CONN[DB connectors] -.-> DB[(DB)]
CLIENTS & DRIVERS -.-> NET_OUT((Network))
style TRANSPORT fill:#e3f2fd
style DATA fill:#fff3e0
style SERVICE fill:#e8f5e9,stroke:#4caf50
Config (OnlineConf)
Конфигурация доступна на всех слоях — Transport, Service, Data. Реализована как синглтон через OnlineConf.
Ключевые слои:
| Слой | Компоненты | Назначение |
|---|---|---|
| Transport | API, CLI, Workers, Bots | Точки входа с Authorise, Helpers, Handlers/Jobs |
| Service | Бизнес-логика | Центральный компонент, единственное место для бизнес-логики |
| Data | Clients, Models, Drivers | Доступ к данным и внешним сервисам |
| DB connectors | Коннекторы к БД | Подключение к базам данных |
| Config | OnlineConf | Конфигурация, доступна на всех слоях |
Внешние связи:
- Network (вверху) → входящие запросы к API и Bots
- Network (внизу) ← исходящие запросы от Clients и Drivers
- DB ← запросы к базе данных через DB connectors
Трёхслойный дизайн¶
Архитектура основана на трёх слоях с различными правилами зависимостей:
flowchart TB
subgraph LAYER1["<b>Слой 1: internal/app/</b><br/>Проект-специфичный код"]
L1_DESC["✓ Зависит от конфига<br/>✓ Использует конкретный логгер<br/>✓ Знает о транспортах"]
end
subgraph LAYER2["<b>Слой 2: internal/pkg/</b><br/>Переиспользуемый код"]
L2_DESC["✗ НЕ зависит от конфига<br/>✗ НЕ привязан к логгеру<br/>✓ Возвращает ошибки вверх"]
end
subgraph LAYER3["<b>Слой 3: pkg/</b><br/>Runtime библиотеки"]
L3_DESC["✗ НЕ зависит от конфига<br/>✗ Максимально переиспользуем<br/>✓ Может быть вынесен в отдельный модуль"]
end
LAYER1 -->|импортирует| LAYER2
LAYER2 -->|импортирует| LAYER3
style LAYER1 fill:#fff3e0,stroke:#ff9800
style LAYER2 fill:#e8f5e9,stroke:#4caf50
style LAYER3 fill:#f3e5f5,stroke:#9c27b0
Почему это важно?¶
| Проблема | Решение в архитектуре |
|---|---|
| Сложность тестирования | Нижние слои не зависят от конфига — легко мокать |
| Привязка к логгеру | internal/pkg возвращает ошибки, логгер — на верхнем уровне |
| Переиспользование | pkg/ можно вынести в отдельный go module |
| Циклические зависимости | Зависимости только сверху вниз |
Spec-First подход¶
Одна из ключевых особенностей сгенерированного приложения — подход spec-first (или contract-first):
flowchart LR
SPEC[OpenAPI/Proto<br/>Спецификация] --> GEN{Генератор}
GEN --> SERVER[Server Code<br/>Handlers]
GEN --> CLIENT[Client Code<br/>HTTP/gRPC клиенты]
style SPEC fill:#e3f2fd
style SERVER fill:#e8f5e9
style CLIENT fill:#fff3e0
Принцип: Спецификация создаётся до написания любого кода. По этой спецификации генерируется:
- Серверный код — handlers для вашего сервиса
- Клиентский код — типизированные клиенты для вызова других сервисов
Преимущества:
| Аспект | Выгода |
|---|---|
| Контракт | API описан до реализации — frontend и backend могут работать параллельно |
| Типизация | Сгенерированные клиенты полностью типизированы |
| Консистентность | Клиент и сервер всегда соответствуют одной спецификации |
Описание слоёв¶
Слой pkg/ (Runtime)¶
flowchart LR
subgraph PKG["pkg/"]
APP["app/<br/>Lifecycle"]
DRIVERS["drivers/<br/>DB, Redis, S3..."]
REST["rest/<br/>HTTP клиенты"]
MIDDLEWARE["middleware/<br/>HTTP middleware"]
end
style PKG fill:#f3e5f5,stroke:#9c27b0
Назначение: Runtime-библиотеки, максимально переиспользуемые между проектами.
Требования:
- ✗ Нет зависимостей от конфига проекта
- ✗ Нет зависимостей от конкретных реализаций логгера
- ✓ Может быть вынесен в отдельный go module
Содержимое:
| Директория | Назначение | Примеры |
|---|---|---|
pkg/app/ |
Жизненный цикл приложения | Runnable интерфейс, graceful shutdown |
pkg/drivers/ |
Драйверы внешних сервисов | PostgreSQL, Redis, S3, Telegram |
pkg/rest/ |
Сгенерированные REST клиенты | Ogen-клиенты для внешних API |
pkg/middleware/ |
HTTP middleware | Metrics, tracing, auth |
Слой internal/pkg/ (Generated Core)¶
flowchart LR
subgraph IPKG["internal/pkg/"]
MODEL["model/<br/>Модели данных"]
SERVICE["service/<br/>Интерфейсы"]
REPO["repository/<br/>Доступ к данным"]
CONSTANT["constant/<br/>Константы"]
end
style IPKG fill:#e8f5e9,stroke:#4caf50
Назначение: Генерируемый код, переиспользуемый между приложениями одного сервиса.
Требования:
- ✗ Не зависит от проект-специфичных настроек
- ✗ Не привязан к конкретному логгеру
- ✓ Функции возвращают ошибки вверх, логирование — на верхнем уровне
Содержимое:
| Директория | Назначение | Пример кода |
|---|---|---|
model/ |
Модели данных | type User struct {...} |
service/ |
Интерфейсы сервисов | type UserService interface {...} |
repository/ |
Репозитории | func (r *Repo) GetUser(ctx, id) |
constant/ |
Константы | const ServiceName = "my-api" |
Слой internal/app/ (Project-Specific)¶
flowchart LR
subgraph IAPP["internal/app/{app_name}/"]
TRANSPORT["transport/<br/>REST, gRPC, CLI"]
WORKER["worker/<br/>Telegram, Daemon"]
SVC["service/<br/>Бизнес-логика"]
end
style IAPP fill:#fff3e0,stroke:#ff9800
Назначение: Код, специфичный для конкретного приложения.
Характеристики:
- ✓ Может зависеть от конфига
- ✓ Может использовать конкретные реализации (логгер, драйверы)
- ✓ Содержит бизнес-логику обработчиков
Содержимое:
| Директория | Назначение | Пример |
|---|---|---|
transport/rest/ |
REST обработчики | handler.go с методами API |
transport/grpc/ |
gRPC реализации | Реализация proto-сервисов |
worker/ |
Фоновые воркеры | Telegram bot, Kafka consumer |
service/ |
Бизнес-логика | Специфичная для приложения логика |
Service — ядро бизнес-логики¶
Service — это центральный компонент, который содержит всю бизнес-логику приложения. Независимо от количества транспортов, воркеров или ботов — все они обращаются к одному и тому же сервису.
flowchart TB
subgraph TRANSPORTS["Транспортный слой"]
REST[REST API]
GRPC[gRPC]
CLI[CLI]
BOT[Telegram Bot]
WORKER[Worker]
end
subgraph CORE["Ядро"]
SERVICE[Service<br/>Бизнес-логика]
end
subgraph DATA["Доступ к данным"]
MODEL[Model/ORM]
CLIENT[REST/gRPC клиенты]
DRIVER[Drivers]
end
REST --> SERVICE
GRPC --> SERVICE
CLI --> SERVICE
BOT --> SERVICE
WORKER --> SERVICE
SERVICE --> MODEL
SERVICE --> CLIENT
SERVICE --> DRIVER
style CORE fill:#e8f5e9,stroke:#4caf50
Ключевые принципы:
- Единственное место для бизнес-логики — вся переиспользуемая логика лежит в сервисе
- Изолированность — сервис не знает о транспортах, он просто выполняет бизнес-операции
- Подготовка к масштабированию — при распиливании монорепо на микросервисы, именно сервис будет рефакториться
Что доступно сервису:
| Компонент | Назначение |
|---|---|
| Model | ORM для работы с базами данных (по умолчанию go-active-record) |
| Client | Сгенерированные клиенты к другим сервисам (REST, gRPC) |
| Driver | Кастомные коннекторы к внешним системам |
| Config | Конфигурация через OnlineConf (синглтон) |
Важно: Уделяйте особое внимание структуре пакетов внутри сервиса. Каждый компонент должен быть изолирован, чтобы в будущем его было проще выделить в отдельный микросервис.
Типы транспортов¶
Транспортный слой делится на 4 типа по способу получения событий:
flowchart TB
subgraph INCOMING["Входящие запросы"]
REST[REST API<br/>Принимает HTTP запросы]
GRPC[gRPC<br/>Принимает RPC вызовы]
end
subgraph POLLING["Опрос/подписка"]
BOT[Bots<br/>Подключается к серверам,<br/>читает события]
WORKER[Workers<br/>Берёт задачи из очереди,<br/>выполняет в цикле]
end
subgraph UTILITY["Утилиты"]
CLI[CLI<br/>Консольные команды<br/>для администрирования]
end
style INCOMING fill:#e3f2fd
style POLLING fill:#fff3e0
style UTILITY fill:#e8f5e9
| Тип | Описание | Примеры |
|---|---|---|
| API (REST/gRPC) | Принимают внешние запросы, обрабатывают, отвечают | HTTP эндпоинты, gRPC сервисы |
| Workers | Демоны с бесконечным циклом, выполняют джобы пачками | Обработка очередей, cron-задачи |
| Bots | Подключаются к внешним серверам, ждут события | Telegram, Slack боты |
| CLI | Консольные утилиты для разовых операций | Миграции, админские команды |
Workers vs Bots:
- Workers — сами берут задачи (pull-модель)
- Bots — получают события от внешних систем (push-модель)
Концепция Application¶
Application — атомарная единица горизонтального масштабирования (контейнер).
flowchart TB
subgraph SERVICE["Один сервис (codebase)"]
subgraph APP1["Application: gateway"]
REST1[REST API v1]
GRPC1[gRPC Users]
end
subgraph APP2["Application: workers"]
TG[Telegram Bot]
KAFKA[Kafka Consumer]
end
subgraph APP3["Application: admin"]
REST2[REST Admin API]
end
end
subgraph DEPLOY["Kubernetes/Docker"]
POD1[Pod: gateway<br/>replicas: 3]
POD2[Pod: workers<br/>replicas: 1]
POD3[Pod: admin<br/>replicas: 1]
end
APP1 -.-> POD1
APP2 -.-> POD2
APP3 -.-> POD3
style SERVICE fill:#e3f2fd
style DEPLOY fill:#e8f5e9
Характеристики¶
| Свойство | Описание |
|---|---|
| Атомарность | Один бинарь = один контейнер = один pod |
| Компоненты | HTTP серверы, gRPC серверы, воркеры инициализируются параллельно |
| Масштабирование | Каждое application масштабируется независимо |
| Конфигурация | Каждое application может иметь свои настройки |
applications:
# API Gateway с REST и gRPC (высоконагруженный)
- name: gateway
transport:
- name: rest_api
- name: grpc_users
# Выделенный worker instance (один экземпляр)
- name: workers
worker: [telegram_bot]
kafka: [order_consumer]
# Всё-в-одном для небольших деплоев
- name: monolith
transport:
- name: rest_api
- name: grpc_users
worker: [telegram_bot]
Жизненный цикл Application¶
stateDiagram-v2
[*] --> Init: Запуск
state Init {
[*] --> LoadConfig: 1. Загрузка конфига
LoadConfig --> CreateLogger: 2. Создание логгера
CreateLogger --> InitDrivers: 3. Init драйверов
InitDrivers --> [*]
}
Init --> Run: Успешная инициализация
state Run {
[*] --> RunDrivers: 1. Run драйверов
RunDrivers --> StartHTTP: 2. Запуск HTTP серверов
StartHTTP --> StartGRPC: 3. Запуск gRPC серверов
StartGRPC --> StartWorkers: 4. Запуск воркеров
StartWorkers --> Serving: Все компоненты запущены
Serving --> [*]: SIGTERM/SIGINT
}
Run --> Shutdown: Сигнал завершения
state Shutdown {
[*] --> GracefulWorkers: 1. Graceful shutdown воркеров
GracefulWorkers --> StopHTTP: 2. Остановка HTTP (drain)
StopHTTP --> StopGRPC: 3. Остановка gRPC
StopGRPC --> ShutdownDrivers: 4. Shutdown драйверов
ShutdownDrivers --> [*]
}
Shutdown --> [*]: Завершение
Сохранение пользовательского кода¶
Главная особенность: Перегенерируйте весь проект без потери ваших изменений.
Каждый сгенерированный файл содержит disclaimer-маркер:
// ==========================================
// GENERATED CODE - DO NOT EDIT ABOVE THIS LINE
// Changes manually made below will not be overwritten by generator.
// ==========================================
func (h *Handler) CreateUser(ctx context.Context, req *CreateUserRequest) (*User, error) {
// Ваша бизнес-логика здесь
// Этот код переживёт регенерацию!
user := &User{
Email: req.Email,
Name: req.Name,
}
// Кастомная валидация
if err := h.validateBusinessRules(user); err != nil {
return nil, err
}
return h.repo.Create(ctx, user)
}
Правила:
- Код выше маркера регенерируется при каждом запуске
- Код ниже маркера сохраняется навсегда
- Если нужно изменить сгенерированный код — переместите его ниже маркера
Драйверы интеграций¶
Драйвер — адаптер между бизнес-логикой и внешним API.
flowchart LR
subgraph APP["Application"]
SERVICE[Service<br/>бизнес-логика]
end
subgraph INTERFACE["Interface"]
IFACE["SendMessage(ctx, msg)<br/>GetFile(ctx, id)"]
end
subgraph DRIVERS["Drivers"]
TG[Telegram Driver]
SLACK[Slack Driver]
MOCK[Mock Driver]
end
subgraph EXTERNAL["External APIs"]
TG_API[Telegram API]
SLACK_API[Slack API]
end
SERVICE --> IFACE
IFACE -.-> TG
IFACE -.-> SLACK
IFACE -.-> MOCK
TG --> TG_API
SLACK --> SLACK_API
style APP fill:#fff3e0
style INTERFACE fill:#e8f5e9
style DRIVERS fill:#e3f2fd
style EXTERNAL fill:#fce4ec
Принцип работы¶
| Компонент | Ответственность |
|---|---|
| Service | Вызывает абстрактный интерфейс ("отправь сообщение") |
| Interface | Определяет контракт (что можно делать) |
| Driver | Транслирует вызов в конкретный API |
Преимущества:
- Замена провайдера без изменения бизнес-логики
- Лёгкое тестирование через mock-драйверы
- Поддержка нескольких провайдеров одновременно
Интерфейс Runnable¶
Все драйверы реализуют унифицированный интерфейс жизненного цикла:
type Runnable interface {
Init(ctx context.Context) error // Инициализация подключений
Run(ctx context.Context) error // Запуск фоновых процессов
Shutdown(ctx context.Context) error // Немедленная остановка
GracefulShutdown(ctx context.Context) error // Graceful остановка
}
Типы драйверов¶
flowchart TB
subgraph ACTIVE["Активные драйверы"]
TG["Telegram<br/>webhook listener"]
WS["WebSocket<br/>connection pool"]
end
subgraph PASSIVE["Пассивные драйверы"]
S3["S3<br/>on-demand calls"]
DB["PostgreSQL<br/>connection pool"]
end
ACTIVE -->|Run блокирует| GOROUTINE[Фоновая горутина]
PASSIVE -->|Run = no-op| NOOP[Немедленный возврат]
style ACTIVE fill:#fff3e0
style PASSIVE fill:#e8f5e9
| Тип | Примеры | Метод Run |
|---|---|---|
| Активные | Telegram, WebSocket | Блокирует, слушает события |
| Пассивные | S3, PostgreSQL | No-op, вызовы по требованию |
Middleware Stack¶
flowchart TB
REQ[HTTP Request] --> MW1
subgraph MIDDLEWARE["Middleware Chain"]
MW1["1. Context Creation<br/>таймаут, cancel"]
MW2["2. Panic Recovery<br/>перехват паник"]
MW3["3. Request Timing<br/>время начала"]
MW4["4. Tracing<br/>x-req-id header"]
MW5["5. Metrics<br/>Prometheus counters"]
MW6["6. X-Server Header<br/>идентификатор сервера"]
MW7["7. HTTP Auth<br/>(опционально)"]
MW8["8. CSRF Protection<br/>(опционально)"]
MW1 --> MW2 --> MW3 --> MW4 --> MW5 --> MW6 --> MW7 --> MW8
end
MW8 --> HANDLER[Handler<br/>бизнес-логика]
HANDLER --> RESP[HTTP Response]
style MIDDLEWARE fill:#e3f2fd
Описание middleware¶
| # | Middleware | Назначение |
|---|---|---|
| 1 | Context Creation | Создание request context с таймаутом и cancel функцией |
| 2 | Panic Recovery | Перехват паник, логирование stack trace, возврат 500 |
| 3 | Request Timing | Добавление времени начала запроса в context |
| 4 | Tracing | Добавление/проброс trace ID (x-req-id header) |
| 5 | Metrics | Сбор Prometheus метрик (latency, status codes, in-flight) |
| 6 | X-Server Header | Добавление заголовка с идентификатором сервера |
| 7 | HTTP Auth | Actor-based аутентификация (опционально) |
| 8 | CSRF Protection | Валидация CSRF токена (опционально) |
Поток запроса (Request Flow)¶
Полный путь HTTP-запроса через систему:
sequenceDiagram
participant Client
participant Traefik as Traefik<br/>(reverse proxy)
participant HTTP as HTTP Server
participant MW as Middleware Chain
participant Handler as Handler
participant Service as Service
participant Repo as Repository
participant DB as Database
Client->>Traefik: POST /api/v1/users
Traefik->>HTTP: Forward request
HTTP->>MW: Process request
Note over MW: 1. Context + timeout<br/>2. Panic recovery<br/>3. Tracing (x-req-id)<br/>4. Metrics start
MW->>Handler: Call handler
Handler->>Service: CreateUser(ctx, req)
Service->>Repo: repo.Create(ctx, user)
Repo->>DB: INSERT INTO users...
DB-->>Repo: OK
Repo-->>Service: user, nil
Service-->>Handler: user, nil
Handler-->>MW: response
Note over MW: 5. Metrics end<br/>6. Log request
MW-->>HTTP: response
HTTP-->>Traefik: HTTP 201 Created
Traefik-->>Client: response
Структура сгенерированного проекта¶
myservice/ # ~50 файлов, ~8000 строк кода
├── cmd/
│ └── server/ # Точка входа приложения
│ └── main.go
│
├── internal/
│ ├── app/ # Проект-специфичный код
│ │ └── {app_name}/
│ │ ├── transport/
│ │ │ ├── rest/
│ │ │ │ └── {api_name}/
│ │ │ │ ├── handler.go # ← ВАШ КОД ЗДЕСЬ
│ │ │ │ └── middleware.go
│ │ │ └── grpc/
│ │ │ └── {service_name}/
│ │ │ └── handler.go
│ │ ├── worker/
│ │ │ ├── telegram/
│ │ │ │ └── handler.go
│ │ │ └── daemon/
│ │ │ └── handler.go
│ │ └── service/
│ │ └── service.go # ← ВАШ КОД ЗДЕСЬ
│ │
│ └── pkg/ # Переиспользуемый код
│ ├── model/ # Модели данных
│ ├── repository/ # Доступ к данным
│ ├── service/ # Интерфейсы сервисов
│ └── constant/ # Константы (ServiceName)
│
├── pkg/ # Runtime библиотеки
│ ├── app/ # Lifecycle
│ ├── drivers/ # DB, Redis, S3...
│ ├── rest/ # HTTP клиенты
│ └── middleware/ # HTTP middleware
│
├── api/ # API спецификации
│ ├── openapi.yaml # OpenAPI 3.0
│ └── proto/ # Protobuf файлы
│
├── docker-compose.yaml # Локальное окружение
├── docker-compose-dev.yaml # Dev окружение с OnlineConf
├── Dockerfile # Multi-stage build (~50MB)
├── Makefile # 40+ targets
│
└── .github/
└── workflows/ # CI/CD
├── ci.yaml # Test, lint, build
└── release.yaml # Docker push
Генерируемые endpoints¶
System Health/Monitoring (шаблон sys)¶
| Метод | Путь | Назначение |
|---|---|---|
| GET | /version |
Информация о версии |
| GET | /ready |
Readiness probe |
| GET | /live |
Liveness probe |
| GET | /metrics |
Prometheus метрики |
| GET | /debug/pprof/* |
Go pprof профилирование |