- Replace 4 duplicate meta DTOs (AuthResponseMetaDto, PortfolioResponseMetaDto, BrokerResponseMetaDto, ScreenerResponseMetaDto) with shared ApiResponseMeta - Wrap shares getShare() in ApiEnvelopePayload (was raw object, unlike bonds) - Remove unnecessary CacheModule import from securities module - Update portfolio controller nullDataEnvelopeSchema to use shared ApiResponseMeta - All 116 tests pass
53 lines
2.6 KiB
Markdown
53 lines
2.6 KiB
Markdown
# Backend Architecture Improvements — Plan
|
||
|
||
## Подход
|
||
|
||
Разбиваем на итерации от самых безопасных (только DTO/косметика) к самым рискованным (сплит сервисов). Каждая итерация — отдельная задача с отдельным commit'ом.
|
||
|
||
## Итерации
|
||
|
||
### Итерация 1: Shared envelope DTO + fixes
|
||
|
||
1. Вынести `ApiResponseMeta` в `common/dto/api-response.dto.ts` как единый класс, убрать дубликаты `*ResponseMetaDto` в модулях.
|
||
2. Убрать `AuthResponseMetaDto` → заменить на `ApiResponseMeta`.
|
||
3. Поправить `shares/shares.service.ts:getShare()` — обернуть результат в `ApiEnvelopePayload`.
|
||
4. Убрать лишний импорт `CacheModule` из `securities/securities.module.ts`.
|
||
5. Убрать `PortfolioResponseMetaDto` и `BrokerResponseMetaDto` — заменить на `ApiResponseMeta`.
|
||
|
||
### Итерация 2: Screener caching + server-side пагинация
|
||
|
||
1. Добавить кеширование полного screener-датасета при пустом фильтре.
|
||
2. Ключ кеша: `'screener:full'` с TTL из `app.cache.marketDataTtl`.
|
||
|
||
### Итерация 3: Domain exception hierarchy
|
||
|
||
1. Создать `common/exceptions/` с базой `DomainException` и конкретными классами.
|
||
2. Обновить `HttpExceptionFilter` для map'инга доменных → HTTP исключений.
|
||
3. Заменить `NotFoundException` на `EntityNotFoundException` в сервисах.
|
||
|
||
### Итерация 4: Health check improvement
|
||
|
||
1. Добавить проверки: Prisma ping, MOEX `/health`, T-Bank gRPC connectivity.
|
||
2. Обновить `HealthResponseDto` с полем `checks`.
|
||
|
||
### Итерация 5: RequestLoggingMiddleware через DI
|
||
|
||
1. Перенести middleware в корректный DI-контекст через `configure()` в `AppModule`.
|
||
|
||
### Итерация 6 (отдельный эпик): MoexClientService split
|
||
|
||
1. Выделить `MoexSecuritiesClient`, `MoexMarketDataClient`, `MoexCandlesClient`.
|
||
2. Общий rate limiter + circuit breaker в shared utils.
|
||
|
||
## Data Flow
|
||
|
||
```
|
||
Controller → Service → [CacheService.getOrFetch] → [MoexClient* или TBank*] → Внешнее API
|
||
↓
|
||
ApiEnvelopePayload<T>
|
||
↓
|
||
TransformInterceptor → ApiResponse<T>
|
||
```
|
||
|
||
После итерации 1 все модули следуют этому потоку единообразно.
|