docs: add ADR-020 and feature docs for MoexClientService split
This commit is contained in:
parent
238836c850
commit
d2457d13af
63
apps/docs/docs/adr/ADR-020-moex-client-split.md
Normal file
63
apps/docs/docs/adr/ADR-020-moex-client-split.md
Normal file
@ -0,0 +1,63 @@
|
||||
# ADR-020: Разделение MoexClientService на доменные клиенты
|
||||
|
||||
**Дата:** 2026-06-25
|
||||
**Статус:** Принято
|
||||
**Автор:** AI Agent (codex/backend-architecture-improvements)
|
||||
|
||||
## Контекст
|
||||
|
||||
`MoexClientService` в `apps/backend/src/modules/moex-client/moex-client.service.ts` (423 строки, 11 публичных методов) со временем стал God Service:
|
||||
|
||||
- Нарушает SRP — содержит логику работы с акциями, облигациями, свечами, историей, дивидендами и поиском в одном классе
|
||||
- Инфраструктура (rate limiter, circuit breaker) смешана с бизнес-логикой
|
||||
- Все 11 методов используют один шаблон: `request()` → `extractTable()` → map, но каждый с разными endpoint-ами и типами
|
||||
- Потребители (shares, bonds, candles, portfolio, securities, screener, tbank/events) получают весь сервис целиком, а не только нужную функциональность
|
||||
- Тестирование затруднено: любой тест одного метода тянет весь сервис
|
||||
|
||||
## Рассмотренные варианты
|
||||
|
||||
### A. Полный сплит по доменам (выбран)
|
||||
|
||||
Выделить инфраструктурный слой (`MoexHttpClient`) и 5 доменных клиентов — по одному на группу MOEX-запросов.
|
||||
|
||||
### B. Минимальный сплит
|
||||
|
||||
Вынести только `MoexHttpClient` (request + extractTable + circuit breaker + rate limiter), оставить все методы в одном сервисе с делегированием через DI.
|
||||
|
||||
Отклонён: не решает проблему God Service — один сервис всё ещё содержит всю доменную логику.
|
||||
|
||||
### C. Оставить как есть
|
||||
|
||||
Отклонён: противоречит результатам аудита, 423-строчный сервис ухудшает поддерживаемость.
|
||||
|
||||
## Решение
|
||||
|
||||
Выбран вариант A — полный сплит по доменам:
|
||||
|
||||
- `MoexHttpClient` — инфраструктура (axios, PQueue, circuit breaker, `request()`, `extractTable()`)
|
||||
- `MoexSecuritiesClient` — `searchSecurities()`, `getSecurityDescription()`
|
||||
- `MoexMarketDataClient` — `getShareMarketData()`, `getShareMarketDataBatch()`, `getBondData()`, `getBondMarketData()`, `getBondPositionDataBatch()`
|
||||
- `MoexCandlesClient` — `getCandles()`
|
||||
- `MoexHistoryClient` — `getHistory()`, `getBondHistory()`
|
||||
- `MoexDividendsClient` — `getDividends()`
|
||||
|
||||
Модуль теряет `@Global()` — каждый потребитель явно импортирует `MoexClientModule`.
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
- Чёткое разделение ответственности — каждый клиент отвечает за один домен MOEX API
|
||||
- Возможность мокать только нужный клиент в тестах потребителей
|
||||
- `MoexHttpClient` — внутренняя деталь, не экспортируется из модуля
|
||||
- Явные зависимости через imports модулей вместо одного God Service
|
||||
|
||||
### Риски
|
||||
- Миграция всех 7 потребителей в одном коммите (нельзя оставить половинчатое состояние)
|
||||
- Каждый потребитель должен импортировать `MoexClientModule` — больше boilerplate
|
||||
- Необходимость обновить все тесты потребителей (DI-инъекция меняется)
|
||||
|
||||
## Связанные документы
|
||||
- ADR-003: Стратегия rate limiting (остаётся актуальной, инфраструктура переносится в MoexHttpClient)
|
||||
- ADR-004: Feature modules (принцип явных зависимостей)
|
||||
- `docs/features/backend-architecture-improvements/plan.md` (Iteration 6)
|
||||
- `docs/features/backend-architecture-improvements/tasks.md` (Iteration 6)
|
||||
@ -45,12 +45,7 @@
|
||||
- [x] 5.2 Убран `new RequestLoggingMiddleware()` и `app.use()` из `main.ts`
|
||||
- [x] 120 тестов проходят, build успешен
|
||||
|
||||
## Итерация 6: MoexClientService split (отдельный эпик)
|
||||
## Итерация 6: MoexClientService split → `docs/features/moex-client-split/`
|
||||
|
||||
- [ ] 6.1 ADR на разделение MoexClientService
|
||||
- [ ] 6.2 spec/plan/tasks отдельного эпика
|
||||
- [ ] 6.3 Выделение rate limiter + circuit breaker в shared utils
|
||||
- [ ] 6.4 Создание MoexSecuritiesClient
|
||||
- [ ] 6.5 Создание MoexMarketDataClient
|
||||
- [ ] 6.6 Создание MoexCandlesClient
|
||||
- [ ] 6.7 Обновление всех потребителей
|
||||
- [x] 6.1 ADR на разделение MoexClientService
|
||||
- [x] 6.2 spec/plan/tasks отдельного эпика → перенесено в `docs/features/moex-client-split/{spec,plan,tasks}.md`
|
||||
|
||||
73
docs/features/moex-client-split/plan.md
Normal file
73
docs/features/moex-client-split/plan.md
Normal file
@ -0,0 +1,73 @@
|
||||
# MoexClientService Split — Plan
|
||||
|
||||
## Подход
|
||||
|
||||
Полный сплит по доменам (ADR-020). Один коммит — миграция всех файлов одновременно.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```
|
||||
modules/moex-client/
|
||||
├── moex-http.client.ts # Инфраструктура: axios, PQueue, circuit breaker
|
||||
├── moex-securities.client.ts # searchSecurities(), getSecurityDescription()
|
||||
├── moex-market-data.client.ts # getShareMarketData(), getShareMarketDataBatch(),
|
||||
│ # getBondData(), getBondMarketData(), getBondPositionDataBatch()
|
||||
├── moex-candles.client.ts # getCandles()
|
||||
├── moex-history.client.ts # getHistory(), getBondHistory()
|
||||
├── moex-dividends.client.ts # getDividends()
|
||||
├── moex-client.types.ts # (unchanged)
|
||||
├── moex-client.module.ts # providers: все клиенты, exports: доменные клиенты, не @Global()
|
||||
├── moex-client.service.spec.ts # → moex-http.client.spec.ts
|
||||
└── moex-client.service.integration.spec.ts # → интеграционные тесты
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
Consumer Service
|
||||
↓ (DI)
|
||||
Domain Client (MoexSecuritiesClient | MoexMarketDataClient | etc.)
|
||||
↓ (DI)
|
||||
MoexHttpClient (request + extractTable)
|
||||
↓
|
||||
MOEX ISS API
|
||||
```
|
||||
|
||||
`MoexHttpClient` — не экспортируется из модуля, только доменные клиенты его видят.
|
||||
|
||||
## Consumer Updates
|
||||
|
||||
| Модуль | Было | Стало |
|
||||
|--------|------|-------|
|
||||
| `shares/shares.module.ts` | — | `imports: [MoexClientModule]` |
|
||||
| `shares/shares.service.ts` | `moexClient: MoexClientService` | `moexSecurities: MoexSecuritiesClient`, `moexMarketData: MoexMarketDataClient` |
|
||||
| `bonds/bonds.module.ts` | — | `imports: [MoexClientModule]` |
|
||||
| `bonds/bonds.service.ts` | `moexClient: MoexClientService` | `moexMarketData: MoexMarketDataClient`, `moexHistory: MoexHistoryClient` |
|
||||
| `candles/candles.module.ts` | — | `imports: [MoexClientModule]` |
|
||||
| `candles/candles.service.ts` | `moexClient: MoexClientService` | `moexCandles: MoexCandlesClient` |
|
||||
| `securities/securities.module.ts` | — | `imports: [MoexClientModule]` |
|
||||
| `securities/securities.service.ts` | `moexClient: MoexClientService` | `moexSecurities: MoexSecuritiesClient`, `moexMarketData: MoexMarketDataClient` |
|
||||
| `securities/screener.service.ts` | `moexClient: MoexClientService` | `moexMarketData: MoexMarketDataClient` |
|
||||
| `portfolio/portfolio.module.ts` | — | `imports: [MoexClientModule]` |
|
||||
| `portfolio/portfolio.service.ts` | `moexClient: MoexClientService` | `moexSecurities: MoexSecuritiesClient`, `moexMarketData: MoexMarketDataClient`, `moexDividends: MoexDividendsClient` |
|
||||
| `tbank/tbank.module.ts` | `imports: [MoexClientModule]` | (unchanged) |
|
||||
| `tbank/.../broker-events.service.ts` | `moexClient: MoexClientService` | `moexDividends: MoexDividendsClient`, `moexMarketData: MoexMarketDataClient` |
|
||||
|
||||
## Migration Order
|
||||
|
||||
1. Создать `MoexHttpClient` — перенести инфраструктуру из `MoexClientService`
|
||||
2. Создать `MoexSecuritiesClient` — перенести 2 метода
|
||||
3. Создать `MoexMarketDataClient` — перенести 5 методов
|
||||
4. Создать `MoexCandlesClient` — перенести 1 метод
|
||||
5. Создать `MoexHistoryClient` — перенести 2 метода
|
||||
6. Создать `MoexDividendsClient` — перенести 1 метод
|
||||
7. Обновить `MoexClientModule` — убрать `@Global()`, новые providers/exports
|
||||
8. Обновить всех потребителей (модули + сервисы + тесты)
|
||||
9. Удалить старый `MoexClientService`
|
||||
10. `npm run build && npm run test`
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- `MoexHttpClient` — unit-тесты на circuit breaker, rate limiter, request
|
||||
- Каждый доменный клиент — unit-тесты с mocked `MoexHttpClient`
|
||||
- Существующие тесты потребителей — обновить DI-моки
|
||||
35
docs/features/moex-client-split/spec.md
Normal file
35
docs/features/moex-client-split/spec.md
Normal file
@ -0,0 +1,35 @@
|
||||
# MoexClientService Split
|
||||
|
||||
## Цель
|
||||
|
||||
Разделить God Service `MoexClientService` на инфраструктурный слой и набор доменных клиентов, устранив нарушение SRP и улучшив тестируемость.
|
||||
|
||||
## Требования
|
||||
|
||||
1. `MoexHttpClient` — выделить инфраструктуру (axios, PQueue, circuit breaker, `request()`, `extractTable()`)
|
||||
2. Доменные клиенты — по одному на группу MOEX-запросов:
|
||||
- `MoexSecuritiesClient` — поиск и описание ценных бумаг
|
||||
- `MoexMarketDataClient` — рыночные данные акций и облигаций
|
||||
- `MoexCandlesClient` — свечи
|
||||
- `MoexHistoryClient` — история торгов
|
||||
- `MoexDividendsClient` — дивиденды
|
||||
3. Убрать `@Global()` — каждый потребитель явно импортирует `MoexClientModule`
|
||||
4. `MoexHttpClient` не экспортируется из модуля (внутренняя деталь)
|
||||
5. Все MOEX-типы остаются в `moex-client.types.ts`
|
||||
6. API-контракт всех потребителей не меняется — только DI
|
||||
|
||||
## Ограничения
|
||||
|
||||
- Один коммит на всю миграцию (нельзя половинчатое состояние)
|
||||
- Не менять сигнатуры публичных методов — только перенос кода
|
||||
- Не менять типы в `moex-client.types.ts`
|
||||
- Каждое изменение через TDD-цикл
|
||||
|
||||
## Критерии приемки (Acceptance Criteria)
|
||||
|
||||
- [ ] `MoexClientService` удалён, все 11 методов распределены по 5 доменным клиентам
|
||||
- [ ] `MoexHttpClient` содержит rate limiter + circuit breaker
|
||||
- [ ] `@Global()` убран с `MoexClientModule`
|
||||
- [ ] Все 7 потребителей обновлены: явный импорт модуля + новые DI
|
||||
- [ ] Все тесты проходят (существующие обновлены)
|
||||
- [ ] `npm run build` успешен
|
||||
39
docs/features/moex-client-split/tasks.md
Normal file
39
docs/features/moex-client-split/tasks.md
Normal file
@ -0,0 +1,39 @@
|
||||
# MoexClientService Split — Tasks
|
||||
|
||||
## Этап 1: Создание инфраструктурного клиента
|
||||
|
||||
- [ ] 1.1 Создать `moex-http.client.ts` — перенести `request()`, `extractTable()`, circuit breaker, PQueue из `MoexClientService`
|
||||
- [ ] 1.2 Написать unit-тесты для `MoexHttpClient`
|
||||
- [ ] 1.3 Написать тест на circuit breaker (threshold → open → reset)
|
||||
|
||||
## Этап 2: Создание доменных клиентов
|
||||
|
||||
- [ ] 2.1 `MoexSecuritiesClient` — `searchSecurities()`, `getSecurityDescription()`
|
||||
- [ ] 2.2 `MoexMarketDataClient` — `getShareMarketData()`, `getShareMarketDataBatch()`, `getBondData()`, `getBondMarketData()`, `getBondPositionDataBatch()`
|
||||
- [ ] 2.3 `MoexCandlesClient` — `getCandles()`
|
||||
- [ ] 2.4 `MoexHistoryClient` — `getHistory()`, `getBondHistory()`
|
||||
- [ ] 2.5 `MoexDividendsClient` — `getDividends()`
|
||||
- [ ] 2.6 Написать unit-тесты для каждого доменного клиента (mocked http client)
|
||||
|
||||
## Этап 3: Обновление модуля
|
||||
|
||||
- [ ] 3.1 Убрать `@Global()` из `MoexClientModule`
|
||||
- [ ] 3.2 Добавить новые клиенты в providers/exports
|
||||
- [ ] 3.3 Убрать старый `MoexClientService` из providers
|
||||
- [ ] 3.4 Проверить, что `MoexHttpClient` не экспортируется
|
||||
|
||||
## Этап 4: Миграция потребителей
|
||||
|
||||
- [ ] 4.1 `shares/` — обновить модуль, сервис, тесты
|
||||
- [ ] 4.2 `bonds/` — обновить модуль, сервис, тесты
|
||||
- [ ] 4.3 `candles/` — обновить модуль, сервис, тесты
|
||||
- [ ] 4.4 `securities/` — обновить модуль, securities.service, screener.service, тесты
|
||||
- [ ] 4.5 `portfolio/` — обновить модуль, сервис, тесты
|
||||
- [ ] 4.6 `tbank/` — обновить broker-events.service, тесты
|
||||
|
||||
## Этап 5: Финализация
|
||||
|
||||
- [ ] 5.1 Удалить старый `moex-client.service.ts`
|
||||
- [ ] 5.2 `npm run build` успешен
|
||||
- [ ] 5.3 Все тесты проходят
|
||||
- [ ] 5.4 Обновить `docs/features/backend-architecture-improvements/tasks.md` — отметить Iteration 6 как выполненную
|
||||
Loading…
x
Reference in New Issue
Block a user