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

Бэкенд — модульный монолит

Бэкенд «Оптимы» — одно 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​

КаталогНазначение
configCoreConfigModule, AppConfigService — валидированный типизированный конфиг
databaseDatabaseModule, BaseEntity/SoftDeletableEntity, SnakeNamingStrategy, миграции, сиды, раннер миграций
redisобщий клиент REDIS_CLIENT (кэш, лимитеры, квоты)
cacheCacheService — cache-aside с TTL и тегами поверх Redis
cryptoEncryptionService — AES-256-GCM с key-ring, проверка стойкости секретов
securityлимитер входящих запросов (@nestjs/throttler, хранилище в Redis), @Public(), @ThrottleAuth/Refresh/Llm()
errorsGlobalExceptionFilter, единый формат ошибки ApiErrorBodyDto, коды ApiErrorCode
validationфабрика исключений ValidationPipe → VALIDATION_FAILED с деталями по полям
paginationDTO пагинации/сортировки, @ApiPaginatedResponse, белые списки фильтров
summaryобщий формат KPI-эндпоинтов GET /<module>/summary (ADR-0053)
observabilityструктурный JSON-логгер, correlation-id (AsyncLocalStorage), access-лог
metricsPrometheus /metrics: HTTP, очереди, пул БД
queueQueueModule (BullMQ), дефолты джоб, composeJobId, конкурентность sync-очередей
queue-observabilityавтообнаружение очередей, метрики очередей, Bull Board /admin/queues
scheduleScheduleRegistryService — реестр периодических расписаний
partitioningменеджер месячных партиций fact-таблиц
re-aggregateочередь пересчёта дневных агрегатов по событию «сырые данные обновлены»

Модули src/modules​

Все модули подключены в AppModule. Префикс маршрутов — без глобального префикса (/auth/..., /digitization/...).

МодульТЗ / эпикНазначениеПрефиксы маршрутов
authF2Регистрация, вход, refresh-ротация, выход, смена роли; глобальный GlobalJwtAuthGuard/auth
usersF2Список пользователей для администрирования/users
wb-accountsF2Кабинеты WB: CRUD, зашифрованный API-ключ, проверка ключа, квоты лимитера/wb-accounts
brand-accessF2Явные доступы менеджеров к брендам, BrandAccessGuard/brand-access
wb-apiF3Глобальный клиент WB API: лимитер, ретраи, перенос джоб, gate кабинета— (без HTTP)
catalogF4 · 5.11Бренды/магазины/товары кабинета; синк карточек и цен из WB; единый каталог номенклатуры/brands, /catalog
audit-logF6Журнал аудита: AuditService.record, @Audit(...) + глобальный AuditInterceptor/audit-logs
llmF7Абстракция LLM-провайдера (LLM_PROVIDER), ретраи, суточная квота токенов—
prompt-templatesF7Версионируемые шаблоны промптов (render(key, vars))—
ragF7Векторный поиск по knowledge_chunks (pgvector) и сборка контекста для промпта—
digitization5.1Импорт заказов/продаж/отчёта реализации WB, комиссии и удержания, P&L, отчёты, плановый синк фидов/digitization, /digitization/reports
pricing5.2Изменение цен/скидок (одиночное и массовое через очередь), история изменений/pricing
repricer5.3Правила репрайсера, движок пересчёта, расписание, журнал прогонов/repricer, /repricer/rules, /repricer/products
autobidder5.4Кампании, ставки, стратегии, бюджеты, аналитика рекламы, движок ставок, журнал ставок/autobidder, /autobidder/{strategies,budgets,analytics,run}
autoreply5.5Отзывы и вопросы WB, сценарии, шаблоны, генерация ответов, отправка, жалобы/autoreply, /autoreply/{scenarios,templates,history,complaints,complaint-rules}
rnp5.6Дневные витрины РНП (продажи, реклама, финансы, воронка), аналитика, списки товаров/rnp/analytics, /rnp/product-lists
accounting5.7Доходы/расходы поверх P&L оцифровки, ручные операции, сверка/accounting/reports, /accounting/transactions
booking5.8Задания на поиск слотов поставки, мониторинг коэффициентов приёмки, склады/booking, /booking/tasks, /booking/warehouses
notifications5.8Лента уведомлений пользователя (канал in_app), продюсеры событий/notifications
ai-assistant5.9Диалог с ИИ-помощником (RAG + инструменты над данными РНП/оцифровки/рекламы), анализ товара, история/ai-assistant
ai-agent5.10ИИ-агент маркетолога: разбор задачи, план действий, подтверждение, выполнение с откатом, история/ai-agent
inventory5.11Остатки (проекция + снимки WB), журнал движения товара, операции, синк остатков/inventory
legal5.12Шаблоны досудебных претензий, генерация текста через LLM, редактирование/legal/claims
content5.13Генерация текстов карточек через LLM, хранение и версии контента/content
ab-test5.14A/B-тесты карточек: варианты, фото вариантов, периоды, метрики CTR, выбор победителя, выкладка на WB/ab-tests, /ab-test-images
knowledge-base5.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-тестах, поэтому тестовый и прод-стек ведут себя одинаково).

ЭлементГдеЧто делает
helmetapp.bootstrap.tsзаголовки безопасности, X-Frame-Options: DENY (CSP выключен — API не отдаёт HTML, кроме Swagger)
traceIdMiddlewarecore/observabilityпринимает или генерирует X-Request-Id, кладёт его в AsyncLocalStorage
ThrottlerGuardAPP_GUARD, первыйограничивает частоту запросов до любой криптографии
GlobalJwtAuthGuardAPP_GUARD, второйтребует access-токен везде, кроме маршрутов с @Public()
LoggingInterceptorAPP_INTERCEPTOR (LoggingModule)access-лог каждого запроса
HttpMetricsInterceptorAPP_INTERCEPTOR (MetricsModule)гистограмма optima_http_request_duration_seconds
AuditInterceptorAPP_INTERCEPTOR (AuditLogModule)пишет запись аудита для методов с @Audit(...)
ValidationPipeглобальный pipetransform, 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/swagger CLI-плагин: @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).

Связанные ADR​