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

0001. Выбор ORM и системы миграций

Копия ADR из репозитория: backend/docs/adr/0001-orm-and-migrations.md. Канонический текст — в репозитории; портал обновляется командой npm run sync:adr.

  • Статус: accepted
  • Дата: 2026-05-29
  • Связанные задачи: OP-37 (F4 · Платформа данных)

Контекст и проблема​

Бэкенд «Оптима» на NestJS + PostgreSQL (см. план проекта). Нужен ORM/инструмент доступа к данным и управляемые миграции для всех модулей. В ТЗ (раздел 9.3) детали БД помечены «уточняется». Решение влияет на все модульные эпики (OP-9..OP-23) и блокирует первый «настоящий» модульный тикет, поэтому фиксируется до начала их работы.

Рассмотренные варианты​

  1. TypeORM — нативная интеграция с NestJS (@nestjs/typeorm), декораторные сущности, миграции из коробки (typeorm migration:generate|run|revert), родная поддержка JSONB и кастомных типов через ColumnType. Подходит под модульный монолит: репозитории и сущности живут рядом с модулем. Минус: исторически спорная поддержка сложных аналитических запросов — снимается тем, что тяжёлые отчёты в OP-40 (Spike) могут идти через raw SQL/QueryBuilder.
  2. Prisma — типобезопасный клиент, удобные миграции (prisma migrate), хорошая DX. Минус: отдельная схема .prisma вне TS-исходников, менее «декораторный» стиль, в Nest требует своего модуля-обёртки, и интеграция с class-validator для входных DTO остаётся ручной.
  3. MikroORM — Unit of Work/Identity Map, хорошая типизация, есть Nest-модуль. Минус: менее распространён, меньше примеров под NestJS-стек проекта и под профиль команды (solo + AI).

Решение​

Выбран TypeORM + @nestjs/typeorm. Миграции — встроенный механизм TypeORM (migration:generate|run|revert|show), CLI запускается через data-source.ts из backend/src/core/database/.

Причины:

  • Декораторные сущности и репозитории живут в TS-исходниках модуля — это совпадает с модульным монолитом (см. ADR-0002).
  • Нативная интеграция с DI Nest: @InjectRepository(Entity) без обёрток.
  • JSONB поддерживается типом 'jsonb' из коробки — критично для полу-структурированных данных WB (заказы, отчёты).
  • Для тяжёлых аналитических запросов (OP-40) допустимо использовать QueryBuilder или raw SQL через тот же DataSource — это не блокирующее ограничение.

Последствия​

  • Положительные:
    • Единый слой доступа к данным; модули OP-9..OP-23 пишут репозитории однотипно.
    • Миграции — в репозитории, применение в CI (OP-52) и dev предсказуемо.
    • Базовая сущность с id (uuid), created_at, updated_at, deleted_at (soft-delete) — переиспользуется во всех модулях.
  • Отрицательные / компромиссы:
    • Смена ORM позже — дорого; решение блокирует OP-38 и далее.
    • Для самых тяжёлых отчётов придётся опускаться в QueryBuilder / raw SQL — допустимо как точечное исключение.
  • Дальше:
    • Убрать прямой pg.Pool в DatabaseModule, заменить на TypeOrmModule.forRootAsync (OP-37).
    • Зафиксировать конвенции схемы и миграций в backend/docs/database.md (OP-37).
    • Подсветить выбор в Layer-1 доках при необходимости (только если касается наблюдаемого поведения).