docs: refine backend conventions and routing
Backend security / security (push) Successful in 8s
Backend verify / verify (push) Successful in 2m12s
Conventional commits / commits (pull_request) Successful in 25s
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 2m3s

This commit is contained in:
2026-09-15 05:42:16 +03:00
parent 660b250ecd
commit 9810d47e42
12 changed files with 201 additions and 131 deletions
+7 -23
View File
@@ -1,25 +1,9 @@
# Backend router # Backend conventions: вход
Объединяй triggers из смысла задачи, путей и обнаруженных boundaries. Актуальный routing по задаче, пути и boundary находится в [AGENTS](../AGENTS.md).
Читай выбранные guides до решения; якорь допускает чтение только нужного раздела. Выбирай только активированные темы; при новой границе повтори routing.
Просто backend-файл не требует всех conventions. [Architecture](architecture.md) объясняет mental model, [SOURCES](SOURCES.md)
ведёт к системным фактам, specifications и ADR по конкретному вопросу.
| Решение / сигнал | Читать | Этот адрес сохранён для существующих входящих ссылок; второй копии правил
| --- | --- | и таблицы маршрутов здесь нет.
| Новый файл/suffix/layer; поиск feature owner; progressive structure; перенос responsibility | [Architecture](conventions/backend-architecture.md) |
| TS vocabulary, domain literals, magic values | [Domain literals](conventions/backend-architecture.md#domain-literals-and-magic-values) |
| Controller, DTO, route/status, validation/exception, публичная HTTP-семантика | [HTTP API](conventions/backend-http-api.md) |
| Repository/data access; Prisma/schema/query, transaction, migration, atomic write | [Data](conventions/backend-data.md) |
| Тесты или behavior с testing risk | [Testing](conventions/backend-testing.md) |
| JWT, cookie, login/refresh/logout, guard/access, token encryption, sensitive logs | [Security](conventions/security-auth.md) |
| External client/integration boundary; T-Bank/gRPC/proto, retry/rate limit/cache | [Integrations](conventions/backend-integrations.md) |
| Logging/request context без изменения secrets/auth | [Logging](conventions/backend-architecture.md#logging) |
| Новый abstraction/dependency, refactor, неясный scope | [Общий выбор реализации](../../AGENTS.md) |
| Изменение request/response/route/status/error shape или Swagger metadata | HTTP API + [Источник HTTP-контракта](../../docs/architecture/http-api.md#источник-контракта) |
Новый endpoint обычно требует HTTP API + Testing; Architecture/Data/Security
добавляются по новым ответственности, persistence или access rules.
Изменение одного Prisma query без HTTP/auth границы не требует HTTP API/Security.
Auth refresh активирует Security + Data + Testing; HTTP API добавляется, если
меняется controller/HTTP contract. Exact docs/spec sources выбирай через SOURCES.
Повтори routing при расширении diff, включая новые и unstaged файлы.
+1 -1
View File
@@ -2,7 +2,7 @@
| Что нужно установить | Владелец | | Что нужно установить | Владелец |
| --- | --- | | --- | --- |
| Implementation decision и безопасный pattern | [CONVENTIONS](CONVENTIONS.md) | | Implementation decision и безопасный pattern | [AGENTS](../AGENTS.md) |
| HTTP errors, DTO semantics, public wire shape | [docs: HTTP API](../../docs/architecture/http-api.md); runtime OpenAPI из Nest — единственный editable source | | HTTP errors, DTO semantics, public wire shape | [docs: HTTP API](../../docs/architecture/http-api.md); runtime OpenAPI из Nest — единственный editable source |
| Auth/session, account access, token encryption | [Аутентификация](../../docs/domains/authentication/overview.md), [доступ к данным](../../docs/architecture/containers.md#хранимые-данные-и-границы-доступа), [подключение брокера](../../docs/integrations/tbank.md) | | Auth/session, account access, token encryption | [Аутентификация](../../docs/domains/authentication/overview.md), [доступ к данным](../../docs/architecture/containers.md#хранимые-данные-и-границы-доступа), [подключение брокера](../../docs/integrations/tbank.md) |
| Prisma schema/data ownership и migration context | [docs: Data](../../docs/architecture/containers.md#хранимые-данные-и-границы-доступа) | | Prisma schema/data ownership и migration context | [docs: Data](../../docs/architecture/containers.md#хранимые-данные-и-границы-доступа) |
+44
View File
@@ -0,0 +1,44 @@
# Backend mental model
NestJS + TypeScript modular monolith. Composition root `AppModule` подключает
бизнес-модули, validated configuration, Prisma и Pino. Общая `configureApp`
задаёт `/api/v1`, CORS/cookies, validation, request logging и error filter.
Точные versions/scripts — в [package.json](../package.json); source code
определяет фактическую реализацию.
```text
HTTP controller → application service/use case → pure domain rules
↓
repository/data access → Prisma → SQLite
↓
integration client → T-Bank gRPC / MOEX / NCR HTTP
```
`modules/auth`, `modules/banking/bank-cards` и области `modules/investments`
владеют пользовательскими возможностями. Сложные модули разделены на
presentation/application/domain/infrastructure; компактные остаются плоскими.
`*.module.ts` — Nest DI, `*.api.ts` — используемые cross-module exports.
Application использует конкретные providers; обязательных ports на каждый
класс нет. [Module convention](conventions/backend-architecture.md).
Stocks/bonds — slices одного instruments module: catalog владеет identity/cache,
market — котировками, income — выплатами/личными позициями. Общая orchestration
в `instruments/application` использует DTO/provider records и не становится pure
domain из-за extraction в функцию. Boundary mappers преобразуют внешние records;
чистые расчёты portfolio/banking остаются у domain owner.
PrismaService владеет DB lifecycle. Самостоятельные use cases получают данные
через concrete repositories; существующие связные CRUD/transaction services
сохраняют прямые queries по [data convention](conventions/backend-data.md).
Money — currency + decimal string; расчёты используют точные общие helpers.
Auth управляет identity/session; TbankAccessService проверяет доступ к счёту и
connection generation. Integration clients владеют protocol/deadline/retry;
provider records преобразуются перед чистыми domain calculations.
Пользовательский `tbank-connection` не владеет низкоуровневым gRPC client.
Runtime OpenAPI из Nest → предоставленный frontend snapshot → generated types.
Problem Details фильтруются общей HTTP boundary; StructuredLogger добавляет
request context и ограничивает поля событий. Jest unit tests, изолированные
SQLite/HTTP suites и OpenAPI parity доказывают разные части поведения.
Системные потоки и ADR выбираются через [SOURCES](SOURCES.md).
+20 -73
View File
@@ -2,11 +2,8 @@
## Module-first organization ## Module-first organization
Backend — domain-oriented modular monolith с лёгким разделением Устройство и data flow — в [mental model](../architecture.md).
по ответственности. `src/modules` группирует код по бизнес-возможности: `auth`, `banking/bank-cards`, области `investments`. Сначала выбери business owner в `modules/`, затем ответственность внутри него.
В инвестициях отдельные Nest modules владеют `brokerage-accounts`, `portfolio`,
`operations`, `income`, `upcoming-events`, `instruments`, `tbank-connection`.
`instruments/stocks` и `instruments/bonds` — slices внутри одного модуля.
### Progressive structure ### Progressive structure
@@ -36,15 +33,6 @@ feature/
Для одного controller или пары DTO дополнительная поддиректория не нужна. Для одного controller или пары DTO дополнительная поддиректория не нужна.
Пустые папки, новые barrels, CQRS и interfaces не требуются для этой организации. Пустые папки, новые barrels, CQRS и interfaces не требуются для этой организации.
В текущем коде слои используют `auth`, `bank-cards`, `portfolio`, `operations`
и slices `instruments/stocks`, `instruments/bonds`. Общие application helpers
инструментов находятся в `instruments/application`; `instruments.module.ts`
компонует оба slice. `stock-position.ts` и `instrument-position.ts` формируют
результат из portfolio DTO; `instrument-market.ts` обрабатывает внешние данные
и ошибки Nest. Это application code, а не независимая domain model.
`brokerage-accounts`, `income`, `upcoming-events`, `tbank-connection` остаются
компактными плоскими modules. Наличие tests само по себе не требует слоёв.
## Поиск и создание файла ## Поиск и создание файла
При работе над feature проходи владельцев в порядке: При работе над feature проходи владельцев в порядке:
@@ -134,6 +122,10 @@ Tests могут импортировать внутренний owner для п
Новый export требует текущего потребителя; controller и persistence остаются Новый export требует текущего потребителя; controller и persistence остаются
приватными. Не добавляй `index.ts` на каждом уровне. приватными. Не добавляй `index.ts` на каждом уровне.
Circular dependencies устраняй изменением ownership/composition. `forwardRef()`
не является стандартным исправлением boundaries; исключение требует конкретной
неизбежной связи и проверки DI initialization.
Каждый provider регистрируется у одного владельца. Потребители импортируют Каждый provider регистрируется у одного владельца. Потребители импортируют
его module, а не повторяют provider: это сохраняет единственный кэш и лимитер. его module, а не повторяют provider: это сохраняет единственный кэш и лимитер.
`AuthModule` экспортирует guard; наличие JWT не заменяет account access check. `AuthModule` экспортирует guard; наличие JWT не заменяет account access check.
@@ -169,30 +161,18 @@ DTO остаются транспортными runtime classes; существ
Сохраняй существующий error contract: HTTP exceptions остаются на application/ Сохраняй существующий error contract: HTTP exceptions остаются на application/
transport boundary. Чистому расчёту не нужен новый framework ошибок. transport boundary. Чистому расчёту не нужен новый framework ошибок.
Mapping и stable codes — по [Errors](backend-error-handling.md).
Точные ограничения — [HTTP](backend-http-api.md), [Data](backend-data.md), Точные ограничения — [HTTP](backend-http-api.md), [Data](backend-data.md),
[Security](security-auth.md), [Integrations](backend-integrations.md). [Security](security-auth.md), [Integrations](backend-integrations.md).
## Сценарии и размер сервисов ## Сценарии и размер сервисов
Разделяй независимые операции, а не строки. `BankCardsService` отвечает за карты, Разделяй независимые операции, а не строки. Пример: `BankCardsService` владеет
`BankOperationsService` — журнал/переводы/возвраты, `ExpenseCategoriesService` — картами, `BankOperationsService` — журналом/переводами, `GetBankSummaryUseCase` —
категории, `GetBankSummaryUseCase` — сводку. `BankCardsRepository` сохраняет сводкой через repository и чистый расчёт. Не разрывай денежную transaction,
общий transaction retry и owner lookup; денежные транзакции не разрываются. общий cache/limiter или pending promises при extraction.
Чистые расчёты банковских карт находятся в `domain` этого модуля.
У инструментов `StockCatalogService` / `BondCatalogService` хранят идентичность Расширение abstractions — по [change scope](../../../.ai/conventions/code-health.md#change-scope).
листинга и общий кэш slice; market service — котировки/свечи, income service — выплаты/личные позиции.
`GetStockCompanyUseCase` владеет сведениями об эмитенте. Ключи, TTL и pending
promises разделяют прежний экземпляр кэша; extract не должен менять generation.
Portfolio и operations сохраняют связную координацию снимков/страниц и кэшей.
Расчёты портфеля находятся в `portfolio/domain`; преобразование брокерских
позиций — в `tbank-portfolio.mapper.ts`. `bond-money.mapper.ts` преобразует
ответ клиента и не является value object.
Выбирай обычную функцию для чистого вычисления; repository contract — только
для реальной границы. CQRS/buses/events, BaseRepository/BaseService, DI token
для каждого класса, mapper/presenter для каждого DTO и новые dependencies
не нужны для структурного переноса.
## Common ## Common
@@ -205,39 +185,12 @@ protobuf units/nano conversion — T-Bank integration. Domain используе
## Domain literals and magic values ## Domain literals and magic values
Finite business vocabulary must be modeled explicitly; incidental implementation Общий выбор symbolic values и числовых policies —
literals stay inline. Это относится к error codes, statuses, operation/account/ [Domain values](../../../.ai/conventions/code-health.md#domain-values).
instrument/transaction types, permissions, roles, feature states и бизнес-режимам. Сохраняй strict TS и explicit boundary types. Для недоверенного input выбирай
`unknown` + runtime narrowing; отключённый lint `no-explicit-any` не делает
Перед новым определением найди existing owner/type/value. В branching и comparisons `any` нормой domain/application model. `declare` допустим для заполняемых извне
используй символическое значение владельца; не повторяй raw domain literals. DTO fields, не для сокрытия неинициализированного owned state.
Храни словарь у domain/module/entity с соблюдением границ слоёв, без глобальной
свалки `constants.ts`. Сохраняй принятый pattern; для нового project-owned словаря
без подходящего аналога используй `as const` с derived union:
```ts
const BankErrorCode = {
InvalidDate: "BANK_INVALID_DATE",
ImmutableOperation: "BANK_IMMUTABLE_OPERATION",
} as const;
type BankErrorCode = (typeof BankErrorCode)[keyof typeof BankErrorCode];
// Вместо code === "BANK_INVALID_DATE":
if (code === BankErrorCode.InvalidDate) { /* existing handling */ }
```
Значимые numeric policies (timeout, retry, page size, threshold, coefficient)
называй по смыслу и единицам: `REQUEST_TIMEOUT_MS`, `CACHE_TTL_SECONDS`,
`MAX_RETRY_COUNT`, `PAGE_SIZE`. Оставляй inline UI labels в принятом месте,
log messages, индексы, `index + 1`, `Math.max(value, 0)`, `page ?? 1`,
`percentage * 100` и очевидные локальные детали без semantic benefit.
При неясном owner/смысле оставь значение и укажи ограничение в review.
Не мигрируй корректные existing enum/union styles массово. Не добавляй ради
правила factories, registries, schemas, loaders, abstraction layers, dependencies,
custom lint/AST rules, generation/validation scripts или тесты существования
констант. Semantic review проверяет ownership, единое написание и неизменность
wire values; обычные behavior tests проверяют поведение.
OpenAPI enum metadata и runtime validation должны читать тот же vocabulary, OpenAPI enum metadata и runtime validation должны читать тот же vocabulary,
что application/domain. Например, `BankOperationKind` принадлежит bank-cards/domain, что application/domain. Например, `BankOperationKind` принадлежит bank-cards/domain,
@@ -255,14 +208,8 @@ semantic magic strings проверяются conventions/review без ново
## Logging ## Logging
Единственный logger — nestjs-pino; application events используют существующий События, levels, correlation и safe fields — по
[StructuredLogger](../../src/common/logging/structured-logger.service.ts) и [Observability](backend-observability.md).
request context. Выбирай стабильное имя события и разрешённые scalar fields.
Wrapper отбрасывает неизвестные поля, но это не разрешение помещать secret
в `message`/`stack`: review sensitive logging обязателен.
Не добавляй второй logger или сырой request/body/error payload.
Источник rationale — [ADR 008](../../../docs/adr/008-pino-backend-logging.md);
при secrets/user data читай также Security.
## Проверка ## Проверка
+7
View File
@@ -22,6 +22,13 @@ interface/port/adapter. Он владеет queries, includes/filtering, DB mapp
согласованным чтением. Суффиксы `*.persistence.ts`, `*.store.ts`, `*.dao.ts` согласованным чтением. Суффиксы `*.persistence.ts`, `*.store.ts`, `*.dao.ts`
для той же роли не используем. Nest module регистрирует repository приватно. для той же роли не используем. 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` проверяет владельца карты и получает операции `BankCardsRepository.summaryData` проверяет владельца карты и получает операции
с категориями в одной read transaction. `GetBankSummaryUseCase` выбирает период, с категориями в одной read transaction. `GetBankSummaryUseCase` выбирает период,
вызывает repository, затем чистый `bankSummary` и формирует ответ. Prisma records вызывает repository, затем чистый `bankSummary` и формирует ответ. Prisma records
+40
View File
@@ -0,0 +1,40 @@
# Backend errors
## Поток
```text
application/domain outcome → application mapping → HttpExceptionFilter
→ application/problem+json
```
Сохраняй принятый профиль RFC 9457: `status`, стабильный публичный `code`,
для validation — `errors: [{ pointer, detail }]`. Точный контракт и известное
расхождение validation описаны в [HTTP API](../../../docs/architecture/http-api.md).
Не добавляй `type/title/instance` как обязательные поля без contract change.
Чистый domain не импортирует Nest HTTP exceptions. Существующий application
service может бросить подходящий `HttpException` с public code; не нужен новый
error framework/registry. Если domain outcome требует HTTP interpretation,
маппинг принадлежит application/transport boundary.
## Codes и безопасность
- Используй existing code владельца, не сравнивай внутренние сообщения.
Новое значение имеет один symbolic owner; metadata/validation и consumers
согласуются по [domain values](../../../.ai/conventions/code-health.md#domain-values).
- Общий envelope, default status mapping и unexpected errors принадлежат
[HttpExceptionFilter](../../src/common/filters/http-exception.filter.ts).
Не дублируй `res.status().json()` в controllers/services.
- Неожиданный exception → `500 / INTERNAL_SERVER_ERROR`, без message/stack,
SQL, credentials или raw upstream payload в ответе.
- External auth failure и временная недоступность имеют разные outcomes;
translation выполняется на existing integration/access boundary.
- Логи используют [observability](backend-observability.md). Redaction не
делает произвольное сообщение ошибки безопасным.
## Проверка
Для изменённого кода проверь observable status/code и сохранность данных.
Для validation нужен request через `configureApp`: fabricated `ValidationError`
в unit test filter не доказывает форму, выходящую из реального ValidationPipe.
OpenAPI parity проверяет schema, а не response pipeline; [Testing](backend-testing.md).
+13 -4
View File
@@ -35,10 +35,9 @@ ValidationPipe уже настроен в `configureApp` (`transform`, `whitelis
описывает те же required/optional/nullable/enum/range значения. описывает те же required/optional/nullable/enum/range значения.
Используй типизированные response DTO, `ApiOkResponse`/другой фактический status Используй типизированные response DTO, `ApiOkResponse`/другой фактический status
и существующий `ApiProblemResponses`. При новых ошибках задай публичный stable и существующий `ApiProblemResponses`. Prisma model не является public contract:
`code`; сообщение внутреннего exception не является UI contract. Не возвращай response выбирает поля явно. Stable codes, RFC 9457 профиль и mapping принадлежат
raw upstream error, Prisma record с secrets или stack. Текущая форма ошибок [Errors](backend-error-handling.md).
описана у HTTP owner; не добавляй поля RFC только потому, что они встречаются в tutorial.
**Безопасный путь:** используй exception и общий filter, а не локальный **Безопасный путь:** используй exception и общий filter, а не локальный
`res.status(...).json(...)`. Исключение — транспортная операция вроде cookie; `res.status(...).json(...)`. Исключение — транспортная операция вроде cookie;
@@ -46,6 +45,16 @@ raw upstream error, Prisma record с secrets или stack. Текущая фор
## Пример и проверка ## Пример и проверка
Prefix `/api/v1` задаёт `configureApp`. Route/status/request/response/OpenAPI
change — изменение публичного backend contract; несовместимое изменение требует
согласованного перехода consumers, а не незаметной правки DTO. Новый API version
не вводи для внутреннего refactor.
Pagination сохраняет contract endpoint: operations использует opaque cursor,
limit и DTO bounds. Не заменяй cursor offset/page и не добавляй global pagination
helper ради первого endpoint. Новая pagination явно определяет порядок,
границу страницы и end-of-list; reused cursor не должен менять filters/owner.
[Operations controller](../../src/modules/investments/operations/presentation/brokerage-operations.controller.ts) [Operations controller](../../src/modules/investments/operations/presentation/brokerage-operations.controller.ts)
показывает guard, user.sub, DTO query и service delegation. показывает guard, user.sub, DTO query и service delegation.
Для нового field обновляй canonical source и generated artifacts в определённом Для нового field обновляй canonical source и generated artifacts в определённом
+6 -1
View File
@@ -2,7 +2,8 @@
## Выбор boundary ## Выбор boundary
T-Bank gRPC принадлежит TbankClient; MOEX metadata — MoexClient. Сервисы use case T-Bank gRPC принадлежит TbankClient; MOEX metadata — MoexClient; NCR ratings —
NcrRatingsClient. Сервисы use case
используют объявленные клиентом records; SDK clients, metadata и transport errors используют объявленные клиентом records; SDK clients, metadata и transport errors
остаются внутри integration. Frontend не вызывает остаются внутри integration. Frontend не вызывает
T-Bank и не получает credentials/proto client. Reference source — T-Bank и не получает credentials/proto client. Reference source —
@@ -40,6 +41,10 @@ Existing client уже владеет bounded FIFO queue, rate limit, deadline
CA mechanism. Точные параметры и ограничения принадлежат CA mechanism. Точные параметры и ограничения принадлежат
[connection configuration](../../../docs/integrations/tbank.md#конфигурация-и-ограничения). [connection configuration](../../../docs/integrations/tbank.md#конфигурация-и-ограничения).
Эта queue/retry policy относится к T-Bank. MOEX/NCR HTTP clients используют
собственный bounded timeout и безопасный unavailable outcome; не приписывай им
gRPC retry policy. При изменении upstream сначала прочитай конкретный client.
При query/cache change установи key, user/account/generation ownership, При query/cache change установи key, user/account/generation ownership,
freshness и поведение late response. Старый результат не может обслужить новую freshness и поведение late response. Старый результат не может обслужить новую
connection generation. Client Cached Snapshot frontend отличается от backend connection generation. Client Cached Snapshot frontend отличается от backend
+24
View File
@@ -0,0 +1,24 @@
# Observability
- Единственный logger — nestjs-pino. Application events используют
[StructuredLogger](../../src/common/logging/structured-logger.service.ts),
стабильное имя события и разрешённые scalar fields. `console.log` и второй
logger в production не используются.
- `info` — значимый lifecycle/outcome; `debug` — подробности успешного RPC;
`warn` — восстановимая проблема/retry; `error` — окончательный сбой.
Не добавляй log на каждый вход/выход функции или дубликат HTTP completion.
- RequestContextService и request middleware владеют correlation: серверный
request ID возвращается как `X-Request-ID`, входящий client ID хранится отдельно.
Передавай существующий context, не создавай собственный UUID в каждом service.
- Wrapper ограничивает поля, Pino redacts request secrets. Не логируй passwords,
tokens, cookie, Authorization, credentials, connection generation, raw body
или upstream error. Secret в `message/stack` не становится безопасным из-за
allowlist; правила [Security](security-auth.md) обязательны для sensitive data.
- Для ошибки используй public code и безопасный context операции/попыток;
[error mapping](backend-error-handling.md) не отдаёт внутренние детали клиенту.
Причины — [ADR 008](../../../docs/adr/008-pino-backend-logging.md), системное
поведение — [HTTP observability](../../../docs/architecture/http-api.md#наблюдаемость).
Проверяй новые поля/redaction/correlation existing logging tests; не добавляй
тест на каждую строку log. Existing ESLint не запрещает все вызовы `console`
отдельным правилом: до настройки встроенного `no-console` это semantic review.
+6 -2
View File
@@ -1,5 +1,9 @@
# Backend testing # Backend testing
Перед нетривиальными тестами сформулируй
[общий Test Intent](../../../.ai/conventions/testing.md); отдельный документ
не требуется. Trivial low-risk изменения обходятся без него.
## Выбор уровня ## Выбор уровня
Проверяй риски доступа, денежных расчётов, целостности данных, конкурентных Проверяй риски доступа, денежных расчётов, целостности данных, конкурентных
@@ -40,8 +44,8 @@ Unit specs colocated в `src/**/*.spec.ts`; HTTP/integration — `test/`.
`npm run test:e2e` запускает все HTTP suites через изолированный SQLite runner; `npm run test:e2e` запускает все HTTP suites через изолированный SQLite runner;
`npm run test:sqlite` — только auth/cards. Каждая suite получает новую временную `npm run test:sqlite` — только auth/cards. Каждая suite получает новую временную
базу со штатными migrations. Прямой Jest без runner отклоняется до DB-запросов. базу со штатными migrations. Прямой Jest без runner отклоняется до DB-запросов.
`prisma.config.ts` исключает влияние унаследованного `RUST_LOG=warn/error`, `prisma.config.ts` исключает влияние унаследованного `RUST_LOG=warn/error`
вызывающего ошибку fresh migration на Prisma 7.9.1. Подробности и регрессия запуска — на fresh migration; точная Prisma version принадлежит manifest/lockfile. Подробности и регрессия запуска —
[Backend README](../../README.md#реальная-sqlite-и-сохранность-данных). [Backend README](../../README.md#реальная-sqlite-и-сохранность-данных).
HTTP suites требуют разрешения открывать локальные порты. Ошибка sandbox HTTP suites требуют разрешения открывать локальные порты. Ошибка sandbox
не считается успешным тестом; повторите проверку в подходящем окружении. не считается успешным тестом; повторите проверку в подходящем окружении.
+9
View File
@@ -24,6 +24,10 @@ frontend session/cache очищается и при network failure.
## Trust boundaries ## Trust boundaries
Frontend не является security boundary. DTO runtime validation выполняется
server-side; whitelist не заменяет access checks. Environment/secret inputs
проверяет existing config owner; реальные credentials не попадают в fixtures.
Verified JWT не доказывает доступ к accountId из URL. Используй user.sub и Verified JWT не доказывает доступ к accountId из URL. Используй user.sub и
existing account access policy; не доверяй userId/token из клиента как authority. existing account access policy; не доверяй userId/token из клиента как authority.
Cross-user, invalid/expired/superseded token и concurrent requests — обязательные Cross-user, invalid/expired/superseded token и concurrent requests — обязательные
@@ -39,6 +43,11 @@ cache invalidation при замене/deletion и ограничение key ro
обрабатывает существующий bcrypt flow. Не копируй требования о нормализации обрабатывает существующий bcrypt flow. Не копируй требования о нормализации
или длине из tutorial: сверяй runtime DTO, tests и feature source. или длине из tutorial: сверяй runtime DTO, tests и feature source.
Предпочитай Prisma query API. Raw SQL допускается только по конкретной нужде
с параметризованными значениями; не конкатенируй untrusted input и не передавай
его в `queryRawUnsafe`/`executeRawUnsafe`. Dynamic identifiers выбирай из
закрытого server-owned набора. Проверка user ownership сохраняется и в SQL.
## Безопасный путь и проверка ## Безопасный путь и проверка
При неясной ownership/crypto/session семантике прочитай exact source до edits. При неясной ownership/crypto/session семантике прочитай exact source до edits.
+24 -27
View File
@@ -1,30 +1,27 @@
# Backend # Backend router
NestJS API; общие правила работы — в root `AGENTS.md`. Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи,
До backend-решения прочитай [router](.ai/CONVENTIONS.md) и путей и обнаруженных boundaries; читай только нужные темы до решения.
[sources](.ai/SOURCES.md), затем только применимые документы/разделы. [Sources](.ai/SOURCES.md) выбирает факты/spec/ADR по возникшему вопросу.
## Проверка | Решение / сигнал | Читать |
| --- | --- |
| Первое знакомство, data flow, общая архитектура | [Mental model](.ai/architecture.md) |
| Module/layer/public API/DI/suffix, owner, перенос responsibility | [Modules/application](.ai/conventions/backend-architecture.md) |
| TS vocabulary, domain literals, magic values | [Types/values](.ai/conventions/backend-architecture.md#domain-literals-and-magic-values) |
| Controller/DTO/validation, route/status/response, OpenAPI | [HTTP API](.ai/conventions/backend-http-api.md) |
| Application/domain/HTTP error, exception, code | [Errors](.ai/conventions/backend-error-handling.md) |
| Repository/Prisma/query, transaction, migration, atomic write | [Data](.ai/conventions/backend-data.md) |
| Tests или behavior с regression risk | [Testing](.ai/conventions/backend-testing.md) |
| JWT/cookie/session/guard/access, encryption, secrets | [Security](.ai/conventions/security-auth.md) |
| External client/gateway/SDK/proto, retry/rate limit/cache | [Integrations](.ai/conventions/backend-integrations.md) |
| Logging/context/levels, sensitive logs | [Observability](.ai/conventions/backend-observability.md); sensitive data также Security |
| Изменение публичного wire contract / Swagger metadata | HTTP API + [HTTP contract source](../docs/architecture/http-api.md#источник-контракта) |
| Helper/dependency/новый pattern, refactor/scope | [Code health](../.ai/conventions/code-health.md), для пакета — [Dependencies](../.ai/conventions/dependencies.md) |
| Выбор gate, tooling, завершение | [Guardrails](../.ai/conventions/guardrails.md) |
Перед Node-командами: `nvm use --silent` по `.nvmrc`. Новый endpoint обычно требует HTTP API + Testing; Data/Security добавляются
После lockfile install `npm ci` полный gate — `npm run verify`: по persistence/access trigger. Auth refresh активирует Security + Data + Testing.
zero-warning ESLint, application/test TypeScript, Jest и build. Повтори routing по итоговому diff, включая новые файлы. Migration backlog
`lint:fix`/`format` пишут файлы и запускаются отдельно от доказательства проверки. читается только при соответствующем finding через
`test:cov` — диагностика без общего threshold; scripts принадлежат `package.json`. [plan](../.ai/migrations/conventions-v1/plan.md).
HTTP contract changes сохраняют [направление контракта](../docs/architecture/http-api.md#источник-контракта)
и проходят contract check. Общий e2e запускается на отдельных временных DB;
применимость и маршрут проверки
описаны в [backend testing](.ai/conventions/backend-testing.md).
Generated Prisma client не редактируется вручную. Credentials, авторизация
доступа к счетам и внешние интеграции остаются server-side.
ESLint/TypeScript не доказывают правильность domain boundaries, security или
денежных вычислений: для них нужны semantic review и risk-based tests.
## Ветки и релизы
Работа — в отдельной ветке от `develop`, затем PR в `develop`; в `main` напрямую
не пушить. Release/hotfix и синхронизация веток — по [root README](../README.md#релизы-и-ветки).
Commits и заголовки PR — Conventional Commits; для спецификаций допустим `spec:`.
Перед подготовкой релиза прочитай root README; версии вручную не повышать.