docs: simplify conventions layout
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 2m3s
Conventional commits / commits (pull_request) Successful in 24s
Backend security / security (push) Successful in 7s
Backend verify / verify (push) Successful in 2m10s
Backend security / security (pull_request) Successful in 7s
Backend verify / verify (pull_request) Successful in 2m3s
Conventional commits / commits (pull_request) Successful in 24s
Backend security / security (push) Successful in 7s
Backend verify / verify (push) Successful in 2m10s
This commit was merged in pull request #18.
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
# Backend conventions: вход
|
||||
|
||||
Актуальный routing по задаче, пути и boundary находится в [AGENTS](../AGENTS.md).
|
||||
Выбирай только активированные темы; при новой границе повтори routing.
|
||||
[Architecture](architecture.md) объясняет mental model, [SOURCES](SOURCES.md)
|
||||
ведёт к системным фактам, specifications и ADR по конкретному вопросу.
|
||||
|
||||
Этот адрес сохранён для существующих входящих ссылок; второй копии правил
|
||||
и таблицы маршрутов здесь нет.
|
||||
@@ -1,19 +0,0 @@
|
||||
# Источники backend
|
||||
|
||||
| Что нужно установить | Владелец |
|
||||
| --- | --- |
|
||||
| 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 |
|
||||
| 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 не требуется.
|
||||
+222
-4
@@ -1,4 +1,6 @@
|
||||
# Backend mental model
|
||||
# Backend architecture
|
||||
|
||||
## Устройство и data flow
|
||||
|
||||
NestJS + TypeScript modular monolith. Composition root `AppModule` подключает
|
||||
бизнес-модули, validated configuration, Prisma и Pino. Общая `configureApp`
|
||||
@@ -19,7 +21,7 @@ HTTP controller → application service/use case → pure domain rules
|
||||
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
|
||||
@@ -29,7 +31,7 @@ domain из-за extraction в функцию. Boundary mappers преобраз
|
||||
|
||||
PrismaService владеет DB lifecycle. Самостоятельные use cases получают данные
|
||||
через concrete repositories; существующие связные CRUD/transaction services
|
||||
сохраняют прямые queries по [data convention](conventions/backend-data.md).
|
||||
сохраняют прямые queries по [data convention](conventions/data.md).
|
||||
Money — currency + decimal string; расчёты используют точные общие helpers.
|
||||
|
||||
Auth управляет identity/session; TbankAccessService проверяет доступ к счёту и
|
||||
@@ -41,4 +43,220 @@ Runtime OpenAPI из Nest → предоставленный frontend snapshot
|
||||
Problem Details фильтруются общей HTTP boundary; StructuredLogger добавляет
|
||||
request context и ограничивает поля событий. Jest unit tests, изолированные
|
||||
SQLite/HTTP suites и OpenAPI parity доказывают разные части поведения.
|
||||
Системные потоки и ADR выбираются через [SOURCES](SOURCES.md).
|
||||
Системные потоки и ADR выбираются через [Источники](../AGENTS.md#источники).
|
||||
|
||||
## Module-first organization
|
||||
|
||||
Сначала выбери business owner в `modules/`, затем ответственность внутри него.
|
||||
|
||||
### Progressive structure
|
||||
|
||||
Три уровня организации: business module → responsibility layer → technical grouping
|
||||
при реальной необходимости. Компактный module с понятными controller/DTO/service
|
||||
может оставаться плоским. Ориентир 5–7 файлов не является порогом: учитывай число
|
||||
ответственностей, наличие domain/repository/use cases и удобство просмотра каталога.
|
||||
|
||||
Когда в module смешаны несколько services, transport, расчёты и persistence,
|
||||
группируй файлы по ответственности:
|
||||
|
||||
```text
|
||||
feature/
|
||||
├── feature.module.ts
|
||||
├── feature.api.ts # только при существующих внешних потребителях
|
||||
├── presentation/ # HTTP, DTO, guards
|
||||
├── application/ # сценарии, orchestration, application input
|
||||
├── domain/ # чистые бизнес-правила и расчёты
|
||||
└── infrastructure/ # persistence, framework и boundary mapping
|
||||
```
|
||||
|
||||
Создавай только нужные слои. В корне сложного module оставляй composition и
|
||||
публичную boundary; production code и colocated tests размещай в слое владельца.
|
||||
Внутри business slice действует то же правило, без отдельного Nest module ради папок.
|
||||
`controllers/`, `dto/`, `services/`, `use-cases/`, `persistence/`, `mappers/`
|
||||
допустимы только внутри слоя, когда множество однотипных файлов мешает навигации.
|
||||
Для одного controller или пары DTO дополнительная поддиректория не нужна.
|
||||
Пустые папки, новые barrels, CQRS и interfaces не требуются для этой организации.
|
||||
|
||||
## Поиск и создание файла
|
||||
|
||||
При работе над feature проходи владельцев в порядке:
|
||||
|
||||
1. Найди business module.
|
||||
2. Прочитай его `*.module.ts` (для slice — composition родительского module).
|
||||
3. Проверь `presentation/`: controller и transport contract.
|
||||
4. Проследи вызовы в `application/`: сценарии и services.
|
||||
5. Проверь используемые правила в `domain/`.
|
||||
6. Проверь `infrastructure/`: repository и технические реализации.
|
||||
7. По обнаруженным зависимостям переходи в `integrations/` и `common/`.
|
||||
|
||||
В плоском module классифицируй файлы по suffix и следуй их ответственности.
|
||||
Отсутствующий слой пропускай. Если business owner известен, начинай локально,
|
||||
а не с repository-wide search.
|
||||
|
||||
**Rule of locality:** code that belongs to one business capability stays inside
|
||||
that capability unless it is cross-cutting. Например, расчёты карт принадлежат
|
||||
bank-cards/domain, даже если реализованы обычными функциями.
|
||||
|
||||
Перед созданием файла определи:
|
||||
|
||||
1. Какой business module владеет изменением? Сначала owner, затем common при
|
||||
подтверждённой общей потребности.
|
||||
2. Какова ответственность: HTTP/DTO/guard → presentation; сценарий/orchestration →
|
||||
application; чистое business rule/calculation → domain; persistence или
|
||||
module-specific technical implementation → infrastructure; внешний протокол → integrations.
|
||||
3. Какой approved suffix из таблицы соответствует этой ответственности?
|
||||
4. Какой слой владеет файлом? В компактном плоском module сохрани размещение рядом
|
||||
с owner; в слоистом используй соответствующий слой.
|
||||
5. Достаточно ли colocate файл рядом с существующими? Level 3 grouping создавай
|
||||
только при заметном улучшении навигации по множеству однотипных файлов.
|
||||
|
||||
Если owner или роль не определены, сначала установи ответственность. Generic
|
||||
`utils.ts`, `helpers.ts`, `manager.ts`, `handler.ts` и arbitrary directory её не заменяют.
|
||||
|
||||
## Naming & File Suffix Convention
|
||||
|
||||
Имена файлов — `kebab-case`. File suffix определяет responsibility, directory
|
||||
определяет architectural layer. Размещение в таблице предпочтительно для
|
||||
слоистого module; компактный плоский module сохраняет файлы в корне.
|
||||
Cross-module consumers во всех строках ограничены публичной границей ниже.
|
||||
|
||||
| Suffix | Когда использовать | Где | Кто зависит | Когда не использовать |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `*.controller.ts` | HTTP endpoint / transport | presentation | Nest wiring | DB, расчёты, SDK, сложная оркестрация |
|
||||
| `*.dto.ts` | Request/response, validation, OpenAPI | presentation | Controller; application boundary при совпадении shape | Универсальная domain model |
|
||||
| `*.use-case.ts` | Один самостоятельный application scenario | application | Controller, другой orchestrator | Prisma queries, Request/Response, SDK implementation |
|
||||
| `*.service.ts` | Конкретная cohesive reusable capability | рядом с owner; application/domain/integration по роли | Сценарии и services; domain — только от чистого domain service | Свалка несвязанных сценариев |
|
||||
| `*.repository.ts` | Persistence queries и согласованные DB reads/writes | infrastructure | Application service/use case | Domain calculations, HTTP request, external transport |
|
||||
| `*.client.ts` | Низкоуровневый внешний HTTP/gRPC/SDK | integrations или infrastructure | Application orchestration, boundary mapper | Пользовательский lifecycle и domain rules |
|
||||
| `*.mapper.ts` | Meaningful conversion между моделями | обычно infrastructure у преобразуемой boundary | Код соответствующей boundary | Формальное копирование нескольких одинаковых полей |
|
||||
| `*.policy.ts` | Cohesive decision rule | domain/application/integration по входным данным | Владелец правила; domain — от чистой policy | Persistence, transport, generic strategy |
|
||||
| `*.guard.ts` | Nest guard | presentation | Nest/controller | Основная account authorization и финансовые правила |
|
||||
| `*.strategy.ts` | Framework strategy, например JWT | infrastructure | Framework wiring | Обычный service без strategy contract |
|
||||
| `*.module.ts` | Nest imports/providers/controllers/exports | module root | Composition root, importing module | Бизнес-логика |
|
||||
| `*.types.ts` | Малая группа связанных TS types | рядом с owner | Потребители модели на разрешённых слоях | Глобальные types всего module, runtime validation |
|
||||
| `*.api.ts` | Явный список публичных exports | root конкретного module/integration | Другие modules | DTO/client/service implementation или generic API logic |
|
||||
| `*-input.ts` | Application input: нормализация и проверка формы, отличающейся от wire DTO | application | Application code | Копия DTO под новым именем; вариант `*.input.ts` |
|
||||
| `*.calculations.ts`, `*.rules.ts` или предметное имя | Чистые бизнес-расчёты, value semantics | domain | Application и domain | Nest/Prisma/HTTP/SDK зависимости |
|
||||
| `*.spec.ts` | Существующая unit/component проверка поведения | рядом с owner/slice | Test runner | Архитектурные проверки папок и trivial wiring |
|
||||
|
||||
`bank-card-input.ts` использует выбранный `*-input.ts`: переводит Money в
|
||||
копейки, нормализует значения и проверяет application input. DTO не дублируется
|
||||
отдельным Input type. `rub-money.ts` — domain-правила RUB; `bond-money.mapper.ts`
|
||||
— преобразование ответа клиента. Эти роли не определяются словом money в имени.
|
||||
|
||||
Технические framework suffixes (`*.filter.ts`, `*.middleware.ts`,
|
||||
`*.decorator.ts`, `*.config.ts`, `*.validation.ts`) сохраняют соответствующие
|
||||
существующие роли в common/config. `tbank.crypto.ts` — локальная реализация
|
||||
шифрования токена; имя не является разрешением создавать generic crypto layer.
|
||||
HTTP/e2e naming и расположение — в [Testing](conventions/testing.md).
|
||||
|
||||
Без зафиксированной необходимости не вводи `*.helper.ts`, `*.utils.ts`,
|
||||
`*.manager.ts`, `*.processor.ts`, `*.handler.ts`, `*.provider.ts`, `*.factory.ts`.
|
||||
Новая naming convention сначала получает конкретную роль и обоснование здесь.
|
||||
Для data access выбран только `*.repository.ts`, не persistence/store/dao.
|
||||
|
||||
## Публичные границы
|
||||
|
||||
`*.module.ts` задаёт DI boundary; `*.api.ts` перечисляет используемые другими
|
||||
модулями providers, функции и типы. Внешний production import идёт через этот
|
||||
API или файл Nest module. Чистый `integrations/tbank/decimal.ts` — отдельный
|
||||
публичный вход для protobuf conversion: импорт арифметики не загружает DI/Prisma.
|
||||
Внутри модуля используй прямые локальные импорты.
|
||||
Tests могут импортировать внутренний owner для проверки поведения и mocks.
|
||||
Новый export требует текущего потребителя; controller и persistence остаются
|
||||
приватными. Не добавляй `index.ts` на каждом уровне.
|
||||
|
||||
Circular dependencies устраняй изменением ownership/composition. `forwardRef()`
|
||||
не является стандартным исправлением boundaries; исключение требует конкретной
|
||||
неизбежной связи и проверки DI initialization.
|
||||
|
||||
Каждый provider регистрируется у одного владельца. Потребители импортируют
|
||||
его module, а не повторяют provider: это сохраняет единственный кэш и лимитер.
|
||||
`AuthModule` экспортирует guard; наличие JWT не заменяет account access check.
|
||||
|
||||
- `income` читает операции через `operations.api.ts`.
|
||||
- `instruments` получает личные позиции через `portfolio.api.ts`.
|
||||
- `tbank-connection` использует T-Bank integration и публичный сброс портфеля.
|
||||
- `portfolio` и другие инвестиционные модули используют `TbankModule`, который
|
||||
не зависит от business modules. Так управление подключением не образует цикл.
|
||||
- `investment-calendar.ts` и `account-reference.dto.ts` разделяются только
|
||||
инвестиционными областями; это не глобальная библиотека бизнес-правил.
|
||||
|
||||
## Ответственность и направление зависимостей
|
||||
|
||||
| Владелец | Ответственность |
|
||||
| --- | --- |
|
||||
| Controller / presentation | Route, verified identity, transport DTO, validation, status/cookie; вызов сценария |
|
||||
| Use case / application service | Оркестрация, business validation, доступ и согласованность результата |
|
||||
| Domain | Чистые правила и расчёты; без NestJS, Prisma, HTTP/Swagger и внешних SDK |
|
||||
| Persistence / infrastructure | Запросы, транзакции, технические реализации границ |
|
||||
| `src/integrations/{tbank,moex,ncr}` | Внешние протоколы, декодирование, retry/deadline/cache; без зависимостей от HTTP DTO business modules |
|
||||
| `src/prisma` | Общий DB provider и lifecycle; не generic repository |
|
||||
|
||||
Основной вызов: presentation → application → domain; application использует
|
||||
конкретные infrastructure providers, зарегистрированные module. Infrastructure
|
||||
может использовать application types/input и domain rules; новые interfaces
|
||||
ради инверсии зависимостей не обязательны. Domain не импортирует presentation или
|
||||
infrastructure. Существующие связные CRUD/transaction services могут сохранять прямые queries
|
||||
по [Data](conventions/data.md); use case получает данные через repository и сам
|
||||
Prisma queries не выполняет. Entity/interface на каждую таблицу не требуется.
|
||||
DTO остаются транспортными runtime classes; существующее использование DTO
|
||||
на application boundary допустимо по [HTTP API](conventions/http-api.md).
|
||||
|
||||
Сохраняй существующий error contract: HTTP exceptions остаются на application/
|
||||
transport boundary. Чистому расчёту не нужен новый framework ошибок.
|
||||
Mapping и stable codes — по [Errors](conventions/error-handling.md).
|
||||
Точные ограничения — [HTTP](conventions/http-api.md), [Data](conventions/data.md),
|
||||
[Security](conventions/security-auth.md), [Integrations](conventions/integrations.md).
|
||||
|
||||
## Сценарии и размер сервисов
|
||||
|
||||
Разделяй независимые операции, а не строки. Пример: `BankCardsService` владеет
|
||||
картами, `BankOperationsService` — журналом/переводами, `GetBankSummaryUseCase` —
|
||||
сводкой через repository и чистый расчёт. Не разрывай денежную transaction,
|
||||
общий cache/limiter или pending promises при extraction.
|
||||
|
||||
Расширение abstractions — по [change scope](../../.ai/conventions/code-health.md#change-scope).
|
||||
|
||||
## Common
|
||||
|
||||
`common` содержит общий error filter, logging, API decorators, тип `Money`,
|
||||
его HTTP `MoneyDto` и точную decimal-арифметику, используемые несколькими областями. Код одного
|
||||
модуля принадлежит этому модулю: например, RUB/kopecks rules — банковским картам,
|
||||
protobuf units/nano conversion — T-Bank integration. Domain использует
|
||||
`money.types.ts`, а не Swagger DTO; это общая форма денег для карт и портфеля.
|
||||
Давай функциям предметные имена; не создавай глобальные `utils.ts`/`helpers.ts`.
|
||||
|
||||
## Domain literals and magic values
|
||||
|
||||
Общий выбор symbolic values и числовых policies —
|
||||
[Domain values](../../.ai/conventions/code-health.md#domain-values).
|
||||
Сохраняй strict TS и explicit boundary types. Для недоверенного input выбирай
|
||||
`unknown` + runtime narrowing; отключённый lint `no-explicit-any` не делает
|
||||
`any` нормой domain/application model. `declare` допустим для заполняемых извне
|
||||
DTO fields, не для сокрытия неинициализированного owned state.
|
||||
|
||||
OpenAPI enum metadata и runtime validation должны читать тот же vocabulary,
|
||||
что application/domain. Например, `BankOperationKind` принадлежит bank-cards/domain,
|
||||
а DTO получает допустимые значения через `Object.values(BankOperationKind)`.
|
||||
Frontend переиспользует generated OpenAPI types/values по возможностям генератора,
|
||||
а не определяет независимый backend-owned словарь. Внешние/generated enums
|
||||
сохраняй; generated Prisma/protocol code вручную не меняй.
|
||||
|
||||
`@typescript-eslint/no-magic-numbers` включён как error; базовое ESLint rule
|
||||
отключено в пользу TS-версии. Исключения для очевидных чисел и их bigint форм,
|
||||
индексов, default values, enum/type declarations и readonly properties заданы
|
||||
в ESLint config. Test fixtures/expected values остаются inline, остальные lint
|
||||
правила продолжают действовать. Numeric policy получает предметное имя и единицы;
|
||||
semantic magic strings проверяются conventions/review без нового инструмента.
|
||||
|
||||
## Logging
|
||||
|
||||
События, levels, correlation и safe fields — по
|
||||
[Observability](conventions/observability.md).
|
||||
|
||||
## Проверка
|
||||
|
||||
`npm run typecheck:app`, `npm run lint`, targeted Jest по изменённому use case.
|
||||
Смысл boundaries проверяется review: backend не имеет отдельного автоматического
|
||||
FSD/DI architecture checker. Existing tests и build не доказывают правильный owner.
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
# Backend architecture
|
||||
|
||||
## Module-first organization
|
||||
|
||||
Устройство и data flow — в [mental model](../architecture.md).
|
||||
Сначала выбери business owner в `modules/`, затем ответственность внутри него.
|
||||
|
||||
### Progressive structure
|
||||
|
||||
Три уровня организации: business module → responsibility layer → technical grouping
|
||||
при реальной необходимости. Компактный module с понятными controller/DTO/service
|
||||
может оставаться плоским. Ориентир 5–7 файлов не является порогом: учитывай число
|
||||
ответственностей, наличие domain/repository/use cases и удобство просмотра каталога.
|
||||
|
||||
Когда в module смешаны несколько services, transport, расчёты и persistence,
|
||||
группируй файлы по ответственности:
|
||||
|
||||
```text
|
||||
feature/
|
||||
├── feature.module.ts
|
||||
├── feature.api.ts # только при существующих внешних потребителях
|
||||
├── presentation/ # HTTP, DTO, guards
|
||||
├── application/ # сценарии, orchestration, application input
|
||||
├── domain/ # чистые бизнес-правила и расчёты
|
||||
└── infrastructure/ # persistence, framework и boundary mapping
|
||||
```
|
||||
|
||||
Создавай только нужные слои. В корне сложного module оставляй composition и
|
||||
публичную boundary; production code и colocated tests размещай в слое владельца.
|
||||
Внутри business slice действует то же правило, без отдельного Nest module ради папок.
|
||||
`controllers/`, `dto/`, `services/`, `use-cases/`, `persistence/`, `mappers/`
|
||||
допустимы только внутри слоя, когда множество однотипных файлов мешает навигации.
|
||||
Для одного controller или пары DTO дополнительная поддиректория не нужна.
|
||||
Пустые папки, новые barrels, CQRS и interfaces не требуются для этой организации.
|
||||
|
||||
## Поиск и создание файла
|
||||
|
||||
При работе над feature проходи владельцев в порядке:
|
||||
|
||||
1. Найди business module.
|
||||
2. Прочитай его `*.module.ts` (для slice — composition родительского module).
|
||||
3. Проверь `presentation/`: controller и transport contract.
|
||||
4. Проследи вызовы в `application/`: сценарии и services.
|
||||
5. Проверь используемые правила в `domain/`.
|
||||
6. Проверь `infrastructure/`: repository и технические реализации.
|
||||
7. По обнаруженным зависимостям переходи в `integrations/` и `common/`.
|
||||
|
||||
В плоском module классифицируй файлы по suffix и следуй их ответственности.
|
||||
Отсутствующий слой пропускай. Если business owner известен, начинай локально,
|
||||
а не с repository-wide search.
|
||||
|
||||
**Rule of locality:** code that belongs to one business capability stays inside
|
||||
that capability unless it is cross-cutting. Например, расчёты карт принадлежат
|
||||
bank-cards/domain, даже если реализованы обычными функциями.
|
||||
|
||||
Перед созданием файла определи:
|
||||
|
||||
1. Какой business module владеет изменением? Сначала owner, затем common при
|
||||
подтверждённой общей потребности.
|
||||
2. Какова ответственность: HTTP/DTO/guard → presentation; сценарий/orchestration →
|
||||
application; чистое business rule/calculation → domain; persistence или
|
||||
module-specific technical implementation → infrastructure; внешний протокол → integrations.
|
||||
3. Какой approved suffix из таблицы соответствует этой ответственности?
|
||||
4. Какой слой владеет файлом? В компактном плоском module сохрани размещение рядом
|
||||
с owner; в слоистом используй соответствующий слой.
|
||||
5. Достаточно ли colocate файл рядом с существующими? Level 3 grouping создавай
|
||||
только при заметном улучшении навигации по множеству однотипных файлов.
|
||||
|
||||
Если owner или роль не определены, сначала установи ответственность. Generic
|
||||
`utils.ts`, `helpers.ts`, `manager.ts`, `handler.ts` и arbitrary directory её не заменяют.
|
||||
|
||||
## Naming & File Suffix Convention
|
||||
|
||||
Имена файлов — `kebab-case`. File suffix определяет responsibility, directory
|
||||
определяет architectural layer. Размещение в таблице предпочтительно для
|
||||
слоистого module; компактный плоский module сохраняет файлы в корне.
|
||||
Cross-module consumers во всех строках ограничены публичной границей ниже.
|
||||
|
||||
| Suffix | Когда использовать | Где | Кто зависит | Когда не использовать |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `*.controller.ts` | HTTP endpoint / transport | presentation | Nest wiring | DB, расчёты, SDK, сложная оркестрация |
|
||||
| `*.dto.ts` | Request/response, validation, OpenAPI | presentation | Controller; application boundary при совпадении shape | Универсальная domain model |
|
||||
| `*.use-case.ts` | Один самостоятельный application scenario | application | Controller, другой orchestrator | Prisma queries, Request/Response, SDK implementation |
|
||||
| `*.service.ts` | Конкретная cohesive reusable capability | рядом с owner; application/domain/integration по роли | Сценарии и services; domain — только от чистого domain service | Свалка несвязанных сценариев |
|
||||
| `*.repository.ts` | Persistence queries и согласованные DB reads/writes | infrastructure | Application service/use case | Domain calculations, HTTP request, external transport |
|
||||
| `*.client.ts` | Низкоуровневый внешний HTTP/gRPC/SDK | integrations или infrastructure | Application orchestration, boundary mapper | Пользовательский lifecycle и domain rules |
|
||||
| `*.mapper.ts` | Meaningful conversion между моделями | обычно infrastructure у преобразуемой boundary | Код соответствующей boundary | Формальное копирование нескольких одинаковых полей |
|
||||
| `*.policy.ts` | Cohesive decision rule | domain/application/integration по входным данным | Владелец правила; domain — от чистой policy | Persistence, transport, generic strategy |
|
||||
| `*.guard.ts` | Nest guard | presentation | Nest/controller | Основная account authorization и финансовые правила |
|
||||
| `*.strategy.ts` | Framework strategy, например JWT | infrastructure | Framework wiring | Обычный service без strategy contract |
|
||||
| `*.module.ts` | Nest imports/providers/controllers/exports | module root | Composition root, importing module | Бизнес-логика |
|
||||
| `*.types.ts` | Малая группа связанных TS types | рядом с owner | Потребители модели на разрешённых слоях | Глобальные types всего module, runtime validation |
|
||||
| `*.api.ts` | Явный список публичных exports | root конкретного module/integration | Другие modules | DTO/client/service implementation или generic API logic |
|
||||
| `*-input.ts` | Application input: нормализация и проверка формы, отличающейся от wire DTO | application | Application code | Копия DTO под новым именем; вариант `*.input.ts` |
|
||||
| `*.calculations.ts`, `*.rules.ts` или предметное имя | Чистые бизнес-расчёты, value semantics | domain | Application и domain | Nest/Prisma/HTTP/SDK зависимости |
|
||||
| `*.spec.ts` | Существующая unit/component проверка поведения | рядом с owner/slice | Test runner | Архитектурные проверки папок и trivial wiring |
|
||||
|
||||
`bank-card-input.ts` использует выбранный `*-input.ts`: переводит Money в
|
||||
копейки, нормализует значения и проверяет application input. DTO не дублируется
|
||||
отдельным Input type. `rub-money.ts` — domain-правила RUB; `bond-money.mapper.ts`
|
||||
— преобразование ответа клиента. Эти роли не определяются словом money в имени.
|
||||
|
||||
Технические framework suffixes (`*.filter.ts`, `*.middleware.ts`,
|
||||
`*.decorator.ts`, `*.config.ts`, `*.validation.ts`) сохраняют соответствующие
|
||||
существующие роли в common/config. `tbank.crypto.ts` — локальная реализация
|
||||
шифрования токена; имя не является разрешением создавать generic crypto layer.
|
||||
HTTP/e2e naming и расположение — в [Testing](backend-testing.md).
|
||||
|
||||
Без зафиксированной необходимости не вводи `*.helper.ts`, `*.utils.ts`,
|
||||
`*.manager.ts`, `*.processor.ts`, `*.handler.ts`, `*.provider.ts`, `*.factory.ts`.
|
||||
Новая naming convention сначала получает конкретную роль и обоснование здесь.
|
||||
Для data access выбран только `*.repository.ts`, не persistence/store/dao.
|
||||
|
||||
## Публичные границы
|
||||
|
||||
`*.module.ts` задаёт DI boundary; `*.api.ts` перечисляет используемые другими
|
||||
модулями providers, функции и типы. Внешний production import идёт через этот
|
||||
API или файл Nest module. Чистый `integrations/tbank/decimal.ts` — отдельный
|
||||
публичный вход для protobuf conversion: импорт арифметики не загружает DI/Prisma.
|
||||
Внутри модуля используй прямые локальные импорты.
|
||||
Tests могут импортировать внутренний owner для проверки поведения и mocks.
|
||||
Новый export требует текущего потребителя; controller и persistence остаются
|
||||
приватными. Не добавляй `index.ts` на каждом уровне.
|
||||
|
||||
Circular dependencies устраняй изменением ownership/composition. `forwardRef()`
|
||||
не является стандартным исправлением boundaries; исключение требует конкретной
|
||||
неизбежной связи и проверки DI initialization.
|
||||
|
||||
Каждый provider регистрируется у одного владельца. Потребители импортируют
|
||||
его module, а не повторяют provider: это сохраняет единственный кэш и лимитер.
|
||||
`AuthModule` экспортирует guard; наличие JWT не заменяет account access check.
|
||||
|
||||
- `income` читает операции через `operations.api.ts`.
|
||||
- `instruments` получает личные позиции через `portfolio.api.ts`.
|
||||
- `tbank-connection` использует T-Bank integration и публичный сброс портфеля.
|
||||
- `portfolio` и другие инвестиционные модули используют `TbankModule`, который
|
||||
не зависит от business modules. Так управление подключением не образует цикл.
|
||||
- `investment-calendar.ts` и `account-reference.dto.ts` разделяются только
|
||||
инвестиционными областями; это не глобальная библиотека бизнес-правил.
|
||||
|
||||
## Ответственность и направление зависимостей
|
||||
|
||||
| Владелец | Ответственность |
|
||||
| --- | --- |
|
||||
| Controller / presentation | Route, verified identity, transport DTO, validation, status/cookie; вызов сценария |
|
||||
| Use case / application service | Оркестрация, business validation, доступ и согласованность результата |
|
||||
| Domain | Чистые правила и расчёты; без NestJS, Prisma, HTTP/Swagger и внешних SDK |
|
||||
| Persistence / infrastructure | Запросы, транзакции, технические реализации границ |
|
||||
| `src/integrations/{tbank,moex,ncr}` | Внешние протоколы, декодирование, retry/deadline/cache; без зависимостей от HTTP DTO business modules |
|
||||
| `src/prisma` | Общий DB provider и lifecycle; не generic repository |
|
||||
|
||||
Основной вызов: presentation → application → domain; application использует
|
||||
конкретные infrastructure providers, зарегистрированные module. Infrastructure
|
||||
может использовать application types/input и domain rules; новые interfaces
|
||||
ради инверсии зависимостей не обязательны. Domain не импортирует presentation или
|
||||
infrastructure. Существующие связные CRUD/transaction services могут сохранять прямые queries
|
||||
по [Data](backend-data.md); use case получает данные через repository и сам
|
||||
Prisma queries не выполняет. Entity/interface на каждую таблицу не требуется.
|
||||
DTO остаются транспортными runtime classes; существующее использование DTO
|
||||
на application boundary допустимо по [HTTP API](backend-http-api.md).
|
||||
|
||||
Сохраняй существующий error contract: HTTP exceptions остаются на application/
|
||||
transport boundary. Чистому расчёту не нужен новый framework ошибок.
|
||||
Mapping и stable codes — по [Errors](backend-error-handling.md).
|
||||
Точные ограничения — [HTTP](backend-http-api.md), [Data](backend-data.md),
|
||||
[Security](security-auth.md), [Integrations](backend-integrations.md).
|
||||
|
||||
## Сценарии и размер сервисов
|
||||
|
||||
Разделяй независимые операции, а не строки. Пример: `BankCardsService` владеет
|
||||
картами, `BankOperationsService` — журналом/переводами, `GetBankSummaryUseCase` —
|
||||
сводкой через repository и чистый расчёт. Не разрывай денежную transaction,
|
||||
общий cache/limiter или pending promises при extraction.
|
||||
|
||||
Расширение abstractions — по [change scope](../../../.ai/conventions/code-health.md#change-scope).
|
||||
|
||||
## Common
|
||||
|
||||
`common` содержит общий error filter, logging, API decorators, тип `Money`,
|
||||
его HTTP `MoneyDto` и точную decimal-арифметику, используемые несколькими областями. Код одного
|
||||
модуля принадлежит этому модулю: например, RUB/kopecks rules — банковским картам,
|
||||
protobuf units/nano conversion — T-Bank integration. Domain использует
|
||||
`money.types.ts`, а не Swagger DTO; это общая форма денег для карт и портфеля.
|
||||
Давай функциям предметные имена; не создавай глобальные `utils.ts`/`helpers.ts`.
|
||||
|
||||
## Domain literals and magic values
|
||||
|
||||
Общий выбор symbolic values и числовых policies —
|
||||
[Domain values](../../../.ai/conventions/code-health.md#domain-values).
|
||||
Сохраняй strict TS и explicit boundary types. Для недоверенного input выбирай
|
||||
`unknown` + runtime narrowing; отключённый lint `no-explicit-any` не делает
|
||||
`any` нормой domain/application model. `declare` допустим для заполняемых извне
|
||||
DTO fields, не для сокрытия неинициализированного owned state.
|
||||
|
||||
OpenAPI enum metadata и runtime validation должны читать тот же vocabulary,
|
||||
что application/domain. Например, `BankOperationKind` принадлежит bank-cards/domain,
|
||||
а DTO получает допустимые значения через `Object.values(BankOperationKind)`.
|
||||
Frontend переиспользует generated OpenAPI types/values по возможностям генератора,
|
||||
а не определяет независимый backend-owned словарь. Внешние/generated enums
|
||||
сохраняй; generated Prisma/protocol code вручную не меняй.
|
||||
|
||||
`@typescript-eslint/no-magic-numbers` включён как error; базовое ESLint rule
|
||||
отключено в пользу TS-версии. Исключения для очевидных чисел и их bigint форм,
|
||||
индексов, default values, enum/type declarations и readonly properties заданы
|
||||
в ESLint config. Test fixtures/expected values остаются inline, остальные lint
|
||||
правила продолжают действовать. Numeric policy получает предметное имя и единицы;
|
||||
semantic magic strings проверяются conventions/review без нового инструмента.
|
||||
|
||||
## Logging
|
||||
|
||||
События, levels, correlation и safe fields — по
|
||||
[Observability](backend-observability.md).
|
||||
|
||||
## Проверка
|
||||
|
||||
`npm run typecheck:app`, `npm run lint`, targeted Jest по изменённому use case.
|
||||
Смысл boundaries проверяется review: backend не имеет отдельного автоматического
|
||||
FSD/DI architecture checker. Existing tests и build не доказывают правильный owner.
|
||||
@@ -15,7 +15,7 @@ PrismaService — существующий shared DB provider с lifecycle пр
|
||||
| Независимые reads | Не добавляй transaction ради внешнего вида |
|
||||
| Общая persistence abstraction | Только при реальной общей ответственности; generic repository для одной Prisma реализации не нужен |
|
||||
|
||||
Размещение repository определяется [Architecture](backend-architecture.md):
|
||||
Размещение repository определяется [Architecture](../architecture.md):
|
||||
в слоистом module это `infrastructure/`.
|
||||
Repository — конкретный класс `*.repository.ts` у business owner, без обязательных
|
||||
interface/port/adapter. Он владеет queries, includes/filtering, DB mapping и
|
||||
@@ -81,4 +81,4 @@ client обновляй из schema. Не переписывай уже прим
|
||||
`npx prisma validate`; после schema change `npx prisma generate` и
|
||||
`npm run typecheck:app`. Invariants/atomicity/migration safety требуют tests
|
||||
на соответствующей boundary: mock Prisma не доказывает реальную transaction.
|
||||
Выбор integration/e2e и известные ограничения — в [Testing](backend-testing.md).
|
||||
Выбор integration/e2e и известные ограничения — в [Testing](testing.md).
|
||||
@@ -29,7 +29,7 @@ error framework/registry. Если domain outcome требует HTTP interpreta
|
||||
SQL, credentials или raw upstream payload в ответе.
|
||||
- External auth failure и временная недоступность имеют разные outcomes;
|
||||
translation выполняется на existing integration/access boundary.
|
||||
- Логи используют [observability](backend-observability.md). Redaction не
|
||||
- Логи используют [observability](observability.md). Redaction не
|
||||
делает произвольное сообщение ошибки безопасным.
|
||||
|
||||
## Проверка
|
||||
@@ -37,4 +37,4 @@ error framework/registry. Если domain outcome требует HTTP interpreta
|
||||
Для изменённого кода проверь observable status/code и сохранность данных.
|
||||
Для validation нужен request через `configureApp`: fabricated `ValidationError`
|
||||
в unit test filter не доказывает форму, выходящую из реального ValidationPipe.
|
||||
OpenAPI parity проверяет schema, а не response pipeline; [Testing](backend-testing.md).
|
||||
OpenAPI parity проверяет schema, а не response pipeline; [Testing](testing.md).
|
||||
@@ -27,7 +27,7 @@ module или presentation. DTO можно передать application boundary
|
||||
формы; отдельный Input с теми же полями не нужен. Domain не импортирует DTO:
|
||||
например, чистые денежные расчёты используют `Money`, а Swagger — `MoneyDto`.
|
||||
Нормализованная форма input нужна только при смысловом отличии от wire contract;
|
||||
выбор suffix принадлежит [Architecture](backend-architecture.md).
|
||||
выбор suffix принадлежит [Architecture](../architecture.md).
|
||||
ValidationPipe уже настроен в `configureApp` (`transform`, `whitelist`).
|
||||
Не обещай rejection неизвестных fields: `forbidNonWhitelisted` не включён.
|
||||
Для query numbers/arrays выбирай явное преобразование и валидаторы, как в
|
||||
@@ -37,7 +37,7 @@ ValidationPipe уже настроен в `configureApp` (`transform`, `whitelis
|
||||
Используй типизированные response DTO, `ApiOkResponse`/другой фактический status
|
||||
и существующий `ApiProblemResponses`. Prisma model не является public contract:
|
||||
response выбирает поля явно. Stable codes, RFC 9457 профиль и mapping принадлежат
|
||||
[Errors](backend-error-handling.md).
|
||||
[Errors](error-handling.md).
|
||||
|
||||
**Безопасный путь:** используй exception и общий filter, а не локальный
|
||||
`res.status(...).json(...)`. Исключение — транспортная операция вроде cookie;
|
||||
@@ -64,4 +64,4 @@ helper ради первого endpoint. Новая pagination явно опре
|
||||
errors на boundary, где они возникают. Unit вызов controller не запускает pipes,
|
||||
guards или serialization. `npm run test:openapi-contract` проверяет schema parity,
|
||||
но не поведение invalid request; выбирай соответствующий test по
|
||||
[Testing](backend-testing.md). Финальный gate не заменяет semantic review ошибок.
|
||||
[Testing](testing.md). Финальный gate не заменяет semantic review ошибок.
|
||||
@@ -18,7 +18,7 @@ RPC, credentials/access и resilience. Эти владельцы не объед
|
||||
`*.client.ts` содержит transport и его retries; `*.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`.
|
||||
|
||||
Преобразование provider records происходит на boundary: например,
|
||||
@@ -31,7 +31,7 @@ client или mapper для простого копирования полей.
|
||||
Для нового account use case сначала используй TbankAccessService: access,
|
||||
connection generation и invalid-token translation не дублируются в callers.
|
||||
Mapper внешних финансовых значений остаётся внутри business capability,
|
||||
обычно в infrastructure по [Architecture](backend-architecture.md).
|
||||
обычно в infrastructure по [Architecture](../architecture.md).
|
||||
|
||||
## Retry, cache и protocol
|
||||
|
||||
@@ -69,4 +69,4 @@ Generated artifacts, если есть, создавай из source, без р
|
||||
upstream responses. Проверь queue/deadline/retry limit, token translation,
|
||||
cache isolation и late response для затронутых ветвей. Fixtures синтетические;
|
||||
реальная сеть, секреты и пользовательские accounts не нужны.
|
||||
Logging использует [existing logger](backend-architecture.md#logging).
|
||||
Logging использует [existing logger](../architecture.md#logging).
|
||||
@@ -15,7 +15,7 @@
|
||||
или upstream error. Secret в `message/stack` не становится безопасным из-за
|
||||
allowlist; правила [Security](security-auth.md) обязательны для sensitive data.
|
||||
- Для ошибки используй public code и безопасный context операции/попыток;
|
||||
[error mapping](backend-error-handling.md) не отдаёт внутренние детали клиенту.
|
||||
[error mapping](error-handling.md) не отдаёт внутренние детали клиенту.
|
||||
|
||||
Причины — [ADR 008](../../../docs/adr/008-pino-backend-logging.md), системное
|
||||
поведение — [HTTP observability](../../../docs/architecture/http-api.md#наблюдаемость).
|
||||
@@ -55,6 +55,6 @@ cache invalidation при замене/deletion и ограничение key ro
|
||||
фиктивным success или log. Logger allowlist/redaction не делает произвольный
|
||||
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
|
||||
проверяется MSW. Schema parity не доказывает отсутствие утечки secret.
|
||||
|
||||
@@ -2,20 +2,19 @@
|
||||
|
||||
Общие инструкции — [workspace AGENTS](../AGENTS.md). Объединяй triggers задачи,
|
||||
путей и обнаруженных boundaries; читай только нужные темы до решения.
|
||||
[Sources](.ai/SOURCES.md) выбирает факты/spec/ADR по возникшему вопросу.
|
||||
Системные факты/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) |
|
||||
| Устройство/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/backend-integrations.md) |
|
||||
| Logging/context/levels, sensitive logs | [Observability](.ai/conventions/backend-observability.md); sensitive data также Security |
|
||||
| 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) |
|
||||
@@ -25,3 +24,19 @@
|
||||
Повтори routing по итоговому diff, включая новые файлы. Migration backlog
|
||||
читается только при соответствующем finding через
|
||||
[plan](../.ai/migrations/conventions-v1/plan.md).
|
||||
|
||||
## Источники
|
||||
|
||||
Открывай только источник по возникшему вопросу. Code показывает реализацию,
|
||||
docs — текущую систему, ADR — rationale, specs — requirements. Старый пример
|
||||
не отменяет контракт; conflicting security/data facts выясняй до решения.
|
||||
|
||||
| Вопрос | Источник |
|
||||
| --- | --- |
|
||||
| HTTP errors/DTO/wire shape | [HTTP API](../docs/architecture/http-api.md); editable OpenAPI генерирует Nest |
|
||||
| Auth/session, account access, secrets | [Authentication](../docs/domains/authentication/overview.md), [T-Bank](../docs/integrations/tbank.md) |
|
||||
| Prisma/data ownership, хранение и доступ | [Containers](../docs/architecture/containers.md#хранимые-данные-и-границы-доступа) |
|
||||
| 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)
|
||||
- [Backend architecture](.ai/conventions/backend-architecture.md)
|
||||
- [HTTP API](.ai/conventions/backend-http-api.md), [data](.ai/conventions/backend-data.md) и [testing](.ai/conventions/backend-testing.md)
|
||||
- [Security/auth](.ai/conventions/security-auth.md) и [integrations](.ai/conventions/backend-integrations.md)
|
||||
- [AGENTS: правила и источники](AGENTS.md)
|
||||
- [Backend architecture](.ai/architecture.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/integrations.md)
|
||||
|
||||
- [Карта проектных фактов](../docs/README.md)
|
||||
- [API contract и runtime OpenAPI](../docs/architecture/http-api.md#источник-контракта)
|
||||
|
||||
Reference in New Issue
Block a user