Бэкенд — модульный монолит
Бэкенд «Оптимы» — одно NestJS-приложение (NestJS 11, TypeScript, Node.js 22) в
каталоге backend/. Один процесс обслуживает HTTP API, воркеры BullMQ и
планировщик расписаний. Хранилища — PostgreSQL (TypeORM) и Redis (ioredis).
Раскладка src/core + src/modules
Решение — ADR-0002.
backend/src/
main.ts точка входа: логгер, CORS, глобальный конвейер, Swagger, Bull Board, /metrics
app.module.ts корень композиции: core-модули + фичи-модули + глобальные guard'ы
app.bootstrap.ts applyGlobalApiPipeline / applyTrustProxy (общие для prod и e2e)
swagger.ts сборка OpenAPI-документа и глобальные error-ответы
health/ GET /health, /health/live, /version
core/ горизонтальные сервисы, без бизнес-логики (@Global)
modules/<feature>/ бизнес-модули (ТЗ 5.1–5.15) и фундамент F2/F3/F6/F7
src/core/— только горизонтальные сервисы. Модули объявлены@Global()и доступны фичам через DI без явного импорта.src/modules/<feature>/— фичи-модули:*.module.ts,*.controller.ts,*.service.ts,dto/,entities/,*.spec.tsрядом с исходником.- Доступ к окружению — только через
AppConfigService(env валидируется zod-схемойcore/config/env.schema.tsна старте; ошибка в env роняет bootstrap). Прямое чтениеprocess.envв коде фич запрещено. - Доступ к PostgreSQL —
@InjectRepository(Entity)послеTypeOrmModule.forFeature([...])или@InjectDataSource()для сырого SQL и транзакций; к Redis —@Inject(REDIS_CLIENT). - Фичи не импортируют внутренности друг друга: зависимость оформляется импортом модуля,
который экспортирует нужный сервис (например,
WbAccountsModule→WbAccountsService).
Модули src/core
| Каталог | Назначение |
|---|---|
config | CoreConfigModule, AppConfigService — валидированный типизированный конфиг |
database | DatabaseModule, BaseEntity/SoftDeletableEntity, SnakeNamingStrategy, миграции, сиды, раннер миграций |
redis | общий клиент REDIS_CLIENT (кэш, лимитеры, квоты) |
cache | CacheService — cache-aside с TTL и тегами поверх Redis |
crypto | EncryptionService — AES-256-GCM с key-ring, проверка стойкости секретов |
security | лимитер входящих запросов (@nestjs/throttler, хранилище в Redis), @Public(), @ThrottleAuth/Refresh/Llm() |
errors | GlobalExceptionFilter, единый формат ошибки ApiErrorBodyDto, коды ApiErrorCode |
validation | фабрика исключений ValidationPipe → VALIDATION_FAILED с деталями по полям |
pagination | DTO пагинации/сортировки, @ApiPaginatedResponse, белые списки фильтров |
summary | общий формат KPI-эндпоинтов GET /<module>/summary (ADR-0053) |
observability | структурный JSON-логгер, correlation-id (AsyncLocalStorage), access-лог |
metrics | Prometheus /metrics: HTTP, очереди, пул БД |
queue | QueueModule (BullMQ), дефолты джоб, composeJobId, конкурентность sync-очередей |
queue-observability | автообнаружение очередей, метрики очередей, Bull Board /admin/queues |
schedule | ScheduleRegistryService — реестр периодических расписаний |
partitioning | менеджер месячных партиций fact-таблиц |
re-aggregate | очередь пересчёта дневных агрегатов по событию «сырые данные обновлены» |
Модули src/modules
Все модули подключены в AppModule. Префикс маршрутов — без глобального префикса
(/auth/..., /digitization/...).
| Модуль | ТЗ / эпик | Назначение | Префиксы маршрутов |
|---|---|---|---|
auth | F2 | Регистрация, вход, refresh-ротация, выход, смена роли; глобальный GlobalJwtAuthGuard | /auth |
users | F2 | Список пользователей для администрирования | /users |
wb-accounts | F2 | Кабинеты WB: CRUD, зашифрованный API-ключ, проверка ключа, квоты лимитера | /wb-accounts |
brand-access | F2 | Явные доступы менеджеров к брендам, BrandAccessGuard | /brand-access |
wb-api | F3 | Глобальный клиент WB API: лимитер, ретраи, перенос джоб, gate кабинета | — (без HTTP) |
catalog | F4 · 5.11 | Бренды/магазины/товары кабинета; синк карточек и цен из WB; единый каталог номенклатуры | /brands, /catalog |
audit-log | F6 | Журнал аудита: AuditService.record, @Audit(...) + глобальный AuditInterceptor | /audit-logs |
llm | F7 | Абстракция LLM-провайдера (LLM_PROVIDER), ретраи, суточная квота токенов | — |
prompt-templates | F7 | Версионируемые шаблоны промптов (render(key, vars)) | — |
rag | F7 | Векторный поиск по knowledge_chunks (pgvector) и сборка контекста для промпта | — |
digitization | 5.1 | Импорт заказов/продаж/отчёта реализации WB, комиссии и удержания, P&L, отчёты, плановый синк фидов | /digitization, /digitization/reports |
pricing | 5.2 | Изменение цен/скидок (одиночное и массовое через очередь), история изменений | /pricing |
repricer | 5.3 | Правила репрайсера, движок пересчёта, расписание, журнал прогонов | /repricer, /repricer/rules, /repricer/products |
autobidder | 5.4 | Кампании, ставки, стратегии, бюджеты, аналитика рекламы, движок ставок, журнал ставок | /autobidder, /autobidder/{strategies,budgets,analytics,run} |
autoreply | 5.5 | Отзывы и вопросы WB, сценарии, шаблоны, генерация ответов, отправка, жалобы | /autoreply, /autoreply/{scenarios,templates,history,complaints,complaint-rules} |
rnp | 5.6 | Дневные витрины РНП (продажи, реклама, финансы, воронка), аналитика, списки товаров | /rnp/analytics, /rnp/product-lists |
accounting | 5.7 | Доходы/расходы поверх P&L оцифровки, ручные операции, сверка | /accounting/reports, /accounting/transactions |
booking | 5.8 | Задания на поиск слотов поставки, мониторинг коэффициентов приёмки, склады | /booking, /booking/tasks, /booking/warehouses |
notifications | 5.8 | Лента уведомлений пользователя (канал in_app), продюсеры событий | /notifications |
ai-assistant | 5.9 | Диалог с ИИ-помощником (RAG + инструменты над данными РНП/оцифровки/рекламы), анализ товара, история | /ai-assistant |
ai-agent | 5.10 | ИИ-агент маркетолога: разбор задачи, план действий, подтверждение, выполнение с откатом, история | /ai-agent |
inventory | 5.11 | Остатки (проекция + снимки WB), журнал движения товара, операции, синк остатков | /inventory |
legal | 5.12 | Шаблоны досудебных претензий, генерация текста через LLM, редактирование | /legal/claims |
content | 5.13 | Генерация текстов карточек через LLM, хранение и версии контента | /content |
ab-test | 5.14 | A/B-тесты карточек: варианты, фото вариантов, периоды, метрики CTR, выбор победителя, выкладка на WB | /ab-tests, /ab-test-images |
knowledge-base | 5.15 | Разделы и материалы базы знаний, права доступа, загрузка документов и индексация в RAG | /knowledge-base |
examples | — | Демо-эндпоинты конвенций (пагинация, очередь). Не подключается в production | /examples/* |
Служебные эндпоинты вне модулей: GET /health, GET /health/live, GET /version,
GET /metrics, Swagger UI /api/docs, Bull Board /admin/queues
(см. Наблюдаемость и Безопасность).
Глобальный конвейер запроса
Собирается в main.ts и app.bootstrap.ts (applyGlobalApiPipeline используется
и в e2e-тестах, поэтому тестовый и прод-стек ведут себя одинаково).
| Элемент | Где | Что делает |
|---|---|---|
helmet | app.bootstrap.ts | заголовки безопасности, X-Frame-Options: DENY (CSP выключен — API не отдаёт HTML, кроме Swagger) |
traceIdMiddleware | core/observability | принимает или генерирует X-Request-Id, кладёт его в AsyncLocalStorage |
ThrottlerGuard | APP_GUARD, первый | ограничивает частоту запросов до любой криптографии |
GlobalJwtAuthGuard | APP_GUARD, второй | требует access-токен везде, кроме маршрутов с @Public() |
LoggingInterceptor | APP_INTERCEPTOR (LoggingModule) | access-лог каждого запроса |
HttpMetricsInterceptor | APP_INTERCEPTOR (MetricsModule) | гистограмма optima_http_request_duration_seconds |
AuditInterceptor | APP_INTERCEPTOR (AuditLogModule) | пишет запись аудита для методов с @Audit(...) |
ValidationPipe | глобальный pipe | transform, whitelist, forbidNonWhitelisted; ошибки → VALIDATION_FAILED |
GlobalExceptionFilter | глобальный filter | любые исключения → { code, message, details?, traceId } |
Дополнительно в main.ts:
- CORS — в dev/test открыт для любого origin; в production разрешены только
origin'ы из
CORS_ORIGINS(через запятую), без переменной CORS-заголовки не выдаются. trust proxy—TRUST_PROXY_HOPS(по умолчанию 1): за обратным прокси Coolify лимитер видит реальный IP клиента.- Graceful shutdown —
enableShutdownHooks(): по SIGTERM закрываются пул PostgreSQL, клиент Redis и воркеры BullMQ. - Миграции применяет entrypoint контейнера до старта приложения (см. Данные).
Формат ошибки
Все ошибки API имеют одну форму (ApiErrorBodyDto): code, message, опционально
details (для VALIDATION_FAILED — список полей), traceId. Коды (core/errors/api-error-code.ts):
VALIDATION_FAILED, BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT,
TOO_MANY_REQUESTS, WB_AUTH_FAILED (502 — WB отверг ключ кабинета), WB_UNAVAILABLE (502),
LLM_UNAVAILABLE (502), SERVICE_UNAVAILABLE (503), INTERNAL_ERROR.
Конвенции разработки
- Документирование вместе с кодом, на русском. Каждый публичный класс, метод и DTO —
TSDoc-комментарий; из него Compodoc строит документацию кода
(
npm run docs, порог покрытия 80% —npm run docs:coverage, проверяется в CI). - Swagger. Контроллеры размечаются
@ApiTags,@ApiOperation({ summary }),@ApiOkResponse({ type })и т. п. Глобальные ответы 400/401/403/404/500 сApiErrorBodyDtoдобавляет пост-процессорsrc/swagger.ts— повторять их на эндпоинтах не нужно. - DTO. Включён
@nestjs/swaggerCLI-плагин:@ApiPropertyгенерируется по типам, декораторамclass-validatorи TSDoc. Явный@ApiProperty— только дляexample,enum, полиморфизма. Валидация —class-validator; лишние поля отклоняются. - Контракт.
npm run docs:openapiвыгружаетopenapi.json;npm run test:contractсверяет список путей. Изменение набора эндпоинтов требует обновленияtest/openapi.e2e-spec.ts. - Пагинация и фильтры — общие DTO из
core/pagination; фильтры принимаются только из белого списка. - Решения — новый ADR в
backend/docs/adr/(публикуются в журнал ADR). - Гейты качества: pre-commit (husky + lint-staged:
eslint --fixиtsc --noEmitпо staged-файлам), CI (backend-ci.yml: lint, unit-тесты, миграции, e2e миграций, сборка, покрытие документации, выгрузка OpenAPI).