refactor: organize backend by business responsibility
This commit is contained in:
+3
-3
@@ -6,12 +6,12 @@
|
||||
|
||||
| Решение / сигнал | Читать |
|
||||
| --- | --- |
|
||||
| Новый module/provider/service; бизнес-логика; перенос responsibility | [Architecture](conventions/backend-architecture.md) |
|
||||
| Новый файл/suffix/layer; поиск feature owner; progressive structure; перенос responsibility | [Architecture](conventions/backend-architecture.md) |
|
||||
| Controller, DTO, route/status, validation/exception, публичная HTTP-семантика | [HTTP API](conventions/backend-http-api.md) |
|
||||
| Prisma/schema/query/persistence, transaction, migration, atomic write | [Data](conventions/backend-data.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) |
|
||||
| T-Bank/gRPC/proto, upstream retry/rate limit/cache, MoexClient | [Integrations](conventions/backend-integrations.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 | [Общий выбор реализации](../../docs/development/engineering-governance.md#выбор-реализации) |
|
||||
| Изменение request/response/route/status/error shape или Swagger metadata | HTTP API + [общий contract workflow](../../docs/development/runtime-openapi-verification.md) |
|
||||
|
||||
@@ -1,39 +1,207 @@
|
||||
# Backend architecture
|
||||
|
||||
## Выбор владельца
|
||||
## Module-first organization
|
||||
|
||||
Найди существующий use case, его collaborators и tests до создания module/service.
|
||||
Сохрани ответственность в текущем module, если новая возможность относится к нему.
|
||||
Module — граница связного набора providers, а не папка для каждого endpoint.
|
||||
Backend — domain-oriented modular monolith с лёгким разделением
|
||||
по ответственности. `src/modules` группирует код по бизнес-возможности: `auth`, `banking/bank-cards`, области `investments`.
|
||||
В инвестициях отдельные Nest modules владеют `brokerage-accounts`, `portfolio`,
|
||||
`operations`, `income`, `upcoming-events`, `instruments`, `tbank-connection`.
|
||||
`instruments/stocks` и `instruments/bonds` — slices внутри одного модуля.
|
||||
|
||||
| Решение | Owner |
|
||||
### 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 не требуются для этой организации.
|
||||
|
||||
В текущем коде слои используют `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 проходи владельцев в порядке:
|
||||
|
||||
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` на каждом уровне.
|
||||
|
||||
Каждый 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` разделяются только
|
||||
инвестиционными областями; это не глобальная библиотека бизнес-правил.
|
||||
|
||||
## Ответственность и направление зависимостей
|
||||
|
||||
| Владелец | Ответственность |
|
||||
| --- | --- |
|
||||
| Route, request/response mapping, status/cookie | Controller, [HTTP rules](backend-http-api.md) |
|
||||
| Business use case, orchestration, domain validation | Feature service |
|
||||
| Input shape/format | DTO + configured ValidationPipe |
|
||||
| Атомарность/DB query | Сервис use case через PrismaService, [Data](backend-data.md) |
|
||||
| Внешний protocol, deadline/retry/limiting | Existing client, [Integrations](backend-integrations.md) |
|
||||
| Чистое вычисление/mapper без lifecycle и DI | Обычная функция рядом с domain owner |
|
||||
| Bootstrap/config/global cross-cutting concern | AppModule, config, существующий common owner |
|
||||
| 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 |
|
||||
|
||||
**Безопасный путь:** расширь текущий owner; выделяй collaborator, когда он скрывает
|
||||
самостоятельный инвариант или координацию. Не вводи CQRS, generic repository,
|
||||
interface с одной реализацией или base controller только ради шаблона Nest.
|
||||
Не превращай common в хранилище бизнес-логики и не переноси frontend модели сюда.
|
||||
Основной вызов: 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).
|
||||
|
||||
## Существующие seams
|
||||
Сохраняй существующий error contract: HTTP exceptions остаются на application/
|
||||
transport boundary. Чистому расчёту не нужен новый framework ошибок.
|
||||
Точные ограничения — [HTTP](backend-http-api.md), [Data](backend-data.md),
|
||||
[Security](security-auth.md), [Integrations](backend-integrations.md).
|
||||
|
||||
`TbankConnectionModule` уже объединяет счета, портфель, операции, доходы и события.
|
||||
Новый связанный use case обычно добавляется в него; создание отдельного module
|
||||
требует собственной ответственности, не только нового файла.
|
||||
## Сценарии и размер сервисов
|
||||
|
||||
- [BrokerageOperationsController](../../src/tbank/brokerage-operations.controller.ts) передаёт user/account/query в service, без финансовых вычислений в controller.
|
||||
- [TbankAccessService](../../src/tbank/tbank-access.service.ts) владеет connection/account access и generation; новые account use cases используют его вместо копирования guard/decrypt logic.
|
||||
- [decimal.ts](../../src/tbank/decimal.ts) — пример pure domain helper; Injectable и adapter для арифметики не нужны.
|
||||
Разделяй независимые операции, а не строки. `BankCardsService` отвечает за карты,
|
||||
`BankOperationsService` — журнал/переводы/возвраты, `ExpenseCategoriesService` —
|
||||
категории, `GetBankSummaryUseCase` — сводку. `BankCardsRepository` сохраняет
|
||||
общий transaction retry и owner lookup; денежные транзакции не разрываются.
|
||||
Чистые расчёты банковских карт находятся в `domain` этого модуля.
|
||||
|
||||
Наличие guard подтверждает session, но не владение account. При пересечении
|
||||
access boundary добавь [Security](security-auth.md). Ошибка upstream не должна
|
||||
обходить общий HTTP filter или превращать чужой account в доступный.
|
||||
У инструментов `StockCatalogService` / `BondCatalogService` хранят идентичность
|
||||
листинга и общий кэш 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` содержит общий 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`.
|
||||
|
||||
## Logging
|
||||
|
||||
|
||||
@@ -9,12 +9,33 @@ PrismaService — существующий shared DB provider с lifecycle пр
|
||||
|
||||
| Ситуация | Решение |
|
||||
| --- | --- |
|
||||
| Один query/write конкретного use case | Existing service + PrismaService |
|
||||
| Данные для use case | Concrete repository; use case не импортирует PrismaService/Prisma и не строит queries |
|
||||
| Несколько записей обязаны выполниться вместе | Database transaction вокруг инварианта |
|
||||
| Конкурентная смена одного значения | Atomic conditional update/constraint с проверкой результата |
|
||||
| Независимые reads | Не добавляй transaction ради внешнего вида |
|
||||
| Общая persistence abstraction | Только при реальной общей ответственности; generic repository для одной Prisma реализации не нужен |
|
||||
|
||||
Размещение repository определяется [Architecture](backend-architecture.md):
|
||||
в слоистом module это `infrastructure/`.
|
||||
Repository — конкретный класс `*.repository.ts` у business owner, без обязательных
|
||||
interface/port/adapter. Он владеет queries, includes/filtering, DB mapping и
|
||||
согласованным чтением. Суффиксы `*.persistence.ts`, `*.store.ts`, `*.dao.ts`
|
||||
для той же роли не используем. Nest module регистрирует repository приватно.
|
||||
|
||||
`BankCardsRepository.summaryData` проверяет владельца карты и получает операции
|
||||
с категориями в одной read transaction. `GetBankSummaryUseCase` выбирает период,
|
||||
вызывает repository, затем чистый `bankSummary` и формирует ответ. Prisma records
|
||||
не становятся domain entities: расчёт получает только нужные ему значения.
|
||||
|
||||
Не создавай обёртку на каждый вызов Prisma. Существующие связные CRUD/transaction
|
||||
services (auth, lifecycle подключения, изменения карт/операций/категорий)
|
||||
сохраняют queries рядом с CAS и проверками инварианта, если их перенос требует
|
||||
лишь набора pass-through методов. Новая самостоятельная read/write capability
|
||||
получает repository method; самостоятельный use case не выполняет Prisma queries.
|
||||
`BankCardsRepository.write` сохраняет общую retry/transaction boundary для
|
||||
существующих banking services. Такой callback с Prisma transaction client —
|
||||
локальная техническая граница этих services, не API для domain или use cases.
|
||||
|
||||
Выбирай только нужные данные, сохраняй user/account ownership в запросах и
|
||||
application checks. Предварительный lookup не заменяет unique constraint при race.
|
||||
Пример: AuthService обрабатывает P2002 и использует conditional `updateMany`
|
||||
|
||||
@@ -16,16 +16,23 @@ use case. Для route/request/response/status/error shape читай
|
||||
| HTTP envelope/безопасная ошибка | Общий HttpExceptionFilter |
|
||||
|
||||
Controller объявляет contract и делегирует; не обращается напрямую к Prisma/gRPC
|
||||
ради обхода service. Request identity берётся из verified `request.user`, не из
|
||||
body/userId, предоставленного клиентом. Access rules активируют Security.
|
||||
и не выполняет финансовые расчёты или сложную оркестрацию.
|
||||
Controller вызывает application use case/service. Identity берётся из verified
|
||||
`request.user`, не из body/userId, предоставленного клиентом. Access rules активируют Security.
|
||||
|
||||
## DTO и errors
|
||||
|
||||
DTO — runtime class: TS interface не предоставляет validation metadata.
|
||||
`*.controller.ts` и `*.dto.ts` остаются около HTTP boundary, в root компактного
|
||||
module или presentation. DTO можно передать application boundary при совпадении
|
||||
формы; отдельный Input с теми же полями не нужен. Domain не импортирует DTO:
|
||||
например, чистые денежные расчёты используют `Money`, а Swagger — `MoneyDto`.
|
||||
Нормализованная форма input нужна только при смысловом отличии от wire contract;
|
||||
выбор suffix принадлежит [Architecture](backend-architecture.md).
|
||||
ValidationPipe уже настроен в `configureApp` (`transform`, `whitelist`).
|
||||
Не обещай rejection неизвестных fields: `forbidNonWhitelisted` не включён.
|
||||
Для query numbers/arrays выбирай явное преобразование и валидаторы, как в
|
||||
[operations DTO](../../src/tbank/brokerage-operation.dto.ts). Swagger metadata
|
||||
[operations DTO](../../src/modules/investments/operations/presentation/brokerage-operation.dto.ts). Swagger metadata
|
||||
описывает те же required/optional/nullable/enum/range значения.
|
||||
|
||||
Используй типизированные response DTO, `ApiOkResponse`/другой фактический status
|
||||
@@ -40,7 +47,7 @@ raw upstream error, Prisma record с secrets или stack. Текущая фор
|
||||
|
||||
## Пример и проверка
|
||||
|
||||
[Operations controller](../../src/tbank/brokerage-operations.controller.ts)
|
||||
[Operations controller](../../src/modules/investments/operations/presentation/brokerage-operations.controller.ts)
|
||||
показывает guard, user.sub, DTO query и service delegation.
|
||||
Для нового field обновляй canonical source и generated artifacts в определённом
|
||||
направлении; body type frontend не исправляется вручную.
|
||||
|
||||
@@ -3,14 +3,34 @@
|
||||
## Выбор boundary
|
||||
|
||||
T-Bank gRPC принадлежит TbankClient; MOEX metadata — MoexClient. Сервисы use case
|
||||
получают нормализованный результат через эти clients. Frontend не вызывает
|
||||
используют объявленные клиентом records; SDK clients, metadata и transport errors
|
||||
остаются внутри integration. Frontend не вызывает
|
||||
T-Bank и не получает credentials/proto client. Reference source —
|
||||
[ADR 003](../../../docs/adr/003-tbank-server-side-grpc-boundary.md) и
|
||||
[connection behavior](../../../docs/integrations/tbank.md).
|
||||
|
||||
`modules/investments/tbank-connection` — пользовательская capability чтения,
|
||||
создания/замены и удаления подключения. `integrations/tbank` — технические
|
||||
RPC, credentials/access и resilience. Эти владельцы не объединяются из-за имени
|
||||
поставщика; низкоуровневый client не зависит от пользовательского module.
|
||||
|
||||
`*.client.ts` содержит transport и его retries; `*.api.ts` только перечисляет
|
||||
публичные exports. Все шесть `auth/operations/portfolio/tbank/moex/ncr.api.ts`
|
||||
имеют одинаковую роль программной границы, определённую в
|
||||
[Architecture](backend-architecture.md). Логика, DTO definitions и HTTP routes
|
||||
в эти файлы не помещаются. Type exports обозначаются `export type`.
|
||||
|
||||
Преобразование provider records происходит на boundary: например,
|
||||
`tbank-portfolio.mapper.ts` и `bond-money.mapper.ts`; protobuf decimal helper
|
||||
остаётся отдельным чистым входом integration. Domain получает внутренние
|
||||
значения и не импортирует gRPC/SDK types. Не добавляй новый adapter на каждый
|
||||
client или mapper для простого копирования полей. Даты/units/nano разбираются
|
||||
на границе клиента/его mapping, а не становятся моделью чистых расчётов.
|
||||
|
||||
Для нового account use case сначала используй TbankAccessService: access,
|
||||
connection generation и invalid-token translation не дублируются в callers.
|
||||
Чистый mapper финансового значения остаётся рядом с domain owner.
|
||||
Mapper внешних финансовых значений остаётся внутри business capability,
|
||||
обычно в infrastructure по [Architecture](backend-architecture.md).
|
||||
|
||||
## Retry, cache и protocol
|
||||
|
||||
@@ -30,7 +50,7 @@ cache/stale policy; semantics конкретного use case читай чер
|
||||
raw gRPC exception не уходит клиенту. Existing translation — в TbankTokenService
|
||||
и общем HTTP boundary. Для HTTP error change читай HTTP API/Security по trigger.
|
||||
|
||||
Vendor proto хранится в `src/tbank/proto/contracts`; для обновления сверяй
|
||||
Vendor proto хранится в `src/integrations/tbank/proto/contracts`; для обновления сверяй
|
||||
официальный source и влияние на loader/DTO, не редактируй read-only references.
|
||||
Generated artifacts, если есть, создавай из source, без ручной подгонки вывода.
|
||||
|
||||
@@ -40,7 +60,7 @@ Generated artifacts, если есть, создавай из source, без р
|
||||
ответственного service. Отдельный abstraction/adapter оправдан новой
|
||||
ответственностью или несовместимым protocol, не только вторым методом.
|
||||
|
||||
[TbankClient tests](../../src/tbank/tbank.client.spec.ts) — пример controlled
|
||||
[TbankClient tests](../../src/integrations/tbank/tbank.client.spec.ts) — пример controlled
|
||||
upstream responses. Проверь queue/deadline/retry limit, token translation,
|
||||
cache isolation и late response для затронутых ветвей. Fixtures синтетические;
|
||||
реальная сеть, секреты и пользовательские accounts не нужны.
|
||||
|
||||
@@ -21,8 +21,8 @@ Unit specs colocated в `src/**/*.spec.ts`; HTTP/integration — `test/`.
|
||||
|
||||
## Случаи и примеры
|
||||
|
||||
- [BrokerageAccountsService tests](../../src/tbank/brokerage-accounts.service.spec.ts): отсутствие connection не вызывает upstream, нормализация и точные суммы.
|
||||
- [AuthService tests](../../src/auth/auth.service.spec.ts): invalid/expired/superseded token и concurrent rotation; mock race test не является доказательством DB transaction.
|
||||
- [BrokerageAccountsService tests](../../src/modules/investments/brokerage-accounts/brokerage-accounts.service.spec.ts): отсутствие connection не вызывает upstream, нормализация и точные суммы.
|
||||
- [AuthService tests](../../src/modules/auth/application/auth.service.spec.ts): invalid/expired/superseded token и concurrent rotation; mock race test не является доказательством DB transaction.
|
||||
- [HTTP filter tests](../../src/common/filters/http-exception.filter.spec.ts): безопасный error envelope.
|
||||
- [OpenAPI integration](../../test/openapi-contract.e2e-spec.ts): configured app + frontend snapshot, без записи snapshot и без T-Bank.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ TbankTokenService/TbankCrypto. Расширяй существующую boundar
|
||||
[ADR 006](../../../docs/adr/006-auth-session-policy.md)/[009](../../../docs/adr/009-concurrent-refresh-rotation-policy.md)
|
||||
задают session semantics. Access хранится только в памяти frontend; refresh
|
||||
передаётся httpOnly cookie. Source code конфигурации cookie —
|
||||
[AuthController](../../src/auth/auth.controller.ts); изменение flags/path требует
|
||||
[AuthController](../../src/modules/auth/presentation/auth.controller.ts); изменение flags/path требует
|
||||
проверки browser/HTTP поведения, не только TypeScript.
|
||||
|
||||
Refresh rotation атомарна: один победитель, проигравший/superseded token получает
|
||||
|
||||
Reference in New Issue
Block a user