6.8 KiB
Backend data
Ownership и выбор операции
PrismaService — существующий shared DB provider с lifecycle приложения.
prisma/schema.prisma и migrations владеют persistence; generated/prisma
создаётся Prisma и не редактируется вручную. Конфигурация URL находится в
prisma.config.ts/validated environment; SQLite — текущая локальная база.
| Ситуация | Решение |
|---|---|
| Данные для use case | Concrete repository; use case не импортирует PrismaService/Prisma и не строит queries |
| Несколько записей обязаны выполниться вместе | Database transaction вокруг инварианта |
| Конкурентная смена одного значения | Atomic conditional update/constraint с проверкой результата |
| Независимые reads | Не добавляй transaction ради внешнего вида |
| Общая persistence abstraction | Только при реальной общей ответственности; generic repository для одной Prisma реализации не нужен |
Размещение repository определяется Architecture:
в слоистом module это infrastructure/.
Repository — конкретный класс *.repository.ts у business owner, без обязательных
interface/port/adapter. Он владеет queries, includes/filtering, DB mapping и
согласованным чтением. Суффиксы *.persistence.ts, *.store.ts, *.dao.ts
для той же роли не используем. Nest module регистрирует repository приватно.
Новые business decisions (можно ли менять архивную карту, допустим ли перевод)
принадлежат application/domain; repository сохраняет persistence и ownership
ограничения. Existing checks/HTTP helpers в BankCardsRepository — migration
candidate, не повод дублировать их при новом use case или разрывать transaction.
BaseRepository<T>, GenericRepository<T> и RepositoryFactory поверх Prisma
не нужны без отдельной общей ответственности.
BankCardsRepository.summaryData проверяет владельца карты и получает операции
с категориями в одной read transaction. GetBankSummaryUseCase выбирает период,
вызывает repository, затем чистый bankSummary и формирует ответ. Prisma records
не становятся domain entities: расчёт получает только нужные ему значения.
Не создавай обёртку на каждый вызов Prisma. Существующие связные CRUD/transaction
services (auth, lifecycle подключения, изменения карт/операций/категорий)
сохраняют queries рядом с CAS и проверками инварианта, если их перенос требует
лишь набора pass-through методов. Новая самостоятельная read/write capability
получает repository method; самостоятельный use case не выполняет Prisma queries.
BankCardsRepository.write сохраняет общую retry/transaction boundary для
существующих banking services. Такой callback с Prisma transaction client —
локальная техническая граница этих services, не API для domain или use cases.
Выбирай только нужные данные, сохраняй user/account ownership в запросах и
application checks. Предварительный lookup не заменяет unique constraint при race.
Пример: AuthService обрабатывает P2002 и использует conditional updateMany
для refresh rotation. Сначала пойми invariant; не копируй его flow в другой use case.
Transaction не охватывает сетевой T-Bank вызов как будто это одна atomic DB операция. Планируй порядок и compensation по текущей задаче; не удерживай DB lock во время retry внешней сети. Access/cache generation semantics — у T-Bank source.
Деньги и точность
Для денежных значений используй existing decimal helpers и строковый wire value, не JS floating-point арифметику для сумм. Currency — отдельная часть Money; разные валюты не складываются без явно заданного conversion contract. Источник: Money, пример: decimal.ts. UI formatting не меняет backend ownership расчётов и доменную семантику.
Schema и migration
Сначала определи, какие существующие данные/consumers должны сохраниться. Миграцию проверяй на disposable копии с representative existing data; generated client обновляй из schema. Не переписывай уже применённую migration ради нового состояния и не выполняй reset/drop реальной базы как шаг верификации. Для destructive или compatibility-sensitive изменения подготовь backup/restore, переход и проверку отката до его применения. SQLite/local MVP не означает disposable data.
Безопасный путь: additive изменение, заполнение/проверка данных и только затем удаление прежней формы, если задача этого требует. Примеры schema/migrations: Prisma source; исторический rationale — ADR 001, не текущий список таблиц.
Проверка
npx prisma validate; после schema change npx prisma generate и
npm run typecheck:app. Invariants/atomicity/migration safety требуют tests
на соответствующей boundary: mock Prisma не доказывает реальную transaction.
Выбор integration/e2e и известные ограничения — в Testing.