docs: refine backend conventions and routing #18
@@ -1,25 +0,0 @@
|
|||||||
# Backend router
|
|
||||||
|
|
||||||
Объединяй triggers из смысла задачи, путей и обнаруженных boundaries.
|
|
||||||
Читай выбранные guides до решения; якорь допускает чтение только нужного раздела.
|
|
||||||
Просто backend-файл не требует всех conventions.
|
|
||||||
|
|
||||||
| Решение / сигнал | Читать |
|
|
||||||
| --- | --- |
|
|
||||||
| Новый файл/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,19 +0,0 @@
|
|||||||
# Источники backend
|
|
||||||
|
|
||||||
| Что нужно установить | Владелец |
|
|
||||||
| --- | --- |
|
|
||||||
| Implementation decision и безопасный pattern | [CONVENTIONS](CONVENTIONS.md) |
|
|
||||||
| 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) |
|
|
||||||
| Prisma schema/data ownership и migration context | [docs: Data](../../docs/architecture/containers.md#хранимые-данные-и-границы-доступа) |
|
|
||||||
| T-Bank/gRPC/cache, logging | [T-Bank](../../docs/integrations/tbank.md), [свежесть данных](../../docs/architecture/cross-cutting/data-freshness.md), [наблюдаемость](../../docs/architecture/http-api.md#наблюдаемость) |
|
|
||||||
| Money, account/position/income/cache meanings | [docs: Domain](../../docs/glossary.md) |
|
|
||||||
| Поведение конкретной feature | [docs: Features](../../docs/README.md#предметные-области) |
|
|
||||||
| Существующие behavioral requirements при необходимости | [specs index](../../specs/AI_INDEX.md) |
|
|
||||||
| Versions/scripts и scope автоматических checks | package.json/lockfile, ESLint/TypeScript/Jest/Prisma configs |
|
|
||||||
|
|
||||||
SOURCES — карта, не требование прочитать все ссылки. Code показывает реализацию,
|
|
||||||
ADR — rationale, docs — текущие факты; старый пример не отменяет контракт.
|
|
||||||
Исследование предлагает варианты, а actual source разрешает выбор. При missing
|
|
||||||
или conflicting security/data facts сначала установи источник. Для обычной
|
|
||||||
работы OpenSpec/SDD не требуется.
|
|
||||||
@@ -1,12 +1,53 @@
|
|||||||
# Backend architecture
|
# Backend architecture
|
||||||
|
|
||||||
|
## Устройство и data flow
|
||||||
|
|
||||||
|
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 на каждый
|
||||||
|
класс нет.
|
||||||
|
|
||||||
|
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/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 выбираются через [Источники](../AGENTS.md#источники).
|
||||||
|
|
||||||
## Module-first organization
|
## Module-first organization
|
||||||
|
|
||||||
Backend — domain-oriented modular monolith с лёгким разделением
|
Сначала выбери business owner в `modules/`, затем ответственность внутри него.
|
||||||
по ответственности. `src/modules` группирует код по бизнес-возможности: `auth`, `banking/bank-cards`, области `investments`.
|
|
||||||
В инвестициях отдельные Nest modules владеют `brokerage-accounts`, `portfolio`,
|
|
||||||
`operations`, `income`, `upcoming-events`, `instruments`, `tbank-connection`.
|
|
||||||
`instruments/stocks` и `instruments/bonds` — slices внутри одного модуля.
|
|
||||||
|
|
||||||
### Progressive structure
|
### Progressive structure
|
||||||
|
|
||||||
@@ -36,15 +77,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 проходи владельцев в порядке:
|
||||||
@@ -116,7 +148,7 @@ Cross-module consumers во всех строках ограничены пуб
|
|||||||
`*.decorator.ts`, `*.config.ts`, `*.validation.ts`) сохраняют соответствующие
|
`*.decorator.ts`, `*.config.ts`, `*.validation.ts`) сохраняют соответствующие
|
||||||
существующие роли в common/config. `tbank.crypto.ts` — локальная реализация
|
существующие роли в common/config. `tbank.crypto.ts` — локальная реализация
|
||||||
шифрования токена; имя не является разрешением создавать generic crypto layer.
|
шифрования токена; имя не является разрешением создавать generic crypto layer.
|
||||||
HTTP/e2e naming и расположение — в [Testing](backend-testing.md).
|
HTTP/e2e naming и расположение — в [Testing](conventions/testing.md).
|
||||||
|
|
||||||
Без зафиксированной необходимости не вводи `*.helper.ts`, `*.utils.ts`,
|
Без зафиксированной необходимости не вводи `*.helper.ts`, `*.utils.ts`,
|
||||||
`*.manager.ts`, `*.processor.ts`, `*.handler.ts`, `*.provider.ts`, `*.factory.ts`.
|
`*.manager.ts`, `*.processor.ts`, `*.handler.ts`, `*.provider.ts`, `*.factory.ts`.
|
||||||
@@ -134,6 +166,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.
|
||||||
@@ -162,37 +198,25 @@ Tests могут импортировать внутренний owner для п
|
|||||||
может использовать application types/input и domain rules; новые interfaces
|
может использовать application types/input и domain rules; новые interfaces
|
||||||
ради инверсии зависимостей не обязательны. Domain не импортирует presentation или
|
ради инверсии зависимостей не обязательны. Domain не импортирует presentation или
|
||||||
infrastructure. Существующие связные CRUD/transaction services могут сохранять прямые queries
|
infrastructure. Существующие связные CRUD/transaction services могут сохранять прямые queries
|
||||||
по [Data](backend-data.md); use case получает данные через repository и сам
|
по [Data](conventions/data.md); use case получает данные через repository и сам
|
||||||
Prisma queries не выполняет. Entity/interface на каждую таблицу не требуется.
|
Prisma queries не выполняет. Entity/interface на каждую таблицу не требуется.
|
||||||
DTO остаются транспортными runtime classes; существующее использование DTO
|
DTO остаются транспортными runtime classes; существующее использование DTO
|
||||||
на application boundary допустимо по [HTTP API](backend-http-api.md).
|
на application boundary допустимо по [HTTP API](conventions/http-api.md).
|
||||||
|
|
||||||
Сохраняй существующий error contract: HTTP exceptions остаются на application/
|
Сохраняй существующий error contract: HTTP exceptions остаются на application/
|
||||||
transport boundary. Чистому расчёту не нужен новый framework ошибок.
|
transport boundary. Чистому расчёту не нужен новый framework ошибок.
|
||||||
Точные ограничения — [HTTP](backend-http-api.md), [Data](backend-data.md),
|
Mapping и stable codes — по [Errors](conventions/error-handling.md).
|
||||||
[Security](security-auth.md), [Integrations](backend-integrations.md).
|
Точные ограничения — [HTTP](conventions/http-api.md), [Data](conventions/data.md),
|
||||||
|
[Security](conventions/security-auth.md), [Integrations](conventions/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 +229,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 +252,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](conventions/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.
|
|
||||||
|
|
||||||
## Проверка
|
## Проверка
|
||||||
|
|
||||||
@@ -15,13 +15,20 @@ PrismaService — существующий shared DB provider с lifecycle пр
|
|||||||
| Независимые reads | Не добавляй transaction ради внешнего вида |
|
| Независимые reads | Не добавляй transaction ради внешнего вида |
|
||||||
| Общая persistence abstraction | Только при реальной общей ответственности; generic repository для одной Prisma реализации не нужен |
|
| Общая persistence abstraction | Только при реальной общей ответственности; generic repository для одной Prisma реализации не нужен |
|
||||||
|
|
||||||
Размещение repository определяется [Architecture](backend-architecture.md):
|
Размещение repository определяется [Architecture](../architecture.md):
|
||||||
в слоистом module это `infrastructure/`.
|
в слоистом module это `infrastructure/`.
|
||||||
Repository — конкретный класс `*.repository.ts` у business owner, без обязательных
|
Repository — конкретный класс `*.repository.ts` у business owner, без обязательных
|
||||||
interface/port/adapter. Он владеет queries, includes/filtering, DB mapping и
|
interface/port/adapter. Он владеет queries, includes/filtering, DB mapping и
|
||||||
согласованным чтением. Суффиксы `*.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
|
||||||
@@ -74,4 +81,4 @@ client обновляй из schema. Не переписывай уже прим
|
|||||||
`npx prisma validate`; после schema change `npx prisma generate` и
|
`npx prisma validate`; после schema change `npx prisma generate` и
|
||||||
`npm run typecheck:app`. Invariants/atomicity/migration safety требуют tests
|
`npm run typecheck:app`. Invariants/atomicity/migration safety требуют tests
|
||||||
на соответствующей boundary: mock Prisma не доказывает реальную transaction.
|
на соответствующей boundary: mock Prisma не доказывает реальную transaction.
|
||||||
Выбор integration/e2e и известные ограничения — в [Testing](backend-testing.md).
|
Выбор integration/e2e и известные ограничения — в [Testing](testing.md).
|
||||||
@@ -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](observability.md). Redaction не
|
||||||
|
делает произвольное сообщение ошибки безопасным.
|
||||||
|
|
||||||
|
## Проверка
|
||||||
|
|
||||||
|
Для изменённого кода проверь observable status/code и сохранность данных.
|
||||||
|
Для validation нужен request через `configureApp`: fabricated `ValidationError`
|
||||||
|
в unit test filter не доказывает форму, выходящую из реального ValidationPipe.
|
||||||
|
OpenAPI parity проверяет schema, а не response pipeline; [Testing](testing.md).
|
||||||
@@ -27,7 +27,7 @@ module или presentation. DTO можно передать application boundary
|
|||||||
формы; отдельный Input с теми же полями не нужен. Domain не импортирует DTO:
|
формы; отдельный Input с теми же полями не нужен. Domain не импортирует DTO:
|
||||||
например, чистые денежные расчёты используют `Money`, а Swagger — `MoneyDto`.
|
например, чистые денежные расчёты используют `Money`, а Swagger — `MoneyDto`.
|
||||||
Нормализованная форма input нужна только при смысловом отличии от wire contract;
|
Нормализованная форма input нужна только при смысловом отличии от wire contract;
|
||||||
выбор suffix принадлежит [Architecture](backend-architecture.md).
|
выбор suffix принадлежит [Architecture](../architecture.md).
|
||||||
ValidationPipe уже настроен в `configureApp` (`transform`, `whitelist`).
|
ValidationPipe уже настроен в `configureApp` (`transform`, `whitelist`).
|
||||||
Не обещай rejection неизвестных fields: `forbidNonWhitelisted` не включён.
|
Не обещай rejection неизвестных fields: `forbidNonWhitelisted` не включён.
|
||||||
Для query numbers/arrays выбирай явное преобразование и валидаторы, как в
|
Для query numbers/arrays выбирай явное преобразование и валидаторы, как в
|
||||||
@@ -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](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 в определённом
|
||||||
@@ -55,4 +64,4 @@ raw upstream error, Prisma record с secrets или stack. Текущая фор
|
|||||||
errors на boundary, где они возникают. Unit вызов controller не запускает pipes,
|
errors на boundary, где они возникают. Unit вызов controller не запускает pipes,
|
||||||
guards или serialization. `npm run test:openapi-contract` проверяет schema parity,
|
guards или serialization. `npm run test:openapi-contract` проверяет schema parity,
|
||||||
но не поведение invalid request; выбирай соответствующий test по
|
но не поведение invalid request; выбирай соответствующий test по
|
||||||
[Testing](backend-testing.md). Финальный gate не заменяет semantic review ошибок.
|
[Testing](testing.md). Финальный gate не заменяет semantic review ошибок.
|
||||||
@@ -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 —
|
||||||
@@ -17,7 +18,7 @@ RPC, credentials/access и resilience. Эти владельцы не объед
|
|||||||
`*.client.ts` содержит transport и его retries; `*.api.ts` только перечисляет
|
`*.client.ts` содержит transport и его retries; `*.api.ts` только перечисляет
|
||||||
публичные exports. Все шесть `auth/operations/portfolio/tbank/moex/ncr.api.ts`
|
публичные exports. Все шесть `auth/operations/portfolio/tbank/moex/ncr.api.ts`
|
||||||
имеют одинаковую роль программной границы, определённую в
|
имеют одинаковую роль программной границы, определённую в
|
||||||
[Architecture](backend-architecture.md). Логика, DTO definitions и HTTP routes
|
[Architecture](../architecture.md). Логика, DTO definitions и HTTP routes
|
||||||
в эти файлы не помещаются. Type exports обозначаются `export type`.
|
в эти файлы не помещаются. Type exports обозначаются `export type`.
|
||||||
|
|
||||||
Преобразование provider records происходит на boundary: например,
|
Преобразование provider records происходит на boundary: например,
|
||||||
@@ -30,7 +31,7 @@ client или mapper для простого копирования полей.
|
|||||||
Для нового account use case сначала используй TbankAccessService: access,
|
Для нового account use case сначала используй TbankAccessService: access,
|
||||||
connection generation и invalid-token translation не дублируются в callers.
|
connection generation и invalid-token translation не дублируются в callers.
|
||||||
Mapper внешних финансовых значений остаётся внутри business capability,
|
Mapper внешних финансовых значений остаётся внутри business capability,
|
||||||
обычно в infrastructure по [Architecture](backend-architecture.md).
|
обычно в infrastructure по [Architecture](../architecture.md).
|
||||||
|
|
||||||
## Retry, cache и protocol
|
## Retry, cache и protocol
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -64,4 +69,4 @@ Generated artifacts, если есть, создавай из source, без р
|
|||||||
upstream responses. Проверь queue/deadline/retry limit, token translation,
|
upstream responses. Проверь queue/deadline/retry limit, token translation,
|
||||||
cache isolation и late response для затронутых ветвей. Fixtures синтетические;
|
cache isolation и late response для затронутых ветвей. Fixtures синтетические;
|
||||||
реальная сеть, секреты и пользовательские accounts не нужны.
|
реальная сеть, секреты и пользовательские accounts не нужны.
|
||||||
Logging использует [existing logger](backend-architecture.md#logging).
|
Logging использует [existing logger](../architecture.md#logging).
|
||||||
@@ -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](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.
|
||||||
@@ -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.
|
||||||
@@ -46,6 +55,6 @@ cache invalidation при замене/deletion и ограничение key ro
|
|||||||
фиктивным success или log. Logger allowlist/redaction не делает произвольный
|
фиктивным success или log. Logger allowlist/redaction не делает произвольный
|
||||||
message безопасным.
|
message безопасным.
|
||||||
|
|
||||||
Targeted auth/access/crypto/logging tests + [Testing](backend-testing.md).
|
Targeted auth/access/crypto/logging tests + [Testing](testing.md).
|
||||||
Cookie/guard/HTTP changes требуют configured HTTP proof; frontend Bearer/retry
|
Cookie/guard/HTTP changes требуют configured HTTP proof; frontend Bearer/retry
|
||||||
проверяется MSW. Schema parity не доказывает отсутствие утечки secret.
|
проверяется MSW. Schema parity не доказывает отсутствие утечки secret.
|
||||||
|
|||||||
@@ -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
|
||||||
не считается успешным тестом; повторите проверку в подходящем окружении.
|
не считается успешным тестом; повторите проверку в подходящем окружении.
|
||||||
@@ -1,30 +1,42 @@
|
|||||||
# Backend
|
# Backend router
|
||||||
|
|
||||||
NestJS API; общие правила работы — в root `AGENTS.md`.
|
Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи,
|
||||||
До backend-решения прочитай [router](.ai/CONVENTIONS.md) и
|
путей и обнаруженных boundaries; читай только нужные темы до решения.
|
||||||
[sources](.ai/SOURCES.md), затем только применимые документы/разделы.
|
Системные факты/spec/ADR выбирай в разделе [Источники](#источники) по вопросу.
|
||||||
|
|
||||||
## Проверка
|
| Решение / сигнал | Читать |
|
||||||
|
| --- | --- |
|
||||||
|
| Устройство/data flow, module/layer/public API/DI/suffix, owner | [Architecture](.ai/architecture.md) |
|
||||||
|
| TS vocabulary, domain literals, magic values | [Types/values](.ai/architecture.md#domain-literals-and-magic-values) |
|
||||||
|
| Controller/DTO/validation, route/status/response, OpenAPI | [HTTP API](.ai/conventions/http-api.md) |
|
||||||
|
| Application/domain/HTTP error, exception, code | [Errors](.ai/conventions/error-handling.md) |
|
||||||
|
| Repository/Prisma/query, transaction, migration, atomic write | [Data](.ai/conventions/data.md) |
|
||||||
|
| Tests или behavior с regression risk | [Testing](.ai/conventions/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/integrations.md) |
|
||||||
|
| Logging/context/levels, sensitive logs | [Observability](.ai/conventions/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 или
|
Открывай только источник по возникшему вопросу. Code показывает реализацию,
|
||||||
денежных вычислений: для них нужны semantic review и risk-based tests.
|
docs — текущую систему, ADR — rationale, specs — requirements. Старый пример
|
||||||
|
не отменяет контракт; conflicting security/data facts выясняй до решения.
|
||||||
|
|
||||||
## Ветки и релизы
|
| Вопрос | Источник |
|
||||||
|
| --- | --- |
|
||||||
Работа — в отдельной ветке от `develop`, затем PR в `develop`; в `main` напрямую
|
| HTTP errors/DTO/wire shape | [HTTP API](../docs/architecture/http-api.md); editable OpenAPI генерирует Nest |
|
||||||
не пушить. Release/hotfix и синхронизация веток — по [root README](../README.md#релизы-и-ветки).
|
| Auth/session, account access, secrets | [Authentication](../docs/domains/authentication/overview.md), [T-Bank](../docs/integrations/tbank.md) |
|
||||||
Commits и заголовки PR — Conventional Commits; для спецификаций допустим `spec:`.
|
| Prisma/data ownership, хранение и доступ | [Containers](../docs/architecture/containers.md#хранимые-данные-и-границы-доступа) |
|
||||||
Перед подготовкой релиза прочитай root README; версии вручную не повышать.
|
| T-Bank/cache/logging | [T-Bank](../docs/integrations/tbank.md), [Freshness](../docs/architecture/cross-cutting/data-freshness.md), [Observability](../docs/architecture/http-api.md#наблюдаемость) |
|
||||||
|
| Money, account/position/income/cache meanings | [Глоссарий](../docs/glossary.md) |
|
||||||
|
| Поведение области / требования | [Области docs](../docs/README.md#предметные-области), [Specs index](../specs/AI_INDEX.md) |
|
||||||
|
| Versions/scripts/check scope | Manifest/lockfile, ESLint/TypeScript/Jest/Prisma configs |
|
||||||
|
|||||||
@@ -78,10 +78,10 @@ runtime-валидацию. В обычном коде нужны инициал
|
|||||||
|
|
||||||
## Документация
|
## Документация
|
||||||
|
|
||||||
- [Router conventions](.ai/CONVENTIONS.md) и [источники](.ai/SOURCES.md)
|
- [AGENTS: правила и источники](AGENTS.md)
|
||||||
- [Backend architecture](.ai/conventions/backend-architecture.md)
|
- [Backend architecture](.ai/architecture.md)
|
||||||
- [HTTP API](.ai/conventions/backend-http-api.md), [data](.ai/conventions/backend-data.md) и [testing](.ai/conventions/backend-testing.md)
|
- [HTTP API](.ai/conventions/http-api.md), [data](.ai/conventions/data.md) и [testing](.ai/conventions/testing.md)
|
||||||
- [Security/auth](.ai/conventions/security-auth.md) и [integrations](.ai/conventions/backend-integrations.md)
|
- [Security/auth](.ai/conventions/security-auth.md) и [integrations](.ai/conventions/integrations.md)
|
||||||
|
|
||||||
- [Карта проектных фактов](../docs/README.md)
|
- [Карта проектных фактов](../docs/README.md)
|
||||||
- [API contract и runtime OpenAPI](../docs/architecture/http-api.md#источник-контракта)
|
- [API contract и runtime OpenAPI](../docs/architecture/http-api.md#источник-контракта)
|
||||||
|
|||||||
Reference in New Issue
Block a user