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

Соглашения и паттерны

Этот раздел описывает повторяющиеся паттерны кода. Понимание этих соглашений — ключ к тому, чтобы добавлять новые фичи «в том же стиле».

Каждый домен в libs/apis/providers/<service>/ делится на два вида проектов Nx:

providers/<service>/
├── data-access/ # Один проект: DTO, константы, типы, интерфейсы
│ └── src/lib/{dtos,constants,types,interfaces}/
└── features/<feature>/ # Проект на фичу: модуль + логика
└── src/lib/
├── <feature>.module.ts
├── <feature>.controller.ts
├── <feature>.service.ts
└── <feature>.repository.ts
  • data-access не содержит контроллеров и сервисов — только контракты данных. Его импортируют и фичи, и другие сервисы.
  • features/* — самодостаточные NestJS-модули, экспортируемые через барель src/index.ts.
Controller → Service → Repository → PrismaClientService / RedisClient / RabbitClient
(HTTP) (логика) (доступ к БД) (инфраструктура)
  • Controller — только маршрутизация, guard’ы, извлечение данных из запроса (через декораторы), вызов сервиса. Без логики.
  • Service — бизнес-правила, оркестрация, транзакции.
  • Repository — инкапсулирует запросы Prisma/Redis. Именно здесь PrismaClientService.

Не в каждой фиче есть все три слоя (простые фичи могут обходиться без repository), но направление зависимостей всегда сверху вниз.

СуффиксНазначение
*.module.tsNestJS-модуль
*.controller.tsHTTP-контроллер
*-core.module.ts«Ядровый» модуль без контроллеров — переиспользуемый набор провайдеров (сервис+репозиторий), который импортируют другие модули
*.service.tsСервис с логикой
*.repository.tsДоступ к данным
*.strategy.tsPassport-стратегия
*.guard.tsGuard авторизации
*.dto.tsDTO (валидация + сериализация)
*.type.ts / *.interface.tsТипы/интерфейсы
*.constants.tsКонстанты (имена стратегий, ключи Redis и т.п.)
*.env.ts / *.validation.ts / *.config.tsКонфиг-модуль (см. ниже)

Часто фича имеет xxx-core.module.ts (экспортирует сервис/репозиторий, без контроллеров) и xxx.module.ts (импортирует core + подключает контроллеры). Это позволяет другому сервису переиспользовать логику без HTTP-слоя.

Импорты идут через алиасы из tsconfig.base.json, а не по относительным путям:

import { CustomerAuthModule } from '@crewsforge-back/apis/providers/auth-api/features/customer-auth';
import { PrismaClientService } from '@crewsforge-back/apis/utils/prisma-client';
import { PrismaExceptionFilter } from '@crewsforge-back/apis/shared';

Каждый lib-проект экспортирует публичный API через src/index.ts (барель). Импортировать внутренние файлы напрямую нельзя — только то, что реэкспортировано из index.ts.

  • Глобальный ValidationPipe с transform: true и transformOptions.strategy = 'excludeAll' — значит в ответ попадают только поля, помеченные @Expose() (безопасно по умолчанию).
  • У части сервисов дополнительно whitelist: true / forbidNonWhitelisted: true — отсекают лишние поля во входе.
  • Маппинг сущность → DTO делается через общий MapperService (libs/apis/shared, на базе class-transformer).

Каждый конфиг — отдельный Nx-проект в libs/apis/configs/ из 4-5 файлов:

<config>/src/lib/
├── <config>.env.ts # маппинг process.env → объект
├── <config>.validation.ts # Joi-схема валидации
├── <config>.config.ts # registerAs(...) namespace
├── <config>-config.service.ts # типизированный сервис-обёртка
└── <config>-config.module.ts # ConfigModule.forFeature(...)

Сервисы инжектят типизированный XxxConfigService вместо process.env. Полный список переменных — в configs.

В каждом приложении подключён ClsModule.forRoot(...) с ClsPluginTransactional:

  • Транзакции объявляются декларативно (через @Transactional() в сервисах) и живут в CLS-контексте запроса.
  • X-Request-Id берётся из заголовка или генерируется (uuid) — сквозная трассировка.
  • JWT access/refresh, стратегии passport по типу пользователя (customer/employee/admin).
  • Guard’ы и декораторы (@Token(), @HeaderFingerprint(), @HeaderSessionId()) — в shared-lib.
  • Refresh-токены передаются через httpOnly cookie (cookie-parser + cookie.helper).

Глобальный PrismaExceptionFilter (libs/apis/shared) маппит ошибки Prisma (например, нарушение уникальности) в корректные HTTP-статусы. Подключается в main.ts каждого сервиса.

  1. Создать DTO в data-access нужного сервиса (@Expose() на выходных полях).
  2. Создать features/<feature>/ с module/controller/service/(repository).
  3. Экспортировать модуль через src/index.ts.
  4. Добавить алиас в tsconfig.base.json (если Nx-генератор не сделал сам).
  5. Подключить модуль в apps/<service>/src/app/app.module.ts.
  6. Guard’ы и маппинг брать из shared, конфиги — из configs.