6375 lines
155 KiB
Markdown
6375 lines
155 KiB
Markdown
# Build 060.19 — Trade Recovery Architecture
|
||
|
||
**Статус:** Architecture Specification
|
||
**Build:** 060.19
|
||
**Ветка:** Trades Feed (Time & Sales)
|
||
**Документ:** `build_060_19_architecture.md`
|
||
**Связанные документы:** `build_060_19.md` — план реализации данного Build.
|
||
**Предыдущий Build:** Build 060.18 — Trade Stream Consistency Controller.
|
||
|
||
---
|
||
|
||
# Назначение документа
|
||
|
||
Настоящий документ является официальной архитектурной спецификацией Build 060.19 и определяет построение подсистемы восстановления потока сделок (**Trade Recovery**).
|
||
|
||
Документ фиксирует все архитектурные решения, принятые до начала реализации, и является единственным источником истины (Single Source of Truth) при разработке данного Build.
|
||
|
||
Все решения, описанные ниже, считаются утверждёнными до начала реализации и не должны изменяться в процессе написания кода без подготовки нового ADR.
|
||
|
||
Настоящий документ подготовлен на основании:
|
||
|
||
- архитектуры предыдущих Build серии 060;
|
||
- экспериментального исследования REST API биржи;
|
||
- полного аудита существующей реализации Acquisition Pipeline;
|
||
- анализа существующих транспортных адаптеров, Feed, Handler, Runtime и Consistency Layer.
|
||
|
||
Никакие положения настоящего документа не основаны на предположениях о поведении биржи. Все архитектурные решения принимаются исключительно на основании подтверждённого поведения существующей системы и экспериментально проверенного контракта REST API.
|
||
|
||
---
|
||
|
||
# Статус Build
|
||
|
||
Build 060.19 является непосредственным продолжением Build 060.18.
|
||
|
||
К моменту начала данного Build в системе уже существуют:
|
||
|
||
- Canonical Trade Model;
|
||
- Parser;
|
||
- Mapper;
|
||
- Value Validation;
|
||
- REST Adapter;
|
||
- WebSocket Adapter;
|
||
- Trades Feed;
|
||
- Trades Handler;
|
||
- Trade Stream Consistency Controller;
|
||
- Canonical Trade Stream.
|
||
|
||
Таким образом, система уже умеет:
|
||
|
||
- получать сделки через REST;
|
||
- получать сделки через WebSocket;
|
||
- преобразовывать транспортные структуры в Canonical Trade;
|
||
- формировать согласованный поток сделок.
|
||
|
||
Однако система ещё не умеет восстанавливать пропущенный участок истории после временной потери WebSocket-потока.
|
||
|
||
Именно эту задачу решает Build 060.19.
|
||
|
||
---
|
||
|
||
# Контекст
|
||
|
||
После завершения Build 060.18 система гарантирует согласованность каждой опубликованной сделки.
|
||
|
||
Однако согласованность ещё не означает полноту потока.
|
||
|
||
Рассмотрим пример.
|
||
|
||
```text
|
||
Trade #100
|
||
|
||
Trade #101
|
||
|
||
(WebSocket отключился)
|
||
|
||
Trade #110
|
||
```
|
||
|
||
С точки зрения Build 060.18:
|
||
|
||
- Trade #110 является корректной;
|
||
- порядок не нарушен;
|
||
- конфликтующих повторов нет.
|
||
|
||
Следовательно, сделка будет успешно опубликована.
|
||
|
||
Однако фактически поток потерял сделки:
|
||
|
||
```text
|
||
102
|
||
|
||
103
|
||
|
||
104
|
||
|
||
105
|
||
|
||
106
|
||
|
||
107
|
||
|
||
108
|
||
|
||
109
|
||
```
|
||
|
||
Следовательно, после появления Canonical Trade Stream возникает новая архитектурная задача.
|
||
|
||
Необходимо восстановить отсутствующий участок истории до продолжения нормальной обработки WebSocket.
|
||
|
||
Именно этот механизм вводится Build 060.19.
|
||
|
||
---
|
||
|
||
# Предпосылки
|
||
|
||
Настоящий Build опирается на архитектурные решения предыдущих Build серии 060.
|
||
|
||
## Build 057
|
||
|
||
Определены фундаментальные принципы Acquisition Layer.
|
||
|
||
Разделены транспортный и доменный уровни.
|
||
|
||
---
|
||
|
||
## Build 060.1
|
||
|
||
Построена Canonical Trade Model.
|
||
|
||
Все транспортные источники приводятся к единому объекту `Trade`.
|
||
|
||
---
|
||
|
||
## Build 060.4
|
||
|
||
Завершено разделение Parser и Mapper.
|
||
|
||
Parser отвечает исключительно за транспортный уровень.
|
||
|
||
Mapper отвечает исключительно за построение Domain Model.
|
||
|
||
---
|
||
|
||
## Build 060.9
|
||
|
||
Построена система Value Validation.
|
||
|
||
Корректность отдельных значений гарантируется до создания объекта `Trade`.
|
||
|
||
---
|
||
|
||
## Build 060.11
|
||
|
||
Определены транспортные адаптеры REST и WebSocket.
|
||
|
||
---
|
||
|
||
## Build 060.17
|
||
|
||
Построен Trades Feed.
|
||
|
||
Получение сделок полностью функционирует.
|
||
|
||
---
|
||
|
||
## Build 060.18
|
||
|
||
Построен Canonical Trade Stream.
|
||
|
||
Введён `TradeStreamConsistencyController`, обеспечивающий:
|
||
|
||
- дедупликацию;
|
||
- контроль порядка;
|
||
- обнаружение конфликтующих дублей;
|
||
- формирование единственного согласованного потока сделок.
|
||
|
||
Build 060.19 обязан использовать данный механизм без изменения его архитектуры.
|
||
|
||
Recovery не имеет права реализовывать:
|
||
|
||
- собственную дедупликацию;
|
||
- собственную проверку порядка;
|
||
- собственную модель Trade;
|
||
- собственный поток сделок.
|
||
|
||
---
|
||
|
||
# Проблема
|
||
|
||
После потери WebSocket-соединения поток сделок становится неполным.
|
||
|
||
При этом существующий Consistency Controller не способен самостоятельно восстановить отсутствующий участок истории.
|
||
|
||
Это не является недостатком Build 060.18.
|
||
|
||
Напротив, Build 060.18 сознательно ограничивает собственную ответственность исключительно проверкой согласованности уже поступающих сделок.
|
||
|
||
Следовательно, необходим отдельный компонент, который сможет:
|
||
|
||
- получить исторические сделки через REST;
|
||
- преобразовать их в Canonical Trade;
|
||
- безопасно объединить их с текущим потоком;
|
||
- использовать уже существующий механизм проверки согласованности.
|
||
|
||
Такой компонент должен быть полностью независимым от Runtime, WebSocket Transport и транспортного уровня биржи.
|
||
|
||
Именно таким компонентом становится Trade Recovery.
|
||
|
||
---
|
||
|
||
# Основная идея Build
|
||
|
||
Главная идея Build заключается в строгом разделении двух независимых задач.
|
||
|
||
Первая задача:
|
||
|
||
```text
|
||
Получение исторических сделок.
|
||
```
|
||
|
||
Вторая задача:
|
||
|
||
```text
|
||
Проверка согласованности потока.
|
||
```
|
||
|
||
Получение истории не должно знать, каким образом выполняется дедупликация.
|
||
|
||
Контроллер согласованности, в свою очередь, не должен знать, каким способом были получены сделки.
|
||
|
||
Следовательно, Recovery становится исключительно связующим звеном между существующим REST Pipeline и существующим Stream Consistency Layer.
|
||
|
||
Он не изменяет их архитектуру и не дублирует их функциональность.
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
Build обязан обеспечить безопасное восстановление отсутствующего участка истории торгов без нарушения уже существующих архитектурных инвариантов.
|
||
|
||
После завершения Build система должна обладать следующими свойствами.
|
||
|
||
- Recovery использует существующий REST Pipeline.
|
||
- Recovery использует существующий TradeStreamConsistencyController.
|
||
- Recovery не создаёт собственную модель Trade.
|
||
- Recovery не создаёт собственную модель потока.
|
||
- Recovery не выполняет собственную дедупликацию.
|
||
- Recovery не реализует собственные правила Ordering.
|
||
- Recovery не зависит от Runtime.
|
||
- Recovery не зависит от WebSocket Transport.
|
||
- Recovery не зависит от механизма Reconnect.
|
||
|
||
Таким образом Recovery становится полностью автономным компонентом Acquisition Layer.
|
||
|
||
---
|
||
|
||
# Что НЕ входит в Scope Build
|
||
|
||
Настоящий Build сознательно НЕ реализует:
|
||
|
||
- автоматическое обнаружение потери WebSocket;
|
||
- обнаружение Gap;
|
||
- принятие решения о запуске Recovery;
|
||
- управление переподключением;
|
||
- Runtime Integration;
|
||
- Composition Root;
|
||
- сохранение состояния между перезапусками;
|
||
- журналирование Recovery;
|
||
- Retry Policy;
|
||
- стратегию многократного Backfill;
|
||
- телеметрию;
|
||
- метрики.
|
||
|
||
Все перечисленные задачи относятся к следующим Build серии 060.
|
||
|
||
Build 060.19 реализует исключительно механизм безопасного восстановления исторических сделок по уже подготовленному диапазону времени.
|
||
|
||
---
|
||
|
||
# Архитектурные принципы
|
||
|
||
При реализации Build 060.19 используются следующие фундаментальные принципы.
|
||
|
||
---
|
||
|
||
## 1. Canonical First
|
||
|
||
Recovery никогда не работает с транспортными структурами данных.
|
||
|
||
Любая информация, полученная через REST API, должна пройти существующий Pipeline:
|
||
|
||
```text
|
||
REST Document
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Canonical Trade
|
||
```
|
||
|
||
Только после появления объекта `Trade` начинается работа Recovery.
|
||
|
||
Recovery никогда не анализирует JSON-документ REST напрямую.
|
||
|
||
---
|
||
|
||
## 2. Existing Pipeline Reuse
|
||
|
||
Build 060.19 не создаёт собственный REST Pipeline.
|
||
|
||
Recovery полностью переиспользует существующие компоненты:
|
||
|
||
- REST Transport;
|
||
- REST Parser;
|
||
- REST Value Validation;
|
||
- REST Mapper;
|
||
- REST Adapter.
|
||
|
||
Во время проектирования была рассмотрена возможность реализации отдельного Recovery Adapter.
|
||
|
||
Данный вариант отклонён.
|
||
|
||
Причина:
|
||
|
||
это привело бы к появлению двух различных способов преобразования одного и того же REST-документа в Canonical Trade, что нарушило бы принцип единственной модели преобразования данных.
|
||
|
||
---
|
||
|
||
## 3. Single Domain Model
|
||
|
||
Во всей системе продолжает существовать единственная доменная модель сделки:
|
||
|
||
```python
|
||
Trade
|
||
```
|
||
|
||
Recovery не имеет собственной модели сделки.
|
||
|
||
Recovery не создаёт дополнительных DTO доменного уровня.
|
||
|
||
Все операции выполняются исключительно над существующим объектом `Trade`.
|
||
|
||
---
|
||
|
||
## 4. Existing Consistency First
|
||
|
||
Recovery никогда самостоятельно не принимает решения о корректности сделки.
|
||
|
||
После нормализации последовательности каждая сделка обязательно передаётся в существующий:
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
Именно данный компонент остаётся единственным владельцем правил:
|
||
|
||
- Ordering;
|
||
- Deduplication;
|
||
- Conflict Detection.
|
||
|
||
Recovery не имеет права дублировать эти проверки.
|
||
|
||
---
|
||
|
||
## 5. Stateless Recovery
|
||
|
||
Recovery не хранит собственного состояния потока.
|
||
|
||
Он не знает:
|
||
|
||
- последний обработанный trade_id;
|
||
- последний опубликованный Trade;
|
||
- состояние дедупликации;
|
||
- историю уже обработанных сделок.
|
||
|
||
Recovery является stateless-компонентом.
|
||
|
||
Всё состояние системы по-прежнему принадлежит только Build 060.18.
|
||
|
||
---
|
||
|
||
## 6. Runtime Independence
|
||
|
||
Recovery не зависит от Runtime.
|
||
|
||
Он не знает:
|
||
|
||
- произошло ли переподключение;
|
||
- был ли разрыв WebSocket;
|
||
- почему потребовалось восстановление;
|
||
- сколько времени отсутствовало соединение.
|
||
|
||
Recovery получает уже сформированный запрос восстановления и выполняет только его.
|
||
|
||
---
|
||
|
||
## 7. Time-Based Recovery
|
||
|
||
Recovery использует исключительно временные диапазоны.
|
||
|
||
Во время проектирования были рассмотрены различные варианты построения курсора восстановления.
|
||
|
||
Использование `trade_id` было отклонено после экспериментального исследования REST API.
|
||
|
||
Официальным механизмом навигации Build 060.19 являются:
|
||
|
||
```text
|
||
start_time
|
||
|
||
↓
|
||
|
||
end_time
|
||
```
|
||
|
||
Других типов курсоров Build не предусматривает.
|
||
|
||
---
|
||
|
||
## 8. One Recovery Direction
|
||
|
||
REST API возвращает сделки в порядке:
|
||
|
||
```text
|
||
DESCENDING
|
||
```
|
||
|
||
Однако Canonical Trade Stream существует исключительно в порядке:
|
||
|
||
```text
|
||
ASCENDING
|
||
```
|
||
|
||
Следовательно Build 060.19 вводит обязательный этап нормализации порядка.
|
||
|
||
Никакая сделка не может быть передана в Consistency Layer до завершения данной нормализации.
|
||
|
||
---
|
||
|
||
## 9. Separation Of Responsibilities
|
||
|
||
Recovery отвечает исключительно за:
|
||
|
||
- получение истории;
|
||
- нормализацию порядка;
|
||
- передачу сделок в Consistency Layer.
|
||
|
||
Recovery сознательно не отвечает за:
|
||
|
||
- принятие решения о запуске;
|
||
- вычисление диапазона восстановления;
|
||
- повторные попытки;
|
||
- работу WebSocket;
|
||
- Runtime Integration.
|
||
|
||
Все перечисленные обязанности относятся к следующим Build серии 060.
|
||
|
||
---
|
||
|
||
## 10. Unique File Naming
|
||
|
||
Во всём проекте Dzentra продолжает действовать правило уникальности имён файлов.
|
||
|
||
Новые файлы Build 060.19 обязаны иметь уникальные имена во всём репозитории.
|
||
|
||
Допускается единственное исключение:
|
||
|
||
```text
|
||
__init__.py
|
||
```
|
||
|
||
Использование общих имён файлов запрещается.
|
||
|
||
Например, не допускаются:
|
||
|
||
```text
|
||
controller.py
|
||
|
||
request.py
|
||
|
||
normalizer.py
|
||
|
||
exceptions.py
|
||
```
|
||
|
||
Файлы должны отражать своё назначение.
|
||
|
||
Например:
|
||
|
||
```text
|
||
trade_recovery_controller.py
|
||
|
||
trade_recovery_request.py
|
||
|
||
trade_recovery_normalizer.py
|
||
|
||
trade_recovery_protocol.py
|
||
|
||
trade_recovery_exceptions.py
|
||
```
|
||
|
||
---
|
||
|
||
# Архитектурный фундамент Recovery
|
||
|
||
После завершения Build 060.18 система получила понятие:
|
||
|
||
```text
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
Build 060.19 не изменяет данную модель.
|
||
|
||
Вместо этого появляется новая независимая архитектурная сущность.
|
||
|
||
```text
|
||
Trade Recovery
|
||
```
|
||
|
||
Recovery не становится частью Stream Consistency.
|
||
|
||
Recovery располагается перед ним.
|
||
|
||
Архитектурно система принимает следующий вид.
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Trade Recovery
|
||
|
||
↓
|
||
|
||
Trade Stream Consistency
|
||
|
||
↓
|
||
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
Таким образом Build 060.19 не расширяет обязанности Consistency Controller.
|
||
|
||
Он вводит новый независимый уровень Acquisition Pipeline.
|
||
|
||
---
|
||
|
||
# Место Recovery в Acquisition Pipeline
|
||
|
||
До Build 060.19 существовала следующая архитектурная схема.
|
||
|
||
```text
|
||
REST / WebSocket
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Trade
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
↓
|
||
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
После завершения Build 060.19 появляется дополнительный путь обработки исторических сделок.
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Trade Recovery
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
↓
|
||
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
При этом путь обработки WebSocket-сделок остаётся неизменным.
|
||
|
||
Recovery является дополнительной веткой Acquisition Pipeline и не изменяет существующую архитектуру получения потоковых сделок.
|
||
|
||
---
|
||
|
||
# Экспериментальное исследование REST API
|
||
|
||
Перед проектированием Recovery было выполнено отдельное инженерное исследование поведения REST API биржи.
|
||
|
||
Целью исследования являлось подтверждение фактического поведения endpoint получения исторических сделок и исключение архитектурных решений, основанных исключительно на документации биржи.
|
||
|
||
Исследование выполнялось специализированным диагностическим инструментом:
|
||
|
||
```text
|
||
check_trade_backfill_api_final_test.py
|
||
```
|
||
|
||
Все архитектурные решения настоящего Build принимаются исключительно на основании подтверждённого поведения API.
|
||
|
||
---
|
||
|
||
# Подтверждённый контракт REST API
|
||
|
||
Исследование подтвердило следующие свойства endpoint:
|
||
|
||
```text
|
||
GET /api/v2/aggTrades
|
||
```
|
||
|
||
Все перечисленные ниже свойства считаются частью архитектурного контракта Build 060.19.
|
||
|
||
---
|
||
|
||
# Endpoint
|
||
|
||
Экспериментально подтверждено:
|
||
|
||
- endpoint доступен;
|
||
- endpoint стабилен;
|
||
- endpoint детерминирован;
|
||
- повторные запросы возвращают предсказуемый результат.
|
||
|
||
Recovery полностью опирается на данный контракт.
|
||
|
||
---
|
||
|
||
# Порядок выдачи
|
||
|
||
REST API возвращает сделки в порядке:
|
||
|
||
```text
|
||
DESCENDING
|
||
```
|
||
|
||
то есть
|
||
|
||
```text
|
||
newest
|
||
|
||
↓
|
||
|
||
oldest
|
||
```
|
||
|
||
Это фундаментальное свойство Build.
|
||
|
||
Recovery никогда не имеет права передавать данный поток непосредственно в Stream Consistency.
|
||
|
||
Перед публикацией последовательность обязательно нормализуется.
|
||
|
||
```text
|
||
REST
|
||
|
||
DESCENDING
|
||
|
||
↓
|
||
|
||
Recovery Normalizer
|
||
|
||
ASCENDING
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
---
|
||
|
||
# Timestamp
|
||
|
||
Экспериментально подтверждено:
|
||
|
||
при уменьшении `trade_id`
|
||
|
||
уменьшается
|
||
|
||
```text
|
||
executed_at
|
||
```
|
||
|
||
Следовательно:
|
||
|
||
- timestamp согласован с порядком выдачи;
|
||
- дополнительная сортировка по времени не требуется.
|
||
|
||
---
|
||
|
||
# Limit
|
||
|
||
Подтверждены следующие ограничения.
|
||
|
||
Корректно работают значения:
|
||
|
||
```text
|
||
1
|
||
|
||
...
|
||
|
||
1000
|
||
```
|
||
|
||
Запрос
|
||
|
||
```text
|
||
limit = 1001
|
||
```
|
||
|
||
возвращает
|
||
|
||
```text
|
||
HTTP 400
|
||
```
|
||
|
||
Следовательно Build фиксирует официальный диапазон.
|
||
|
||
```text
|
||
1 <= limit <= 1000
|
||
```
|
||
|
||
Recovery не имеет права нарушать данный инвариант.
|
||
|
||
---
|
||
|
||
# Time Filters
|
||
|
||
Экспериментально подтверждена корректная работа параметров:
|
||
|
||
```text
|
||
startTime
|
||
```
|
||
|
||
```text
|
||
endTime
|
||
```
|
||
|
||
а также их совместного использования.
|
||
|
||
Recovery использует исключительно временные диапазоны.
|
||
|
||
Поддержка навигации по времени считается официальной частью архитектурного контракта.
|
||
|
||
---
|
||
|
||
# Ограничение временного диапазона
|
||
|
||
При одновременном использовании:
|
||
|
||
```text
|
||
startTime
|
||
|
||
+
|
||
|
||
endTime
|
||
```
|
||
|
||
биржа требует, чтобы длина диапазона была меньше одного часа.
|
||
|
||
Следовательно Recovery принимает следующий инвариант.
|
||
|
||
```text
|
||
end_time > start_time
|
||
```
|
||
|
||
и
|
||
|
||
```text
|
||
end_time - start_time < 1 hour
|
||
```
|
||
|
||
Нарушение данного условия считается ошибкой формирования Recovery Request.
|
||
|
||
---
|
||
|
||
# fromId
|
||
|
||
Во время исследования была проведена серия диагностических тестов параметра:
|
||
|
||
```text
|
||
fromId
|
||
```
|
||
|
||
Исследовались:
|
||
|
||
- исторические значения;
|
||
- глубокие исторические значения;
|
||
- отрицательные значения;
|
||
- будущие значения.
|
||
|
||
Во всех случаях подтверждена одинаковая картина.
|
||
|
||
REST продолжает возвращать последнюю страницу сделок.
|
||
|
||
Следовательно использование `fromId` как курсора восстановления экспериментально не подтверждено.
|
||
|
||
Build 060.19 полностью исключает любую зависимость от данного параметра.
|
||
|
||
---
|
||
|
||
# Trade IDs
|
||
|
||
Экспериментально подтверждено:
|
||
|
||
`trade_id`
|
||
|
||
не образует арифметически непрерывную последовательность.
|
||
|
||
Например:
|
||
|
||
```text
|
||
100
|
||
|
||
101
|
||
|
||
118
|
||
|
||
141
|
||
|
||
205
|
||
```
|
||
|
||
является полностью корректной последовательностью.
|
||
|
||
Следовательно Recovery никогда не делает предположений вида:
|
||
|
||
```text
|
||
next_trade_id = previous_trade_id + 1
|
||
```
|
||
|
||
Пропуски идентификаторов сами по себе не являются ошибкой.
|
||
|
||
---
|
||
|
||
# Repeatability
|
||
|
||
Повторный запрос одного и того же диапазона времени возвращает одинаковую последовательность `trade_id`.
|
||
|
||
Это означает:
|
||
|
||
- результаты детерминированы;
|
||
- повторное получение диапазона безопасно;
|
||
- Recovery может повторно использовать один и тот же диапазон без риска появления новых сделок внутри уже завершённого исторического окна.
|
||
|
||
---
|
||
|
||
# Выводы исследования
|
||
|
||
На основании проведённого исследования Build принимает следующие архитектурные решения.
|
||
|
||
Recovery:
|
||
|
||
- не использует `fromId`;
|
||
- использует только временные диапазоны;
|
||
- никогда не предполагает непрерывность `trade_id`;
|
||
- всегда нормализует порядок выдачи;
|
||
- полностью доверяет существующему REST Pipeline;
|
||
- не выполняет дополнительную сортировку по времени.
|
||
|
||
Все перечисленные положения считаются официальными архитектурными инвариантами Build 060.19.
|
||
|
||
---
|
||
|
||
# Аудит существующей реализации
|
||
|
||
После завершения исследования REST API был выполнен полный аудит существующей реализации Acquisition Layer.
|
||
|
||
Цель аудита состояла в определении минимального объёма изменений, необходимых для реализации Recovery без нарушения уже существующей архитектуры.
|
||
|
||
В ходе аудита были исследованы:
|
||
|
||
- REST Source;
|
||
- REST Adapter;
|
||
- Parser;
|
||
- Mapper;
|
||
- Value Validation;
|
||
- Trades Feed;
|
||
- Trades Handler;
|
||
- Trade Stream Consistency;
|
||
- Runtime;
|
||
- Subscription Layer;
|
||
- WebSocket Protocol.
|
||
|
||
Результаты аудита определяют архитектурные границы настоящего Build.
|
||
|
||
---
|
||
|
||
# Вывод №1
|
||
|
||
REST Pipeline уже полностью реализован.
|
||
|
||
Существующий транспортный источник поддерживает получение исторических сделок по временным диапазонам.
|
||
|
||
Build 060.19 не создаёт нового REST клиента.
|
||
|
||
---
|
||
|
||
# Вывод №2
|
||
|
||
REST Adapter уже полностью реализован.
|
||
|
||
Pipeline:
|
||
|
||
```text
|
||
REST Document
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Trade
|
||
```
|
||
|
||
уже существует.
|
||
|
||
Recovery полностью переиспользует данный Pipeline.
|
||
|
||
Создание второго Adapter запрещается.
|
||
|
||
---
|
||
|
||
# Вывод №3
|
||
|
||
Canonical Trade полностью соответствует требованиям Recovery.
|
||
|
||
Build не изменяет:
|
||
|
||
- модель Trade;
|
||
- Value Validation;
|
||
- Parser;
|
||
- Mapper.
|
||
|
||
Recovery использует существующий доменный объект без каких-либо изменений.
|
||
|
||
---
|
||
|
||
# Вывод №4
|
||
|
||
TradeStreamConsistencyController полностью готов к интеграции.
|
||
|
||
Recovery обязан использовать существующий публичный контракт:
|
||
|
||
```python
|
||
accept(trade: Trade) -> Trade | None
|
||
```
|
||
|
||
Никаких дополнительных методов Controller Build 060.19 не требует.
|
||
|
||
---
|
||
|
||
# Вывод №5
|
||
|
||
Trades Feed не требует изменений архитектуры.
|
||
|
||
Recovery не интегрируется непосредственно в Feed.
|
||
|
||
Интеграция с Runtime будет реализована отдельным Build.
|
||
|
||
Следовательно существующая архитектура Feed остаётся неизменной.
|
||
|
||
---
|
||
|
||
# Вывод №6
|
||
|
||
Trades Handler не требует изменения ответственности.
|
||
|
||
Во время аудита подтверждено, что Handler уже выполняет исключительно транспортные обязанности:
|
||
|
||
- проверку структуры входящего документа;
|
||
- вызов существующего Adapter;
|
||
- получение канонических объектов `Trade`.
|
||
|
||
Recovery не переносит в Handler никакой дополнительной логики.
|
||
|
||
Handler остаётся полностью stateless.
|
||
|
||
---
|
||
|
||
# Вывод №7
|
||
|
||
Runtime уже содержит архитектурные сущности, необходимые для последующей интеграции Recovery.
|
||
|
||
Во время аудита обнаружены отдельные Runtime-компоненты:
|
||
|
||
- Runtime Events;
|
||
- Runtime Commands;
|
||
- Subscription Layer;
|
||
- Runtime Protocol.
|
||
|
||
Однако Build 060.19 сознательно не использует их напрямую.
|
||
|
||
Recovery остаётся полностью независимым компонентом.
|
||
|
||
Интеграция с Runtime переносится на последующие Build серии 060.
|
||
|
||
---
|
||
|
||
# Вывод №8
|
||
|
||
Recovery не отвечает за управление подписками.
|
||
|
||
Во время аудита подтверждено, что существующий Subscription Layer уже определяет архитектуру подписок WebSocket.
|
||
|
||
Следовательно Recovery не:
|
||
|
||
- создаёт подписки;
|
||
- восстанавливает подписки;
|
||
- отслеживает подписки;
|
||
- управляет жизненным циклом подписок.
|
||
|
||
Данные обязанности принадлежат Runtime Layer.
|
||
|
||
---
|
||
|
||
# Вывод №9
|
||
|
||
Recovery не зависит от WebSocket Transport.
|
||
|
||
Во время аудита подтверждено, что существующий WebSocket Protocol определяет исключительно транспортные контракты.
|
||
|
||
Recovery никогда не работает с:
|
||
|
||
- WebSocket Message;
|
||
- Subscription Event;
|
||
- Transport DTO.
|
||
|
||
Recovery получает уже готовый запрос восстановления.
|
||
|
||
Таким образом между Recovery и WebSocket отсутствует прямая зависимость.
|
||
|
||
---
|
||
|
||
# Вывод №10
|
||
|
||
Существующий Runtime ещё не содержит реализации Recovery.
|
||
|
||
Во время аудита подтверждено, что Build 060.19 может быть реализован как полностью автономная подсистема.
|
||
|
||
Это позволяет:
|
||
|
||
- реализовать Recovery;
|
||
- полностью протестировать его;
|
||
- завершить Build;
|
||
|
||
до появления Runtime Integration.
|
||
|
||
Данное решение существенно уменьшает связанность системы.
|
||
|
||
---
|
||
|
||
# Архитектура Recovery
|
||
|
||
После завершения аудита необходимо определить архитектурную модель Recovery.
|
||
|
||
Build вводит новую внутреннюю подсистему Acquisition Layer.
|
||
|
||
Она состоит из нескольких независимых компонентов.
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
Все перечисленные компоненты относятся исключительно к Build 060.19.
|
||
|
||
Никакие существующие подсистемы не изменяют собственную ответственность.
|
||
|
||
---
|
||
|
||
# Общая архитектурная схема
|
||
|
||
Полный путь восстановления исторических сделок выглядит следующим образом.
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Document
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ASCENDING Trade Stream
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Данная схема становится официальной архитектурой Recovery Pipeline.
|
||
|
||
---
|
||
|
||
# Граница Recovery
|
||
|
||
Recovery располагается между существующим REST Adapter и существующим Stream Consistency.
|
||
|
||
До Recovery существует только набор независимых объектов Trade.
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Trade
|
||
```
|
||
|
||
После Recovery появляется уже согласованный поток.
|
||
|
||
```text
|
||
Trade
|
||
|
||
↓
|
||
|
||
Recovery
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
↓
|
||
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
Recovery никогда не публикует сделки самостоятельно.
|
||
|
||
Публикация возможна только после успешного прохождения Stream Consistency.
|
||
|
||
---
|
||
|
||
# TradeRecoveryController
|
||
|
||
## Назначение
|
||
|
||
TradeRecoveryController является центральным компонентом Build 060.19.
|
||
|
||
Именно он управляет полным процессом восстановления исторического диапазона.
|
||
|
||
Контроллер отвечает исключительно за оркестрацию существующих компонентов.
|
||
|
||
Он не реализует:
|
||
|
||
- транспорт;
|
||
- парсинг;
|
||
- дедупликацию;
|
||
- Ordering;
|
||
- Runtime.
|
||
|
||
---
|
||
|
||
# Ответственность Controller
|
||
|
||
TradeRecoveryController отвечает за:
|
||
|
||
- получение Recovery Request;
|
||
- вызов существующего REST Source;
|
||
- вызов существующего REST Adapter;
|
||
- передачу полученного потока в Recovery Normalizer;
|
||
- последовательную передачу сделок в Stream Consistency;
|
||
- формирование итогового результата восстановления.
|
||
|
||
---
|
||
|
||
# Controller НЕ отвечает
|
||
|
||
TradeRecoveryController сознательно не отвечает за:
|
||
|
||
- вычисление диапазона времени;
|
||
- выбор момента запуска;
|
||
- Retry Policy;
|
||
- обнаружение Gap;
|
||
- работу WebSocket;
|
||
- управление подписками;
|
||
- Runtime Integration;
|
||
- хранение состояния.
|
||
|
||
Все перечисленные обязанности относятся к следующим Build.
|
||
|
||
---
|
||
|
||
# Главный принцип Controller
|
||
|
||
TradeRecoveryController не принимает доменных решений.
|
||
|
||
Все решения уже принадлежат существующим компонентам системы.
|
||
|
||
Контроллер лишь организует их совместную работу.
|
||
|
||
Именно поэтому RecoveryController рассматривается как orchestration component, а не как Domain Service.
|
||
|
||
---
|
||
|
||
# TradeRecoveryRequest
|
||
|
||
## Назначение
|
||
|
||
Recovery выполняется исключительно по заранее подготовленному запросу.
|
||
|
||
Build сознательно разделяет:
|
||
|
||
- вычисление диапазона;
|
||
- выполнение восстановления.
|
||
|
||
Следовательно Recovery получает готовый объект запроса.
|
||
|
||
---
|
||
|
||
# Содержимое Recovery Request
|
||
|
||
Минимальная модель запроса включает:
|
||
|
||
```text
|
||
symbol
|
||
|
||
start_time
|
||
|
||
end_time
|
||
|
||
limit
|
||
```
|
||
|
||
Этого достаточно для выполнения одного Recovery Pipeline.
|
||
|
||
Никакие дополнительные поля Build 060.19 не требует.
|
||
|
||
---
|
||
|
||
# Почему отсутствует last_processed_timestamp
|
||
|
||
Во время проектирования рассматривался вариант хранения внутри Recovery собственного курсора:
|
||
|
||
```text
|
||
last_processed_timestamp
|
||
```
|
||
|
||
Данный вариант отклонён.
|
||
|
||
Причины:
|
||
|
||
- появление внутреннего состояния;
|
||
- зависимость от Runtime;
|
||
- нарушение принципа Stateless Recovery;
|
||
- смешение ответственности Runtime и Recovery.
|
||
|
||
Recovery никогда самостоятельно не вычисляет диапазон восстановления.
|
||
|
||
Он лишь выполняет уже подготовленный запрос.
|
||
|
||
---
|
||
|
||
# TradeRecoveryRequest
|
||
|
||
## Инварианты
|
||
|
||
Каждый экземпляр `TradeRecoveryRequest` обязан удовлетворять следующим требованиям.
|
||
|
||
---
|
||
|
||
### Инвариант №1
|
||
|
||
Запрос относится ровно к одному торговому инструменту.
|
||
|
||
Например:
|
||
|
||
```text
|
||
BTCUSDT
|
||
```
|
||
|
||
Recovery никогда не выполняет восстановление нескольких символов одновременно.
|
||
|
||
---
|
||
|
||
### Инвариант №2
|
||
|
||
Временной диапазон обязан быть корректным.
|
||
|
||
Всегда должно выполняться условие:
|
||
|
||
```text
|
||
start_time < end_time
|
||
```
|
||
|
||
Нарушение данного условия считается ошибкой формирования Recovery Request.
|
||
|
||
---
|
||
|
||
### Инвариант №3
|
||
|
||
Продолжительность диапазона не должна превышать ограничение REST API.
|
||
|
||
```text
|
||
end_time - start_time < 1 hour
|
||
```
|
||
|
||
Recovery не выполняет автоматическое разбиение диапазона.
|
||
|
||
Данная задача относится к следующим Build.
|
||
|
||
---
|
||
|
||
### Инвариант №4
|
||
|
||
Поле `limit`, если оно указано, обязано удовлетворять диапазону:
|
||
|
||
```text
|
||
1 <= limit <= 1000
|
||
```
|
||
|
||
Recovery не корректирует ошибочные значения автоматически.
|
||
|
||
---
|
||
|
||
### Инвариант №5
|
||
|
||
Recovery Request является неизменяемым объектом.
|
||
|
||
После создания его содержимое никогда не изменяется.
|
||
|
||
---
|
||
|
||
# Почему используется готовый Request
|
||
|
||
Во время проектирования рассматривались несколько вариантов.
|
||
|
||
---
|
||
|
||
## Вариант 1
|
||
|
||
Передавать только:
|
||
|
||
```text
|
||
symbol
|
||
```
|
||
|
||
Отклонён.
|
||
|
||
Recovery пришлось бы самостоятельно вычислять диапазон восстановления.
|
||
|
||
Это нарушает разделение ответственности.
|
||
|
||
---
|
||
|
||
## Вариант 2
|
||
|
||
Передавать:
|
||
|
||
```text
|
||
last_trade_id
|
||
```
|
||
|
||
Отклонён.
|
||
|
||
Экспериментально подтверждено, что Recovery не должен строиться вокруг `trade_id`.
|
||
|
||
Кроме того, существующий REST API не предоставляет надёжного механизма навигации через `fromId`.
|
||
|
||
---
|
||
|
||
## Вариант 3
|
||
|
||
Передавать:
|
||
|
||
```text
|
||
start_time
|
||
|
||
end_time
|
||
|
||
limit
|
||
```
|
||
|
||
Принят.
|
||
|
||
Recovery полностью независим от Runtime и выполняет исключительно уже сформированный запрос.
|
||
|
||
---
|
||
|
||
# TradeRecoveryNormalizer
|
||
|
||
## Назначение
|
||
|
||
TradeRecoveryNormalizer является вторым компонентом Recovery Pipeline.
|
||
|
||
Его единственная задача —
|
||
|
||
привести последовательность сделок, полученную от REST API, к каноническому порядку публикации.
|
||
|
||
RecoveryNormalizer не анализирует содержимое сделок.
|
||
|
||
Он работает исключительно с их последовательностью.
|
||
|
||
---
|
||
|
||
# Почему необходим отдельный компонент
|
||
|
||
Во время проектирования рассматривалась возможность выполнения нормализации непосредственно внутри Controller.
|
||
|
||
Данный вариант был отклонён.
|
||
|
||
Причины:
|
||
|
||
- смешение обязанностей;
|
||
- ухудшение тестируемости;
|
||
- усложнение Controller;
|
||
- невозможность повторного использования алгоритма.
|
||
|
||
Разделение Controller и Normalizer соответствует общим архитектурным принципам серии Build 060.
|
||
|
||
---
|
||
|
||
# Ответственность Normalizer
|
||
|
||
TradeRecoveryNormalizer отвечает исключительно за:
|
||
|
||
- анализ направления последовательности;
|
||
- нормализацию порядка сделок;
|
||
- проверку корректности направления потока.
|
||
|
||
Никаких других обязанностей компонент не имеет.
|
||
|
||
---
|
||
|
||
# Normalizer НЕ отвечает
|
||
|
||
TradeRecoveryNormalizer сознательно не отвечает за:
|
||
|
||
- REST;
|
||
- Parser;
|
||
- Mapper;
|
||
- Value Validation;
|
||
- Deduplication;
|
||
- Ordering;
|
||
- Runtime;
|
||
- Recovery Request.
|
||
|
||
---
|
||
|
||
# Контракт Normalizer
|
||
|
||
Normalizer получает:
|
||
|
||
```text
|
||
tuple[Trade]
|
||
```
|
||
|
||
и возвращает:
|
||
|
||
```text
|
||
tuple[Trade]
|
||
```
|
||
|
||
Количество сделок никогда не изменяется.
|
||
|
||
Normalizer никогда:
|
||
|
||
- не удаляет сделки;
|
||
- не добавляет сделки;
|
||
- не модифицирует сделки.
|
||
|
||
Изменяется исключительно порядок их следования.
|
||
|
||
---
|
||
|
||
# Модель нормализации
|
||
|
||
Во время исследования REST API было подтверждено следующее поведение.
|
||
|
||
REST API возвращает сделки в порядке:
|
||
|
||
```text
|
||
DESCENDING
|
||
```
|
||
|
||
Следовательно Recovery обязан преобразовать поток в:
|
||
|
||
```text
|
||
ASCENDING
|
||
```
|
||
|
||
Только после этого сделки могут быть переданы в Stream Consistency.
|
||
|
||
---
|
||
|
||
# Допустимые варианты последовательности
|
||
|
||
Build 060.19 формально определяет допустимые варианты входной последовательности.
|
||
|
||
---
|
||
|
||
## Вариант №1
|
||
|
||
Последовательность уже является возрастающей.
|
||
|
||
Например:
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
|
||
140
|
||
```
|
||
|
||
В этом случае RecoveryNormalizer возвращает её без изменений.
|
||
|
||
---
|
||
|
||
## Вариант №2
|
||
|
||
Последовательность является убывающей.
|
||
|
||
Например:
|
||
|
||
```text
|
||
140
|
||
|
||
121
|
||
|
||
105
|
||
|
||
100
|
||
```
|
||
|
||
В этом случае выполняется нормализация.
|
||
|
||
Результат:
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
|
||
140
|
||
```
|
||
|
||
---
|
||
|
||
## Вариант №3
|
||
|
||
Последовательность имеет смешанный порядок.
|
||
|
||
Например:
|
||
|
||
```text
|
||
100
|
||
|
||
150
|
||
|
||
120
|
||
|
||
180
|
||
```
|
||
|
||
Такой поток считается архитектурно недопустимым.
|
||
|
||
Normalizer обязан завершить обработку ошибкой.
|
||
|
||
Никакие сделки не передаются в Stream Consistency.
|
||
|
||
---
|
||
|
||
# Почему запрещается смешанный порядок
|
||
|
||
Смешанная последовательность означает нарушение фундаментального контракта транспортного уровня.
|
||
|
||
Recovery не должен самостоятельно исправлять подобные ошибки.
|
||
|
||
Подобная ситуация рассматривается как нарушение архитектурных инвариантов Acquisition Layer.
|
||
|
||
Следовательно единственно допустимым поведением является генерация доменного исключения.
|
||
|
||
---
|
||
|
||
# Пустая последовательность
|
||
|
||
Если REST Pipeline возвращает:
|
||
|
||
```text
|
||
()
|
||
```
|
||
|
||
Normalizer возвращает ту же пустую последовательность.
|
||
|
||
Ошибки не возникает.
|
||
|
||
Это означает отсутствие сделок внутри указанного диапазона времени.
|
||
|
||
---
|
||
|
||
# Последовательность из одной сделки
|
||
|
||
Если получена единственная сделка:
|
||
|
||
```text
|
||
Trade
|
||
```
|
||
|
||
никакая нормализация не требуется.
|
||
|
||
Trade передаётся в Stream Consistency без изменений.
|
||
|
||
---
|
||
|
||
# Неизменяемость Trade
|
||
|
||
Во время нормализации запрещается изменять объект Trade.
|
||
|
||
Допускается изменение исключительно порядка следования элементов внутри коллекции.
|
||
|
||
Canonical Trade остаётся полностью immutable.
|
||
|
||
---
|
||
|
||
# Передача сделок в Stream Consistency
|
||
|
||
После завершения нормализации Recovery начинает публикацию сделок в существующий `TradeStreamConsistencyController`.
|
||
|
||
Передача выполняется строго последовательно.
|
||
|
||
Для каждой сделки вызывается существующий публичный контракт:
|
||
|
||
```python
|
||
accept(trade: Trade) -> Trade | None
|
||
```
|
||
|
||
Recovery никогда не обходит данный интерфейс.
|
||
|
||
---
|
||
|
||
# Последовательность обработки
|
||
|
||
После получения нормализованной последовательности обработка всегда выполняется по одной и той же схеме.
|
||
|
||
```text
|
||
Trade #1
|
||
|
||
↓
|
||
|
||
accept()
|
||
|
||
↓
|
||
|
||
Trade #2
|
||
|
||
↓
|
||
|
||
accept()
|
||
|
||
↓
|
||
|
||
Trade #3
|
||
|
||
↓
|
||
|
||
accept()
|
||
|
||
↓
|
||
|
||
...
|
||
```
|
||
|
||
Recovery никогда не передаёт контроллеру сразу всю коллекцию.
|
||
|
||
Контроллер продолжает работать исключительно со входящим потоком отдельных сделок.
|
||
|
||
Это сохраняет единый механизм обработки как для REST, так и для WebSocket.
|
||
|
||
---
|
||
|
||
# Почему используется последовательная обработка
|
||
|
||
Во время проектирования рассматривалась возможность добавить новый метод вида:
|
||
|
||
```python
|
||
accept_many(...)
|
||
```
|
||
|
||
Данный вариант был отклонён.
|
||
|
||
Причины:
|
||
|
||
- появление второго публичного API;
|
||
- дублирование логики;
|
||
- нарушение единой модели обработки потока;
|
||
- увеличение сложности тестирования.
|
||
|
||
Build 060.19 сохраняет существующий контракт без изменений.
|
||
|
||
---
|
||
|
||
# Роль TradeStreamConsistencyController
|
||
|
||
Recovery не принимает решений относительно результата обработки сделки.
|
||
|
||
Все решения принадлежат исключительно существующему Controller.
|
||
|
||
Для каждой сделки возможны только три сценария.
|
||
|
||
---
|
||
|
||
## Сценарий №1
|
||
|
||
Controller возвращает:
|
||
|
||
```python
|
||
Trade
|
||
```
|
||
|
||
Это означает успешное прохождение проверки согласованности.
|
||
|
||
Recovery включает сделку в итоговый результат восстановления.
|
||
|
||
---
|
||
|
||
## Сценарий №2
|
||
|
||
Controller возвращает:
|
||
|
||
```python
|
||
None
|
||
```
|
||
|
||
Это означает обнаружение корректного дубликата.
|
||
|
||
Recovery не считает подобную ситуацию ошибкой.
|
||
|
||
Такая сделка просто не включается в итоговый поток восстановления.
|
||
|
||
---
|
||
|
||
## Сценарий №3
|
||
|
||
Controller генерирует исключение.
|
||
|
||
Например:
|
||
|
||
```text
|
||
TradeConsistencyError
|
||
```
|
||
|
||
или
|
||
|
||
```text
|
||
TradeOrderingError
|
||
```
|
||
|
||
Recovery немедленно прекращает выполнение.
|
||
|
||
Итог восстановления считается неуспешным.
|
||
|
||
---
|
||
|
||
# Почему Recovery не перехватывает ошибки Consistency
|
||
|
||
Во время проектирования рассматривался вариант автоматического продолжения восстановления после ошибок согласованности.
|
||
|
||
Данный вариант отклонён.
|
||
|
||
Причины:
|
||
|
||
- нарушение архитектурных инвариантов;
|
||
- сокрытие ошибок потока;
|
||
- появление недостоверного результата восстановления.
|
||
|
||
Recovery никогда не скрывает ошибки Controller.
|
||
|
||
Исключения распространяются вверх без изменения их смысла.
|
||
|
||
---
|
||
|
||
# Формирование результата Recovery
|
||
|
||
После обработки всех сделок Recovery формирует единый результат выполнения.
|
||
|
||
Build 060.19 вводит отдельную доменную модель результата.
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Данный объект не относится к транспортному уровню.
|
||
|
||
Он описывает исключительно итог работы Recovery.
|
||
|
||
---
|
||
|
||
# Почему вводится отдельный Result
|
||
|
||
Во время проектирования рассматривались несколько вариантов.
|
||
|
||
---
|
||
|
||
## Вариант №1
|
||
|
||
Возвращать:
|
||
|
||
```python
|
||
tuple[Trade]
|
||
```
|
||
|
||
Отклонён.
|
||
|
||
По коллекции невозможно определить:
|
||
|
||
- была ли выполнена обработка полностью;
|
||
- сколько сделок было отброшено как дубликаты;
|
||
- завершилось ли восстановление успешно.
|
||
|
||
---
|
||
|
||
## Вариант №2
|
||
|
||
Возвращать:
|
||
|
||
```python
|
||
list[Trade]
|
||
```
|
||
|
||
Отклонён по тем же причинам.
|
||
|
||
Кроме того, Canonical Pipeline использует неизменяемые коллекции.
|
||
|
||
---
|
||
|
||
## Вариант №3
|
||
|
||
Использовать специализированный объект результата.
|
||
|
||
Принят.
|
||
|
||
Именно он становится официальным контрактом Recovery.
|
||
|
||
---
|
||
|
||
# Назначение TradeRecoveryResult
|
||
|
||
TradeRecoveryResult описывает завершённую операцию восстановления.
|
||
|
||
Он не является журналом выполнения.
|
||
|
||
Он не содержит внутреннего состояния Recovery.
|
||
|
||
Он лишь фиксирует итог уже завершённой операции.
|
||
|
||
---
|
||
|
||
# Минимальный состав Result
|
||
|
||
Build 060.19 определяет следующий минимальный набор информации.
|
||
|
||
```text
|
||
Recovered Trades
|
||
|
||
Skipped Duplicates
|
||
```
|
||
|
||
Этого достаточно для оценки результата работы Recovery.
|
||
|
||
Расширенные диагностические поля будут добавлены в следующих Build.
|
||
|
||
---
|
||
|
||
# Почему Result не содержит ошибки
|
||
|
||
Во время проектирования рассматривался вариант хранения исключения внутри объекта результата.
|
||
|
||
Например:
|
||
|
||
```text
|
||
error
|
||
```
|
||
|
||
или
|
||
|
||
```text
|
||
exception
|
||
```
|
||
|
||
Данный вариант отклонён.
|
||
|
||
Recovery использует стандартную модель обработки ошибок Python.
|
||
|
||
При возникновении исключения объект результата не создаётся.
|
||
|
||
---
|
||
|
||
# Обработка пустого восстановления
|
||
|
||
Если REST не возвращает ни одной сделки,
|
||
|
||
Recovery успешно завершается.
|
||
|
||
Результат содержит:
|
||
|
||
```text
|
||
Recovered Trades = 0
|
||
|
||
Skipped Duplicates = 0
|
||
```
|
||
|
||
Подобная ситуация считается полностью корректной.
|
||
|
||
---
|
||
|
||
# Обработка полного дублирования
|
||
|
||
Если все полученные сделки уже присутствуют в Canonical Stream,
|
||
|
||
Controller вернёт:
|
||
|
||
```python
|
||
None
|
||
```
|
||
|
||
для каждой сделки.
|
||
|
||
Recovery завершится успешно.
|
||
|
||
Результат будет иметь вид:
|
||
|
||
```text
|
||
Recovered Trades = 0
|
||
|
||
Skipped Duplicates = N
|
||
```
|
||
|
||
Ошибки не возникает.
|
||
|
||
---
|
||
|
||
# Обработка частичного восстановления
|
||
|
||
Наиболее типичный сценарий.
|
||
|
||
Например:
|
||
|
||
REST вернул:
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
|
||
140
|
||
```
|
||
|
||
Controller определил:
|
||
|
||
```text
|
||
100 -> duplicate
|
||
|
||
105 -> accepted
|
||
|
||
121 -> accepted
|
||
|
||
140 -> accepted
|
||
```
|
||
|
||
Итог Recovery:
|
||
|
||
```text
|
||
Recovered Trades = 3
|
||
|
||
Skipped Duplicates = 1
|
||
```
|
||
|
||
Именно подобный сценарий считается основной моделью работы Recovery.
|
||
|
||
---
|
||
|
||
# Завершение Recovery
|
||
|
||
Recovery считается успешно завершённым только после выполнения всех условий:
|
||
|
||
- REST Pipeline завершился без ошибок;
|
||
- Normalizer успешно обработал последовательность;
|
||
- все сделки переданы в Stream Consistency;
|
||
- Controller не сгенерировал исключений;
|
||
- сформирован TradeRecoveryResult.
|
||
|
||
Только после этого операция восстановления считается завершённой.
|
||
|
||
---
|
||
|
||
# Исключения Recovery
|
||
|
||
Build 060.19 вводит собственный набор доменных исключений Recovery.
|
||
|
||
Они описывают ошибки исключительно уровня восстановления истории и не заменяют существующие исключения других компонентов системы.
|
||
|
||
Recovery никогда не создаёт новые исключения для задач:
|
||
|
||
- Parser;
|
||
- Mapper;
|
||
- Value Validation;
|
||
- Trade Stream Consistency.
|
||
|
||
Каждый уровень системы продолжает использовать собственную модель ошибок.
|
||
|
||
---
|
||
|
||
# Принцип распространения исключений
|
||
|
||
Recovery придерживается принципа Fail Fast.
|
||
|
||
Если любой нижележащий компонент завершает работу исключением,
|
||
|
||
Recovery немедленно прекращает выполнение.
|
||
|
||
Никакие ошибки не:
|
||
|
||
- скрываются;
|
||
- преобразуются;
|
||
- игнорируются;
|
||
- журналируются внутри Recovery.
|
||
|
||
Ответственность Recovery ограничивается только распространением ошибки вызывающему компоненту.
|
||
|
||
---
|
||
|
||
# Собственные ошибки Recovery
|
||
|
||
Build 060.19 предусматривает появление специализированных исключений Recovery.
|
||
|
||
Например:
|
||
|
||
```text
|
||
TradeRecoveryError
|
||
```
|
||
|
||
базовое исключение Recovery.
|
||
|
||
От него могут наследоваться специализированные ошибки.
|
||
|
||
Например:
|
||
|
||
```text
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
Ошибка нормализации последовательности.
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryRequestError
|
||
```
|
||
|
||
Некорректный Recovery Request.
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryConfigurationError
|
||
```
|
||
|
||
Некорректная конфигурация Recovery.
|
||
|
||
---
|
||
|
||
Данный перечень может быть расширен в последующих Build без изменения публичной архитектуры Recovery.
|
||
|
||
---
|
||
|
||
# Ошибки, которые Recovery не создаёт
|
||
|
||
Recovery никогда не создаёт:
|
||
|
||
```text
|
||
TradeConsistencyError
|
||
```
|
||
|
||
или
|
||
|
||
```text
|
||
TradeOrderingError
|
||
```
|
||
|
||
Данные исключения принадлежат исключительно Build 060.18.
|
||
|
||
Recovery лишь распространяет их без изменения.
|
||
|
||
---
|
||
|
||
# Обработка транспортных ошибок
|
||
|
||
Если REST Source завершает работу исключением,
|
||
|
||
например:
|
||
|
||
```text
|
||
HTTP Error
|
||
|
||
Network Error
|
||
|
||
Timeout
|
||
```
|
||
|
||
Recovery не пытается выполнить повторный запрос.
|
||
|
||
Retry Policy не входит в Scope Build 060.19.
|
||
|
||
Исключение распространяется вызывающему компоненту.
|
||
|
||
---
|
||
|
||
# Обработка ошибок Parser
|
||
|
||
Если Parser обнаруживает нарушение транспортного контракта,
|
||
|
||
Recovery не вмешивается.
|
||
|
||
Исключение считается критическим.
|
||
|
||
Восстановление прекращается.
|
||
|
||
---
|
||
|
||
# Обработка ошибок Value Validation
|
||
|
||
Если хотя бы одна сделка не проходит существующую систему Validation,
|
||
|
||
Recovery завершается ошибкой.
|
||
|
||
Продолжение восстановления после подобных ошибок запрещается.
|
||
|
||
---
|
||
|
||
# Обработка ошибок Mapper
|
||
|
||
Если Mapper не способен сформировать Canonical Trade,
|
||
|
||
Recovery немедленно прекращает выполнение.
|
||
|
||
Никакие частично обработанные результаты не публикуются.
|
||
|
||
---
|
||
|
||
# Обработка ошибок Normalizer
|
||
|
||
Если Normalizer обнаруживает смешанный порядок последовательности,
|
||
|
||
генерируется:
|
||
|
||
```text
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
После возникновения данной ошибки:
|
||
|
||
- ни одна сделка не передаётся в Stream Consistency;
|
||
- результат Recovery не создаётся;
|
||
- выполнение прекращается.
|
||
|
||
---
|
||
|
||
# Обработка ошибок Stream Consistency
|
||
|
||
Если TradeStreamConsistencyController обнаруживает нарушение инвариантов потока,
|
||
|
||
Recovery не выполняет никаких дополнительных действий.
|
||
|
||
Исключение распространяется вверх без изменения.
|
||
|
||
Таким образом единственным владельцем логики проверки согласованности остаётся Build 060.18.
|
||
|
||
---
|
||
|
||
# Тестируемость Recovery
|
||
|
||
Recovery проектируется как полностью детерминированная подсистема.
|
||
|
||
При одинаковых входных данных Recovery всегда обязан выдавать одинаковый результат.
|
||
|
||
Никакие внешние факторы не должны влиять на результат выполнения.
|
||
|
||
---
|
||
|
||
# Принцип детерминированности
|
||
|
||
При фиксированных:
|
||
|
||
- Recovery Request;
|
||
- ответе REST;
|
||
- состоянии Stream Consistency;
|
||
|
||
результат Recovery всегда обязан быть идентичным.
|
||
|
||
Это значительно упрощает автоматическое тестирование.
|
||
|
||
---
|
||
|
||
# Изоляция компонентов
|
||
|
||
Каждый компонент Recovery должен тестироваться независимо.
|
||
|
||
Например:
|
||
|
||
TradeRecoveryNormalizer может быть протестирован без:
|
||
|
||
- REST;
|
||
- Runtime;
|
||
- Stream Consistency.
|
||
|
||
TradeRecoveryController может быть протестирован с использованием Mock Source и Mock ConsistencyController.
|
||
|
||
Подобное разделение является обязательным архитектурным требованием.
|
||
|
||
---
|
||
|
||
# Unit-тестирование
|
||
|
||
Минимальный набор Unit-тестов должен покрывать:
|
||
|
||
## Recovery Request
|
||
|
||
- корректное создание;
|
||
- нарушение временного диапазона;
|
||
- нарушение ограничения limit;
|
||
- неизменяемость объекта.
|
||
|
||
---
|
||
|
||
## Recovery Normalizer
|
||
|
||
- пустая последовательность;
|
||
- одна сделка;
|
||
- ASCENDING;
|
||
- DESCENDING;
|
||
- смешанный порядок;
|
||
- отсутствие изменения объектов Trade.
|
||
|
||
---
|
||
|
||
## Recovery Controller
|
||
|
||
- успешное восстановление;
|
||
- пустое восстановление;
|
||
- полное дублирование;
|
||
- частичное восстановление;
|
||
- ошибка REST;
|
||
- ошибка Parser;
|
||
- ошибка Mapper;
|
||
- ошибка Validation;
|
||
- ошибка Consistency Controller.
|
||
|
||
---
|
||
|
||
# Интеграционные тесты
|
||
|
||
После завершения Unit-тестирования Build предусматривает отдельный набор интеграционных сценариев.
|
||
|
||
Минимально должны быть проверены:
|
||
|
||
- получение истории через существующий REST Pipeline;
|
||
- передача результата в Recovery;
|
||
- нормализация последовательности;
|
||
- взаимодействие с существующим TradeStreamConsistencyController;
|
||
- формирование итогового TradeRecoveryResult.
|
||
|
||
---
|
||
|
||
# Архитектурные инварианты Build
|
||
|
||
После завершения Build 060.19 система обязана удовлетворять следующим инвариантам.
|
||
|
||
---
|
||
|
||
## Инвариант №1
|
||
|
||
Recovery никогда не работает с транспортными структурами.
|
||
|
||
---
|
||
|
||
## Инвариант №2
|
||
|
||
Recovery использует исключительно существующий REST Pipeline.
|
||
|
||
---
|
||
|
||
## Инвариант №3
|
||
|
||
Recovery использует исключительно существующий TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
## Инвариант №4
|
||
|
||
Recovery никогда не реализует собственную дедупликацию.
|
||
|
||
---
|
||
|
||
## Инвариант №5
|
||
|
||
Recovery никогда не реализует собственную проверку порядка сделок.
|
||
|
||
---
|
||
|
||
## Инвариант №6
|
||
|
||
Recovery никогда не изменяет объект Trade.
|
||
|
||
---
|
||
|
||
## Инвариант №7
|
||
|
||
Recovery никогда не вычисляет диапазон восстановления самостоятельно.
|
||
|
||
---
|
||
|
||
## Инвариант №8
|
||
|
||
Recovery полностью независим от Runtime.
|
||
|
||
---
|
||
|
||
## Инвариант №9
|
||
|
||
Recovery полностью независим от WebSocket Transport.
|
||
|
||
---
|
||
|
||
## Инвариант №10
|
||
|
||
Recovery не изменяет архитектуру существующих Build серии 060.
|
||
|
||
Он исключительно дополняет Acquisition Layer новой автономной подсистемой восстановления истории.
|
||
|
||
---
|
||
|
||
# Итоги Build 060.19
|
||
|
||
После реализации Build 060.19 система впервые получает полноценный механизм безопасного восстановления исторических сделок.
|
||
|
||
При этом сохраняются все архитектурные принципы серии Build 060:
|
||
|
||
- единая Canonical Trade Model;
|
||
- единый REST Pipeline;
|
||
- единая система Validation;
|
||
- единый Mapper;
|
||
- единый Trade Stream Consistency Controller;
|
||
- единый Canonical Trade Stream.
|
||
|
||
Recovery не создаёт альтернативную архитектуру обработки сделок.
|
||
|
||
Напротив, он органично встраивается в уже существующий Acquisition Pipeline и использует ранее построенные компоненты без дублирования их ответственности.
|
||
|
||
Именно этот подход обеспечивает минимальную связанность подсистем, высокую тестируемость, предсказуемость поведения и возможность дальнейшего развития Runtime, Reconnect и Backfill-механизмов в последующих Build без изменения фундаментальной архитектуры Recovery.
|
||
|
||
---
|
||
|
||
# Приложение A. Architecture Decision Records (ADR)
|
||
|
||
---
|
||
|
||
# Назначение приложения
|
||
|
||
Настоящее приложение фиксирует все ключевые архитектурные решения, принятые при проектировании Build 060.19.
|
||
|
||
Основной документ описывает итоговую архитектуру системы.
|
||
|
||
ADR, в свою очередь, отвечает на другой вопрос:
|
||
|
||
> **Почему архитектура выглядит именно так?**
|
||
|
||
Каждый ADR фиксирует:
|
||
|
||
- исходную проблему;
|
||
- рассмотренные варианты;
|
||
- принятое решение;
|
||
- причины выбора;
|
||
- последствия данного решения.
|
||
|
||
После утверждения ADR считается частью архитектурного контракта системы.
|
||
|
||
Изменение любого ADR требует подготовки нового архитектурного решения и не допускается в процессе реализации Build.
|
||
|
||
---
|
||
|
||
# ADR-060.19-001
|
||
|
||
# Recovery использует существующий TradeStreamConsistencyController
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
После появления механизма восстановления истории необходимо определить, каким образом проверяется корректность восстановленного потока.
|
||
|
||
К моменту начала Build уже существует полноценный компонент:
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
который гарантирует:
|
||
|
||
- дедупликацию;
|
||
- контроль порядка;
|
||
- обнаружение конфликтующих дублей;
|
||
- защиту Canonical Trade Stream.
|
||
|
||
Возникает вопрос:
|
||
|
||
должен ли Recovery реализовывать аналогичную функциональность самостоятельно?
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Вариант 1
|
||
|
||
Recovery реализует собственную дедупликацию.
|
||
|
||
Преимущества:
|
||
|
||
- независимость.
|
||
|
||
Недостатки:
|
||
|
||
- дублирование логики;
|
||
- появление второго источника истины;
|
||
- риск расхождения алгоритмов;
|
||
- двойное сопровождение.
|
||
|
||
---
|
||
|
||
### Вариант 2
|
||
|
||
Recovery реализует собственную проверку порядка.
|
||
|
||
Преимущества:
|
||
|
||
локальная автономность.
|
||
|
||
Недостатки:
|
||
|
||
- две различные реализации Ordering;
|
||
- вероятность различного поведения REST и WebSocket;
|
||
- нарушение принципа единственного владельца бизнес-правил.
|
||
|
||
---
|
||
|
||
### Вариант 3
|
||
|
||
Recovery полностью использует существующий TradeStreamConsistencyController.
|
||
|
||
Преимущества:
|
||
|
||
- единая логика проверки;
|
||
- единая дедупликация;
|
||
- единая модель Ordering;
|
||
- отсутствие дублирования;
|
||
- минимальная связанность.
|
||
|
||
Недостатков не обнаружено.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Build 060.19 использует исключительно существующий публичный контракт:
|
||
|
||
```python
|
||
accept(trade: Trade) -> Trade | None
|
||
```
|
||
|
||
Recovery никогда не реализует собственную проверку согласованности.
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Во всей системе существует только один компонент, отвечающий за:
|
||
|
||
- Ordering;
|
||
- Deduplication;
|
||
- Conflict Detection.
|
||
|
||
Таким компонентом является:
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
Recovery остаётся исключительно оркестратором.
|
||
|
||
---
|
||
|
||
# ADR-060.19-002
|
||
|
||
# Recovery использует временные диапазоны вместо trade_id
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
Необходимо определить способ получения исторических сделок.
|
||
|
||
Первоначально рассматривались два варианта:
|
||
|
||
- восстановление по trade_id;
|
||
- восстановление по времени.
|
||
|
||
---
|
||
|
||
## Исследование
|
||
|
||
Перед принятием решения было выполнено экспериментальное исследование REST API.
|
||
|
||
Подтверждено:
|
||
|
||
- `startTime` работает корректно;
|
||
- `endTime` работает корректно;
|
||
- совместное использование поддерживается;
|
||
- результаты детерминированы.
|
||
|
||
Одновременно было установлено:
|
||
|
||
использование:
|
||
|
||
```text
|
||
fromId
|
||
```
|
||
|
||
не обеспечивает надёжного позиционирования внутри истории.
|
||
|
||
Во многих случаях REST возвращает последнюю страницу сделок независимо от указанного значения.
|
||
|
||
Следовательно построение архитектуры Recovery вокруг `trade_id` признано небезопасным.
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Вариант 1
|
||
|
||
Использовать:
|
||
|
||
```text
|
||
fromId
|
||
```
|
||
|
||
Отклонён.
|
||
|
||
Причина:
|
||
|
||
контракт REST API не подтверждён экспериментально.
|
||
|
||
---
|
||
|
||
### Вариант 2
|
||
|
||
Использовать:
|
||
|
||
```text
|
||
trade_id
|
||
```
|
||
|
||
как внутренний курсор Runtime.
|
||
|
||
Отклонён.
|
||
|
||
Причины:
|
||
|
||
- привязка Recovery к внутреннему состоянию;
|
||
- невозможность гарантировать корректное восстановление;
|
||
- зависимость от неподтверждённого поведения биржи.
|
||
|
||
---
|
||
|
||
### Вариант 3
|
||
|
||
Использовать исключительно временной диапазон.
|
||
|
||
Принят.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Recovery использует только:
|
||
|
||
```text
|
||
start_time
|
||
|
||
↓
|
||
|
||
end_time
|
||
```
|
||
|
||
Все остальные механизмы навигации исключены из архитектуры Build.
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Recovery становится полностью независимым от внутренней структуры идентификаторов сделок.
|
||
|
||
Даже если биржа изменит механизм формирования `trade_id`, архитектура Recovery останется корректной.
|
||
|
||
---
|
||
|
||
# ADR-060.19-003
|
||
|
||
# Recovery не хранит собственного состояния
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
Во время проектирования возник вопрос:
|
||
|
||
должен ли Recovery хранить информацию о последнем успешно восстановленном диапазоне?
|
||
|
||
Например:
|
||
|
||
```text
|
||
last_trade_id
|
||
|
||
или
|
||
|
||
last_timestamp
|
||
```
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Stateful Recovery
|
||
|
||
Recovery самостоятельно сохраняет:
|
||
|
||
- последний trade_id;
|
||
- последний timestamp;
|
||
- информацию о последнем запуске.
|
||
|
||
Преимущества:
|
||
|
||
локальная автономность.
|
||
|
||
Недостатки:
|
||
|
||
- необходимость хранения состояния;
|
||
- усложнение тестирования;
|
||
- зависимость от Runtime;
|
||
- необходимость восстановления собственного состояния после перезапуска.
|
||
|
||
---
|
||
|
||
### Stateless Recovery
|
||
|
||
Recovery ничего не хранит.
|
||
|
||
Все необходимые параметры приходят внутри Recovery Request.
|
||
|
||
Преимущества:
|
||
|
||
- простая архитектура;
|
||
- отсутствие собственного состояния;
|
||
- высокая тестируемость;
|
||
- независимость от Runtime;
|
||
- отсутствие необходимости синхронизации.
|
||
|
||
Недостатков не выявлено.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Recovery является полностью Stateless-компонентом.
|
||
|
||
Любая информация, необходимая для восстановления, передаётся исключительно через:
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
```
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Recovery становится полностью детерминированным.
|
||
|
||
При одинаковом запросе он всегда выполняет одинаковые действия независимо от предыдущих запусков.
|
||
|
||
---
|
||
|
||
# ADR-060.19-004
|
||
|
||
# Recovery полностью независим от Runtime
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
После появления Recovery возник вопрос:
|
||
|
||
должен ли Recovery самостоятельно взаимодействовать с Runtime?
|
||
|
||
Например:
|
||
|
||
- получать Runtime Events;
|
||
- определять момент восстановления;
|
||
- инициировать переподключение;
|
||
- принимать решение о завершении Recovery.
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Вариант 1
|
||
|
||
Recovery становится частью Runtime.
|
||
|
||
Преимущества:
|
||
|
||
- тесная интеграция;
|
||
- меньше промежуточных компонентов.
|
||
|
||
Недостатки:
|
||
|
||
- высокая связанность;
|
||
- невозможность автономного тестирования;
|
||
- сложность повторного использования;
|
||
- нарушение принципа разделения ответственности.
|
||
|
||
---
|
||
|
||
### Вариант 2
|
||
|
||
Recovery представляет собой независимый сервис.
|
||
|
||
Runtime лишь вызывает его.
|
||
|
||
Преимущества:
|
||
|
||
- слабая связанность;
|
||
- простое тестирование;
|
||
- возможность автономного использования;
|
||
- отсутствие циклических зависимостей.
|
||
|
||
Недостатков не обнаружено.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Recovery ничего не знает о Runtime.
|
||
|
||
Recovery не знает:
|
||
|
||
- почему произошло восстановление;
|
||
- почему был потерян WebSocket;
|
||
- сколько времени отсутствовало соединение;
|
||
- требуется ли последующее переподключение.
|
||
|
||
Recovery выполняет только одну операцию:
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
|
||
↓
|
||
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Runtime становится владельцем жизненного цикла Recovery.
|
||
|
||
Recovery остаётся полностью независимым компонентом Acquisition Layer.
|
||
|
||
Интеграция между ними выполняется исключительно через публичный API.
|
||
|
||
---
|
||
|
||
# ADR-060.19-005
|
||
|
||
# Controller и Normalizer разделены
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
Во время проектирования Recovery возник вопрос:
|
||
|
||
следует ли выполнять нормализацию последовательности непосредственно внутри Controller?
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Вариант 1
|
||
|
||
Controller самостоятельно выполняет:
|
||
|
||
- получение истории;
|
||
- нормализацию;
|
||
- передачу в Consistency.
|
||
|
||
Преимущество:
|
||
|
||
меньшее количество компонентов.
|
||
|
||
Недостатки:
|
||
|
||
- смешение обязанностей;
|
||
- увеличение размера Controller;
|
||
- снижение тестируемости;
|
||
- невозможность повторного использования алгоритма нормализации.
|
||
|
||
---
|
||
|
||
### Вариант 2
|
||
|
||
Выделить отдельный компонент:
|
||
|
||
```text
|
||
TradeRecoveryNormalizer
|
||
```
|
||
|
||
Преимущества:
|
||
|
||
- Single Responsibility;
|
||
- независимое тестирование;
|
||
- простая модификация алгоритма;
|
||
- повторное использование.
|
||
|
||
Недостатков не выявлено.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Recovery состоит минимум из двух независимых компонентов.
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
|
||
↓
|
||
|
||
TradeRecoveryNormalizer
|
||
```
|
||
|
||
Controller отвечает исключительно за оркестрацию.
|
||
|
||
Normalizer отвечает исключительно за порядок сделок.
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Каждый компонент имеет одну ответственность.
|
||
|
||
Изменение алгоритма нормализации не требует изменения Controller.
|
||
|
||
---
|
||
|
||
# ADR-060.19-006
|
||
|
||
# RecoveryResult является отдельной Domain Model
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
После завершения Recovery необходимо определить способ возврата результата.
|
||
|
||
Рассматривались различные модели.
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Вариант 1
|
||
|
||
Вернуть:
|
||
|
||
```python
|
||
tuple[Trade]
|
||
```
|
||
|
||
Недостатки:
|
||
|
||
- отсутствует информация о количестве пропущенных дублей;
|
||
- невозможно отличить пустой диапазон от полного дублирования;
|
||
- отсутствует описание завершённой операции.
|
||
|
||
---
|
||
|
||
### Вариант 2
|
||
|
||
Вернуть:
|
||
|
||
```python
|
||
list[Trade]
|
||
```
|
||
|
||
Недостатки аналогичны.
|
||
|
||
Кроме того, нарушается использование immutable-коллекций.
|
||
|
||
---
|
||
|
||
### Вариант 3
|
||
|
||
Создать отдельную доменную модель результата.
|
||
|
||
Преимущества:
|
||
|
||
- расширяемость;
|
||
- единый контракт;
|
||
- возможность добавления диагностической информации;
|
||
- отсутствие изменения публичного API в будущем.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Recovery возвращает исключительно:
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Этот объект описывает уже завершённую операцию восстановления.
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Публичный API Recovery остаётся стабильным.
|
||
|
||
В следующих Build возможно расширение модели результата без изменения сигнатур Controller.
|
||
|
||
---
|
||
|
||
# ADR-060.19-007
|
||
|
||
# Recovery не реализует Retry Policy
|
||
|
||
## Статус
|
||
|
||
```text
|
||
Accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Контекст
|
||
|
||
Во время проектирования возник вопрос:
|
||
|
||
следует ли Recovery автоматически повторять REST-запросы при временных ошибках сети?
|
||
|
||
---
|
||
|
||
## Рассмотренные варианты
|
||
|
||
### Вариант 1
|
||
|
||
Автоматический Retry внутри Recovery.
|
||
|
||
Преимущества:
|
||
|
||
- уменьшение количества временных ошибок.
|
||
|
||
Недостатки:
|
||
|
||
- появление внутреннего состояния;
|
||
- усложнение Recovery;
|
||
- необходимость настройки стратегий ожидания;
|
||
- смешение обязанностей;
|
||
- невозможность централизованного управления повторными попытками.
|
||
|
||
---
|
||
|
||
### Вариант 2
|
||
|
||
Retry полностью принадлежит Runtime.
|
||
|
||
Recovery выполняет единственную попытку.
|
||
|
||
При ошибке возвращает управление вызывающему компоненту.
|
||
|
||
Преимущества:
|
||
|
||
- простая архитектура;
|
||
- единая стратегия повторных попыток;
|
||
- отсутствие дублирования Retry между компонентами;
|
||
- соответствие принципу Single Responsibility.
|
||
|
||
Недостатков не выявлено.
|
||
|
||
---
|
||
|
||
## Принятое решение
|
||
|
||
Build 060.19 не реализует Retry Policy.
|
||
|
||
Recovery выполняет одну попытку получения исторических данных.
|
||
|
||
Любая транспортная ошибка немедленно распространяется вызывающему компоненту.
|
||
|
||
---
|
||
|
||
## Последствия
|
||
|
||
Recovery остаётся простым и детерминированным.
|
||
|
||
Все стратегии:
|
||
|
||
- Retry;
|
||
- Exponential Backoff;
|
||
- Circuit Breaker;
|
||
- ограничение количества попыток;
|
||
- таймеры ожидания;
|
||
|
||
будут реализованы на уровне Runtime в последующих Build.
|
||
|
||
---
|
||
|
||
# Итоги приложения A
|
||
|
||
Настоящие ADR фиксируют фундаментальные архитектурные решения Build 060.19.
|
||
|
||
Они определяют не только текущее устройство Recovery, но и границы его дальнейшего развития.
|
||
|
||
Любая будущая модификация Recovery должна проверяться на соответствие данным решениям.
|
||
|
||
Если новое архитектурное решение противоречит одному из настоящих ADR, оно не может быть реализовано без подготовки нового Architecture Decision Record и пересмотра архитектурной спецификации Build.
|
||
|
||
---
|
||
|
||
# Приложение B. Диаграммы последовательностей
|
||
|
||
---
|
||
|
||
# Назначение приложения
|
||
|
||
Настоящее приложение фиксирует последовательности взаимодействия компонентов Recovery.
|
||
|
||
В отличие от основной части документа, описывающей архитектурные сущности и их ответственность, настоящее приложение показывает:
|
||
|
||
- порядок вызова компонентов;
|
||
- направление передачи данных;
|
||
- точки возникновения исключений;
|
||
- завершение успешных и неуспешных сценариев.
|
||
|
||
Все диаграммы являются частью архитектурного контракта Build 060.19.
|
||
|
||
---
|
||
|
||
# Обозначения
|
||
|
||
Во всех диаграммах используются одинаковые обозначения.
|
||
|
||
```text
|
||
↓
|
||
|
||
Синхронный вызов
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
←
|
||
|
||
Возврат результата
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
X
|
||
|
||
Возникновение исключения
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
✓
|
||
|
||
Успешное завершение этапа
|
||
```
|
||
|
||
---
|
||
|
||
# Диаграмма 1
|
||
|
||
# Полный успешный сценарий Recovery
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
│
|
||
|
||
REST Request (start/end)
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Response (Document)
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ASCENDING tuple
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
│
|
||
|
||
accept(trade #1)
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
accepted
|
||
|
||
│
|
||
|
||
accept(trade #2)
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
accepted
|
||
|
||
│
|
||
|
||
...
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryResult
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Recovery Done
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
В данном сценарии:
|
||
|
||
- REST завершился успешно;
|
||
- Parser завершился успешно;
|
||
- Validation завершилась успешно;
|
||
- Mapper завершился успешно;
|
||
- Normalizer завершился успешно;
|
||
- все сделки прошли Stream Consistency.
|
||
|
||
Recovery завершается формированием результата.
|
||
|
||
---
|
||
|
||
# Диаграмма 2
|
||
|
||
# Восстановление пустого диапазона
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Source
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Response
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
()
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
()
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryResult
|
||
|
||
Recovered Trades = 0
|
||
|
||
Skipped Duplicates = 0
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Пустой диапазон не считается ошибкой.
|
||
|
||
Recovery завершается успешно.
|
||
|
||
TradeStreamConsistencyController не вызывается.
|
||
|
||
---
|
||
|
||
# Диаграмма 3
|
||
|
||
# Полное дублирование
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
tuple[Trade]
|
||
|
||
↓
|
||
|
||
Normalizer
|
||
|
||
↓
|
||
|
||
Trade #1
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
None
|
||
|
||
↓
|
||
|
||
Trade #2
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
None
|
||
|
||
↓
|
||
|
||
Trade #3
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
None
|
||
|
||
↓
|
||
|
||
TradeRecoveryResult
|
||
|
||
Recovered = 0
|
||
|
||
Duplicates = 3
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Все сделки уже присутствуют в Canonical Trade Stream.
|
||
|
||
Recovery:
|
||
|
||
- не генерирует ошибку;
|
||
- не публикует сделки;
|
||
- успешно завершается.
|
||
|
||
---
|
||
|
||
# Диаграмма 4
|
||
|
||
# Частичное восстановление
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Trade #100
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
None
|
||
|
||
────────────
|
||
|
||
Trade #105
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
Trade
|
||
|
||
────────────
|
||
|
||
Trade #121
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
Trade
|
||
|
||
────────────
|
||
|
||
Trade #140
|
||
|
||
↓
|
||
|
||
Consistency
|
||
|
||
↓
|
||
|
||
Trade
|
||
|
||
────────────
|
||
|
||
TradeRecoveryResult
|
||
|
||
Recovered = 3
|
||
|
||
Duplicates = 1
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Это основной рабочий сценарий Recovery.
|
||
|
||
Часть сделок уже существует.
|
||
|
||
Остальные успешно публикуются.
|
||
|
||
Recovery завершается успешно.
|
||
|
||
---
|
||
|
||
# Диаграмма 5
|
||
|
||
# REST возвращает DESCENDING последовательность
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
140
|
||
|
||
↓
|
||
|
||
121
|
||
|
||
↓
|
||
|
||
105
|
||
|
||
↓
|
||
|
||
100
|
||
|
||
↓
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
↓
|
||
|
||
100
|
||
|
||
↓
|
||
|
||
105
|
||
|
||
↓
|
||
|
||
121
|
||
|
||
↓
|
||
|
||
140
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Нормализация выполняется полностью внутри RecoveryNormalizer.
|
||
|
||
TradeStreamConsistencyController получает уже канонический поток.
|
||
|
||
Recovery никогда не передаёт Controller последовательность в обратном порядке.
|
||
|
||
---
|
||
|
||
# Диаграмма 6
|
||
|
||
# REST возвращает ASCENDING последовательность
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
100
|
||
|
||
↓
|
||
|
||
105
|
||
|
||
↓
|
||
|
||
121
|
||
|
||
↓
|
||
|
||
140
|
||
|
||
↓
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
↓
|
||
|
||
100
|
||
|
||
↓
|
||
|
||
105
|
||
|
||
↓
|
||
|
||
121
|
||
|
||
↓
|
||
|
||
140
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Несмотря на то, что текущий контракт REST API предусматривает выдачу сделок в порядке DESCENDING, RecoveryNormalizer проектируется универсальным.
|
||
|
||
Если в будущем REST API начнёт возвращать последовательность уже в каноническом порядке, Recovery не потребует изменений архитектуры.
|
||
|
||
Normalizer обнаружит, что последовательность уже соответствует Canonical Trade Stream, и вернёт её без изменений.
|
||
|
||
---
|
||
|
||
# Диаграмма 7
|
||
|
||
# REST возвращает смешанный порядок
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
100
|
||
|
||
↓
|
||
|
||
140
|
||
|
||
↓
|
||
|
||
121
|
||
|
||
↓
|
||
|
||
180
|
||
|
||
↓
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
↓
|
||
|
||
X
|
||
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Смешанный порядок рассматривается как нарушение транспортного контракта.
|
||
|
||
Recovery не предпринимает попыток:
|
||
|
||
- отсортировать последовательность;
|
||
- определить правильный порядок;
|
||
- восстановить повреждённые данные.
|
||
|
||
Работа немедленно прекращается.
|
||
|
||
Ни одна сделка не передаётся в TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
# Диаграмма 8
|
||
|
||
# Ошибка Parser
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
|
||
↓
|
||
|
||
REST Source
|
||
|
||
↓
|
||
|
||
REST Parser
|
||
|
||
↓
|
||
|
||
X
|
||
|
||
ParserError
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Recovery не вмешивается в работу Parser.
|
||
|
||
Parser остаётся единственным владельцем транспортной валидации структуры документа.
|
||
|
||
Recovery лишь распространяет возникшее исключение вызывающему компоненту.
|
||
|
||
---
|
||
|
||
# Диаграмма 9
|
||
|
||
# Ошибка Value Validation
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
X
|
||
|
||
ValidationError
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Если хотя бы одна сделка не проходит существующую систему проверки значений,
|
||
|
||
Recovery немедленно прекращает выполнение.
|
||
|
||
TradeRecoveryResult не создаётся.
|
||
|
||
---
|
||
|
||
# Диаграмма 10
|
||
|
||
# Ошибка Mapper
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
X
|
||
|
||
MappingError
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Recovery никогда не работает с частично сформированными объектами.
|
||
|
||
Если Mapper не смог построить корректный объект Trade,
|
||
|
||
дальнейшая обработка невозможна.
|
||
|
||
---
|
||
|
||
# Диаграмма 11
|
||
|
||
# Ошибка Stream Consistency
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
↓
|
||
|
||
Trade #100
|
||
|
||
↓
|
||
|
||
accepted
|
||
|
||
↓
|
||
|
||
Trade #105
|
||
|
||
↓
|
||
|
||
X
|
||
|
||
TradeOrderingError
|
||
```
|
||
|
||
---
|
||
|
||
# Основные свойства сценария
|
||
|
||
Recovery не перехватывает исключения Consistency Layer.
|
||
|
||
После возникновения ошибки:
|
||
|
||
- дальнейшая обработка прекращается;
|
||
- TradeRecoveryResult не создаётся;
|
||
- исключение распространяется вверх.
|
||
|
||
Таким образом владельцем логики проверки потока остаётся исключительно Build 060.18.
|
||
|
||
---
|
||
|
||
# Диаграмма 12
|
||
|
||
# Полный жизненный цикл Recovery
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryController
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Document
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Normalized tuple[Trade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
TradeRecoveryResult
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Caller
|
||
```
|
||
|
||
---
|
||
|
||
# Архитектурные выводы
|
||
|
||
Все приведённые диаграммы подтверждают несколько фундаментальных принципов Build 060.19.
|
||
|
||
---
|
||
|
||
## Принцип №1
|
||
|
||
Recovery никогда не работает с транспортными структурами после завершения этапа Mapping.
|
||
|
||
Начиная с момента формирования объекта `Trade`, Recovery использует исключительно каноническую доменную модель.
|
||
|
||
---
|
||
|
||
## Принцип №2
|
||
|
||
Recovery не изменяет существующий Acquisition Pipeline.
|
||
|
||
Он расширяет его, добавляя дополнительный этап между REST Adapter и TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
## Принцип №3
|
||
|
||
Recovery не принимает доменных решений.
|
||
|
||
Все решения относительно:
|
||
|
||
- корректности сделки;
|
||
- порядка сделок;
|
||
- дубликатов;
|
||
- конфликтов;
|
||
|
||
по-прежнему принадлежат TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
## Принцип №4
|
||
|
||
Recovery является полностью линейным Pipeline.
|
||
|
||
Каждый компонент вызывается строго один раз.
|
||
|
||
Обратные переходы отсутствуют.
|
||
|
||
Циклические зависимости отсутствуют.
|
||
|
||
---
|
||
|
||
## Принцип №5
|
||
|
||
Любое исключение немедленно завершает выполнение Recovery.
|
||
|
||
Частично завершённое восстановление не считается успешным.
|
||
|
||
TradeRecoveryResult формируется только после успешного прохождения всех этапов Pipeline.
|
||
|
||
---
|
||
|
||
# Итоги приложения B
|
||
|
||
Последовательности взаимодействия, приведённые в настоящем приложении, являются нормативным описанием поведения Recovery.
|
||
|
||
Любая реализация Build 060.19 должна соответствовать данным диаграммам.
|
||
|
||
Изменение порядка взаимодействия компонентов, появление дополнительных зависимостей или перенос ответственности между компонентами допускаются только после подготовки нового архитектурного решения (ADR) и внесения соответствующих изменений в архитектурную спецификацию.
|
||
|
||
---
|
||
|
||
# Приложение C. Контракты публичного API
|
||
|
||
---
|
||
|
||
# Назначение приложения
|
||
|
||
Настоящее приложение фиксирует официальный публичный API подсистемы Trade Recovery.
|
||
|
||
Основной документ описывает архитектуру Recovery.
|
||
|
||
Настоящее приложение определяет:
|
||
|
||
- публичные классы;
|
||
- публичные методы;
|
||
- входные параметры;
|
||
- возвращаемые значения;
|
||
- предусловия;
|
||
- постусловия;
|
||
- архитектурные инварианты.
|
||
|
||
Любой внешний компонент системы имеет право взаимодействовать с Recovery исключительно через описанные здесь контракты.
|
||
|
||
Все остальные классы Recovery считаются внутренней реализацией Build 060.19.
|
||
|
||
---
|
||
|
||
# Общие принципы публичного API
|
||
|
||
Recovery строится на следующих принципах.
|
||
|
||
---
|
||
|
||
## Минимальность
|
||
|
||
Публичный API должен содержать только действительно необходимые точки входа.
|
||
|
||
Recovery не предоставляет вспомогательных методов.
|
||
|
||
---
|
||
|
||
## Детерминированность
|
||
|
||
При одинаковых входных данных любой публичный метод обязан возвращать одинаковый результат.
|
||
|
||
---
|
||
|
||
## Отсутствие побочных эффектов
|
||
|
||
Ни один публичный метод Recovery не изменяет:
|
||
|
||
- состояние Runtime;
|
||
- состояние WebSocket;
|
||
- состояние подписок.
|
||
|
||
Recovery воздействует исключительно на Canonical Trade Stream через существующий TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
## Иммутабельность
|
||
|
||
Все входные модели Recovery являются неизменяемыми.
|
||
|
||
Recovery никогда не модифицирует переданные объекты.
|
||
|
||
---
|
||
|
||
# Публичный класс
|
||
|
||
# TradeRecoveryController
|
||
|
||
---
|
||
|
||
## Назначение
|
||
|
||
TradeRecoveryController является единственной точкой входа в Recovery Pipeline.
|
||
|
||
Никакой другой компонент Recovery не должен вызываться напрямую внешними подсистемами.
|
||
|
||
---
|
||
|
||
## Ответственность
|
||
|
||
Контроллер отвечает исключительно за выполнение полного цикла восстановления.
|
||
|
||
Он:
|
||
|
||
- получает Recovery Request;
|
||
- инициирует получение исторических сделок;
|
||
- выполняет нормализацию;
|
||
- передаёт сделки в Stream Consistency;
|
||
- формирует итоговый результат.
|
||
|
||
---
|
||
|
||
## Публичный контракт
|
||
|
||
```python
|
||
recover(
|
||
request: TradeRecoveryRequest,
|
||
) -> TradeRecoveryResult
|
||
```
|
||
|
||
---
|
||
|
||
## Входные параметры
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
```
|
||
|
||
Запрос восстановления.
|
||
|
||
---
|
||
|
||
## Возвращаемое значение
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Описание завершённой операции восстановления.
|
||
|
||
---
|
||
|
||
## Возможные исключения
|
||
|
||
Контроллер может распространять:
|
||
|
||
```text
|
||
TradeRecoveryRequestError
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeConsistencyError
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeOrderingError
|
||
```
|
||
|
||
---
|
||
|
||
а также исключения существующих компонентов:
|
||
|
||
- REST;
|
||
- Parser;
|
||
- Mapper;
|
||
- Value Validation.
|
||
|
||
---
|
||
|
||
## Предусловия
|
||
|
||
Перед вызовом метода должны выполняться следующие условия.
|
||
|
||
- Recovery Request успешно создан.
|
||
- Все обязательные поля заполнены.
|
||
- Диапазон времени корректен.
|
||
- RecoveryController полностью сконфигурирован.
|
||
|
||
---
|
||
|
||
## Постусловия
|
||
|
||
При успешном завершении гарантируется:
|
||
|
||
- все сделки обработаны;
|
||
- все сделки прошли через TradeStreamConsistencyController;
|
||
- сформирован TradeRecoveryResult.
|
||
|
||
---
|
||
|
||
## Побочные эффекты
|
||
|
||
Единственным допустимым побочным эффектом является публикация новых сделок в существующий Canonical Trade Stream.
|
||
|
||
Других изменений состояния системы Controller не выполняет.
|
||
|
||
---
|
||
|
||
# Публичный класс
|
||
|
||
# TradeRecoveryRequest
|
||
|
||
---
|
||
|
||
## Назначение
|
||
|
||
TradeRecoveryRequest описывает один запрос восстановления.
|
||
|
||
После создания объект никогда не изменяется.
|
||
|
||
---
|
||
|
||
## Обязательные поля
|
||
|
||
```text
|
||
symbol
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
start_time
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
end_time
|
||
```
|
||
|
||
---
|
||
|
||
## Необязательные поля
|
||
|
||
```text
|
||
limit
|
||
```
|
||
|
||
---
|
||
|
||
## Инварианты
|
||
|
||
Всегда выполняются условия.
|
||
|
||
```text
|
||
start_time < end_time
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
limit ∈ [1;1000]
|
||
```
|
||
|
||
если limit указан.
|
||
|
||
---
|
||
|
||
Запрос относится только к одному символу.
|
||
|
||
---
|
||
|
||
Объект является immutable.
|
||
|
||
---
|
||
|
||
## Предусловия создания
|
||
|
||
Все поля проходят базовую проверку корректности.
|
||
|
||
---
|
||
|
||
## Постусловия создания
|
||
|
||
После создания Recovery Request считается валидным и может использоваться RecoveryController.
|
||
|
||
---
|
||
|
||
# Публичный класс
|
||
|
||
# TradeRecoveryResult
|
||
|
||
---
|
||
|
||
## Назначение
|
||
|
||
TradeRecoveryResult описывает итог выполнения Recovery.
|
||
|
||
Он создаётся исключительно после успешного завершения Recovery Pipeline.
|
||
|
||
---
|
||
|
||
## Основные поля
|
||
|
||
```text
|
||
Recovered Trades
|
||
```
|
||
|
||
Количество новых опубликованных сделок.
|
||
|
||
---
|
||
|
||
```text
|
||
Skipped Duplicates
|
||
```
|
||
|
||
Количество корректных дублей.
|
||
|
||
---
|
||
|
||
## Инварианты
|
||
|
||
Количество восстановленных сделок всегда больше либо равно нулю.
|
||
|
||
Количество пропущенных дублей всегда больше либо равно нулю.
|
||
|
||
Все значения являются согласованными относительно завершённого Recovery Pipeline.
|
||
|
||
---
|
||
|
||
## Предусловия создания
|
||
|
||
Recovery успешно завершён.
|
||
|
||
---
|
||
|
||
## Постусловия
|
||
|
||
Result полностью описывает завершённую операцию.
|
||
|
||
После создания объект не изменяется.
|
||
|
||
---
|
||
|
||
# Внутренний публичный компонент
|
||
|
||
# TradeRecoveryNormalizer
|
||
|
||
---
|
||
|
||
## Назначение
|
||
|
||
Несмотря на то, что Normalizer используется только Controller, его контракт фиксируется отдельно.
|
||
|
||
Это позволяет независимо тестировать данный компонент.
|
||
|
||
---
|
||
|
||
## Публичный контракт
|
||
|
||
```python
|
||
normalize(
|
||
trades: tuple[Trade, ...],
|
||
) -> tuple[Trade, ...]
|
||
```
|
||
|
||
---
|
||
|
||
## Входные параметры
|
||
|
||
Последовательность Canonical Trade.
|
||
|
||
---
|
||
|
||
## Возвращаемое значение
|
||
|
||
Та же последовательность,
|
||
|
||
но гарантированно приведённая к каноническому порядку.
|
||
|
||
---
|
||
|
||
## Возможные исключения
|
||
|
||
```text
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
---
|
||
|
||
## Предусловия
|
||
|
||
Все элементы коллекции являются корректными объектами Trade.
|
||
|
||
---
|
||
|
||
## Постусловия
|
||
|
||
Количество элементов сохраняется.
|
||
|
||
Ни один объект Trade не изменяется.
|
||
|
||
Допускается изменение исключительно порядка следования элементов.
|
||
|
||
---
|
||
|
||
# Контракт взаимодействия компонентов
|
||
|
||
Настоящий раздел определяет допустимые направления вызовов между компонентами Recovery.
|
||
|
||
---
|
||
|
||
# Допустимые зависимости
|
||
|
||
TradeRecoveryController имеет право зависеть от:
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
DzengiTradesDocumentSource
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
REST Adapter
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryNormalizer
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Других зависимостей Controller иметь не должен.
|
||
|
||
---
|
||
|
||
# Недопустимые зависимости
|
||
|
||
TradeRecoveryController не должен зависеть от:
|
||
|
||
- Runtime;
|
||
- Reconnect;
|
||
- Subscription Manager;
|
||
- WebSocket Transport;
|
||
- Event Bus;
|
||
- Scheduler;
|
||
- Timer;
|
||
- Retry Engine.
|
||
|
||
Появление подобных зависимостей рассматривается как нарушение архитектуры Build 060.19.
|
||
|
||
---
|
||
|
||
# Контракт TradeRecoveryNormalizer
|
||
|
||
Normalizer представляет собой полностью детерминированную функцию.
|
||
|
||
Он зависит исключительно от:
|
||
|
||
```text
|
||
tuple[Trade]
|
||
```
|
||
|
||
Никакие другие объекты системы ему не требуются.
|
||
|
||
---
|
||
|
||
## Запрещённые зависимости Normalizer
|
||
|
||
Normalizer никогда не взаимодействует с:
|
||
|
||
- REST;
|
||
- Runtime;
|
||
- WebSocket;
|
||
- Controller;
|
||
- Repository;
|
||
- Cache;
|
||
- Configuration;
|
||
- Logger.
|
||
|
||
Это обеспечивает возможность полностью изолированного тестирования.
|
||
|
||
---
|
||
|
||
# Контракт взаимодействия с REST Pipeline
|
||
|
||
Recovery не обращается к REST API напрямую.
|
||
|
||
Единственная допустимая точка взаимодействия —
|
||
|
||
существующий транспортный источник.
|
||
|
||
Архитектурная схема выглядит следующим образом.
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
|
||
↓
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
↓
|
||
|
||
REST API
|
||
```
|
||
|
||
Recovery не формирует HTTP-запросы самостоятельно.
|
||
|
||
Recovery не знает формат REST-документа.
|
||
|
||
Recovery не знает структуру JSON.
|
||
|
||
---
|
||
|
||
# Контракт взаимодействия с Parser
|
||
|
||
Recovery никогда не вызывает Parser напрямую.
|
||
|
||
Вызов Parser осуществляется исключительно существующим REST Adapter.
|
||
|
||
Таким образом сохраняется единый Pipeline преобразования транспортных данных.
|
||
|
||
---
|
||
|
||
# Контракт взаимодействия с Mapper
|
||
|
||
Recovery никогда не создаёт объект Trade самостоятельно.
|
||
|
||
Появление Canonical Trade возможно исключительно после успешного завершения Mapper.
|
||
|
||
Recovery использует только уже готовые доменные объекты.
|
||
|
||
---
|
||
|
||
# Контракт взаимодействия со Stream Consistency
|
||
|
||
TradeRecoveryController взаимодействует с TradeStreamConsistencyController исключительно через его публичный API.
|
||
|
||
Recovery запрещается:
|
||
|
||
- изменять внутреннее состояние Controller;
|
||
- обращаться к внутренним коллекциям;
|
||
- использовать приватные методы;
|
||
- обходить механизм `accept()`.
|
||
|
||
Таким образом сохраняется строгая инкапсуляция Consistency Layer.
|
||
|
||
---
|
||
|
||
# Контракт взаимодействия с Runtime
|
||
|
||
В Build 060.19 Runtime не является участником Recovery Pipeline.
|
||
|
||
Единственная допустимая форма взаимодействия:
|
||
|
||
```text
|
||
Runtime
|
||
|
||
↓
|
||
|
||
TradeRecoveryController
|
||
|
||
↓
|
||
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Runtime рассматривается исключительно как вызывающая сторона.
|
||
|
||
Recovery не знает о его внутреннем устройстве.
|
||
|
||
---
|
||
|
||
# Контракт расширяемости
|
||
|
||
Recovery проектируется с учётом последующего развития системы.
|
||
|
||
Следующие компоненты могут быть добавлены без изменения существующего публичного API.
|
||
|
||
Например:
|
||
|
||
```text
|
||
Retry Policy
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
Metrics
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
Tracing
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
Telemetry
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
Performance Monitor
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
Recovery Statistics
|
||
```
|
||
|
||
Все перечисленные расширения должны использовать композицию.
|
||
|
||
Изменение публичного API Recovery не допускается.
|
||
|
||
---
|
||
|
||
# Контракт потокобезопасности
|
||
|
||
Build 060.19 не накладывает требований на многопоточную обработку.
|
||
|
||
Recovery выполняет один запрос восстановления за один вызов.
|
||
|
||
Параллельное выполнение нескольких Recovery Pipeline не входит в Scope настоящего Build.
|
||
|
||
Вопрос конкурентного восстановления нескольких символов рассматривается в последующих Build.
|
||
|
||
---
|
||
|
||
# Контракт детерминированности
|
||
|
||
Recovery обязан удовлетворять следующему свойству.
|
||
|
||
При одинаковых:
|
||
|
||
- Recovery Request;
|
||
- ответе REST;
|
||
- состоянии TradeStreamConsistencyController;
|
||
|
||
результат выполнения обязан быть идентичным.
|
||
|
||
Данное свойство считается обязательным архитектурным инвариантом публичного API.
|
||
|
||
---
|
||
|
||
# Контракт обратной совместимости
|
||
|
||
После утверждения настоящего документа публичный API Recovery считается стабильным.
|
||
|
||
Изменение следующих сущностей требует подготовки нового архитектурного решения (ADR):
|
||
|
||
- сигнатуры `recover()`;
|
||
- структуры `TradeRecoveryRequest`;
|
||
- структуры `TradeRecoveryResult`;
|
||
- публичного контракта `TradeRecoveryNormalizer`.
|
||
|
||
Допускается только расширение функциональности без нарушения существующих контрактов.
|
||
|
||
---
|
||
|
||
# Матрица ответственности публичных компонентов
|
||
|
||
| Компонент | Основная ответственность | Не отвечает за |
|
||
|-----------|--------------------------|----------------|
|
||
| TradeRecoveryController | Оркестрация полного процесса Recovery | Runtime, Retry, Deduplication, Ordering |
|
||
| TradeRecoveryRequest | Описание параметров восстановления | Вычисление диапазона восстановления |
|
||
| TradeRecoveryNormalizer | Нормализация порядка сделок | REST, Validation, Deduplication |
|
||
| TradeRecoveryResult | Представление результата Recovery | Хранение внутреннего состояния Recovery |
|
||
|
||
---
|
||
|
||
# Матрица владения бизнес-правилами
|
||
|
||
| Бизнес-правило | Владелец |
|
||
|----------------|----------|
|
||
| Получение исторических данных | DzengiTradesDocumentSource |
|
||
| Разбор REST-документа | REST Parser |
|
||
| Проверка корректности значений | Value Validation |
|
||
| Построение Canonical Trade | REST Mapper |
|
||
| Нормализация порядка | TradeRecoveryNormalizer |
|
||
| Проверка порядка сделок | TradeStreamConsistencyController |
|
||
| Дедупликация | TradeStreamConsistencyController |
|
||
| Формирование результата Recovery | TradeRecoveryController |
|
||
|
||
---
|
||
|
||
# Итоги приложения C
|
||
|
||
Настоящее приложение фиксирует публичный API Build 060.19 и определяет архитектурные границы взаимодействия Recovery с остальными подсистемами Dzentra.
|
||
|
||
После утверждения настоящего приложения любые новые компоненты должны интегрироваться с Recovery исключительно через описанные контракты.
|
||
|
||
Это обеспечивает:
|
||
|
||
- минимальную связанность подсистем;
|
||
- стабильность публичного API;
|
||
- возможность безопасного расширения функциональности в последующих Build без нарушения существующей архитектуры.
|
||
|
||
---
|
||
|
||
# Приложение D. Сценарии выполнения Recovery
|
||
|
||
---
|
||
|
||
# Назначение приложения
|
||
|
||
Настоящее приложение содержит нормативные сценарии выполнения Build 060.19.
|
||
|
||
Если приложение B описывает последовательности взаимодействия компонентов, то настоящее приложение описывает ожидаемое поведение Recovery при различных входных данных.
|
||
|
||
Каждый сценарий фиксирует:
|
||
|
||
- исходное состояние системы;
|
||
- последовательность действий;
|
||
- ожидаемый результат;
|
||
- архитектурные инварианты.
|
||
|
||
Данные сценарии являются эталоном для разработки интеграционных тестов Build 060.19.
|
||
|
||
---
|
||
|
||
# Scenario 1
|
||
|
||
# Полное успешное восстановление
|
||
|
||
## Исходное состояние
|
||
|
||
В системе существует активный Canonical Trade Stream.
|
||
|
||
Recovery получает корректный диапазон времени.
|
||
|
||
REST API возвращает четыре сделки.
|
||
|
||
```text
|
||
140
|
||
|
||
121
|
||
|
||
105
|
||
|
||
100
|
||
```
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. RecoveryController получает TradeRecoveryRequest.
|
||
|
||
2. Выполняется REST-запрос.
|
||
|
||
3. REST Adapter строит четыре объекта Trade.
|
||
|
||
4. TradeRecoveryNormalizer определяет обратный порядок.
|
||
|
||
5. Последовательность нормализуется.
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
|
||
140
|
||
```
|
||
|
||
6. Каждая сделка последовательно передаётся в TradeStreamConsistencyController.
|
||
|
||
7. Все сделки успешно принимаются.
|
||
|
||
8. Формируется TradeRecoveryResult.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
```text
|
||
Recovered Trades = 4
|
||
|
||
Skipped Duplicates = 0
|
||
```
|
||
|
||
Recovery завершается успешно.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
- нормализация выполнена;
|
||
- все сделки прошли через Stream Consistency;
|
||
- ни одна сделка не была изменена;
|
||
- результат сформирован.
|
||
|
||
---
|
||
|
||
# Scenario 2
|
||
|
||
# В указанном диапазоне отсутствуют сделки
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает пустую последовательность.
|
||
|
||
```text
|
||
()
|
||
```
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. RecoveryController выполняет REST-запрос.
|
||
|
||
2. Parser успешно обрабатывает ответ.
|
||
|
||
3. Mapper формирует пустую коллекцию.
|
||
|
||
4. Normalizer получает пустую последовательность.
|
||
|
||
5. Stream Consistency не вызывается.
|
||
|
||
6. Формируется TradeRecoveryResult.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
```text
|
||
Recovered Trades = 0
|
||
|
||
Skipped Duplicates = 0
|
||
```
|
||
|
||
Recovery завершается успешно.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Пустой диапазон не считается ошибкой.
|
||
|
||
---
|
||
|
||
# Scenario 3
|
||
|
||
# Все сделки являются корректными дубликатами
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает:
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
```
|
||
|
||
Все три сделки уже присутствуют в Canonical Trade Stream.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
Каждая сделка последовательно передаётся Controller.
|
||
|
||
Controller возвращает:
|
||
|
||
```python
|
||
None
|
||
```
|
||
|
||
для каждой сделки.
|
||
|
||
После завершения обработки создаётся TradeRecoveryResult.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
```text
|
||
Recovered Trades = 0
|
||
|
||
Skipped Duplicates = 3
|
||
```
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Recovery не считает корректные дубликаты ошибкой.
|
||
|
||
---
|
||
|
||
# Scenario 4
|
||
|
||
# Частичное восстановление диапазона
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает:
|
||
|
||
```text
|
||
140
|
||
|
||
121
|
||
|
||
105
|
||
|
||
100
|
||
```
|
||
|
||
Controller определяет:
|
||
|
||
```text
|
||
100 -> duplicate
|
||
|
||
105 -> accepted
|
||
|
||
121 -> accepted
|
||
|
||
140 -> accepted
|
||
```
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
Recovery нормализует поток.
|
||
|
||
Каждая сделка проходит через Controller.
|
||
|
||
Дубликат исключается.
|
||
|
||
Три новые сделки публикуются.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
```text
|
||
Recovered Trades = 3
|
||
|
||
Skipped Duplicates = 1
|
||
```
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Recovery не прекращает выполнение при обнаружении корректного дубликата.
|
||
|
||
---
|
||
|
||
# Scenario 5
|
||
|
||
# REST возвращает ASCENDING последовательность
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает:
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
|
||
140
|
||
```
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
Normalizer анализирует направление.
|
||
|
||
Последовательность уже соответствует Canonical Trade Stream.
|
||
|
||
Изменения не выполняются.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
Все сделки передаются Controller в первоначальном порядке.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Normalizer не выполняет лишних преобразований.
|
||
|
||
Последовательность сохраняется без изменений.
|
||
|
||
---
|
||
|
||
# Scenario 6
|
||
|
||
# REST возвращает DESCENDING последовательность
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает:
|
||
|
||
```text
|
||
140
|
||
|
||
121
|
||
|
||
105
|
||
|
||
100
|
||
```
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
Normalizer определяет обратное направление.
|
||
|
||
Выполняется нормализация.
|
||
|
||
Полученный поток передаётся Controller.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
Controller получает:
|
||
|
||
```text
|
||
100
|
||
|
||
105
|
||
|
||
121
|
||
|
||
140
|
||
```
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
TradeStreamConsistencyController никогда не получает последовательность в обратном порядке.
|
||
|
||
---
|
||
|
||
# Scenario 7
|
||
|
||
# REST возвращает смешанный порядок
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает последовательность:
|
||
|
||
```text
|
||
100
|
||
|
||
150
|
||
|
||
121
|
||
|
||
180
|
||
```
|
||
|
||
Последовательность не является ни возрастающей, ни убывающей.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. RecoveryController получает результат REST Pipeline.
|
||
|
||
2. Последовательность передаётся в TradeRecoveryNormalizer.
|
||
|
||
3. Normalizer анализирует направление последовательности.
|
||
|
||
4. Обнаруживается нарушение архитектурного инварианта.
|
||
|
||
5. Генерируется:
|
||
|
||
```text
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
6. Выполнение Recovery прекращается.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
Recovery завершается ошибкой.
|
||
|
||
TradeStreamConsistencyController не вызывается.
|
||
|
||
TradeRecoveryResult не создаётся.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
- смешанная последовательность не допускается;
|
||
- Recovery не пытается исправить поток;
|
||
- никакие сделки не публикуются.
|
||
|
||
---
|
||
|
||
# Scenario 8
|
||
|
||
# Ошибка Parser
|
||
|
||
## Исходное состояние
|
||
|
||
REST возвращает документ с нарушением транспортного контракта.
|
||
|
||
Parser обнаруживает ошибку структуры.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. RecoveryController инициирует получение истории.
|
||
|
||
2. REST Adapter вызывает Parser.
|
||
|
||
3. Parser генерирует исключение.
|
||
|
||
4. Recovery прекращает выполнение.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
TradeRecoveryResult отсутствует.
|
||
|
||
Исключение распространяется вызывающему компоненту.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Recovery не вмешивается в транспортную обработку данных.
|
||
|
||
Parser остаётся единственным владельцем логики разбора REST-документа.
|
||
|
||
---
|
||
|
||
# Scenario 9
|
||
|
||
# Ошибка Value Validation
|
||
|
||
## Исходное состояние
|
||
|
||
Parser успешно завершил обработку.
|
||
|
||
Во время проверки значений обнаружено нарушение одного из инвариантов Canonical Trade.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. Parser завершает работу.
|
||
|
||
2. Запускается Value Validation.
|
||
|
||
3. Validation обнаруживает некорректное значение.
|
||
|
||
4. Генерируется исключение Validation.
|
||
|
||
5. Recovery прекращает выполнение.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
TradeRecoveryResult отсутствует.
|
||
|
||
Никакие сделки не публикуются.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Recovery никогда не работает с объектами, не прошедшими Validation.
|
||
|
||
---
|
||
|
||
# Scenario 10
|
||
|
||
# Ошибка Mapper
|
||
|
||
## Исходное состояние
|
||
|
||
Parser и Validation успешно завершены.
|
||
|
||
Mapper не способен сформировать объект Canonical Trade.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. Recovery получает транспортные данные.
|
||
|
||
2. Mapper генерирует исключение.
|
||
|
||
3. Recovery немедленно завершает выполнение.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
TradeRecoveryResult отсутствует.
|
||
|
||
TradeRecoveryNormalizer не вызывается.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Recovery никогда не получает частично сформированные объекты Trade.
|
||
|
||
---
|
||
|
||
# Scenario 11
|
||
|
||
# Ошибка TradeStreamConsistencyController
|
||
|
||
## Исходное состояние
|
||
|
||
REST Pipeline завершился успешно.
|
||
|
||
Normalizer успешно выполнил нормализацию.
|
||
|
||
Во время обработки одной из сделок Controller обнаруживает нарушение инвариантов потока.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. Recovery начинает последовательную передачу сделок.
|
||
|
||
2. Первые сделки успешно принимаются.
|
||
|
||
3. Для очередной сделки Controller генерирует:
|
||
|
||
```text
|
||
TradeOrderingError
|
||
```
|
||
|
||
или
|
||
|
||
```text
|
||
TradeConsistencyError
|
||
```
|
||
|
||
4. Recovery прекращает выполнение.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
TradeRecoveryResult отсутствует.
|
||
|
||
Исключение распространяется вызывающему компоненту.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Recovery не скрывает ошибки Stream Consistency.
|
||
|
||
Recovery не пытается продолжить обработку.
|
||
|
||
---
|
||
|
||
# Scenario 12
|
||
|
||
# Ошибка получения исторических данных
|
||
|
||
## Исходное состояние
|
||
|
||
Во время обращения к REST API возникает транспортная ошибка.
|
||
|
||
Например:
|
||
|
||
- Timeout;
|
||
- Network Error;
|
||
- HTTP Error.
|
||
|
||
---
|
||
|
||
## Последовательность выполнения
|
||
|
||
1. RecoveryController вызывает DzengiTradesDocumentSource.
|
||
|
||
2. Источник генерирует исключение.
|
||
|
||
3. Recovery немедленно завершает выполнение.
|
||
|
||
---
|
||
|
||
## Ожидаемый результат
|
||
|
||
TradeRecoveryResult отсутствует.
|
||
|
||
Повторный запрос не выполняется.
|
||
|
||
---
|
||
|
||
## Проверяемые инварианты
|
||
|
||
Retry Policy отсутствует.
|
||
|
||
Recovery выполняет только одну попытку получения истории.
|
||
|
||
---
|
||
|
||
# Матрица покрытия сценариев
|
||
|
||
| Сценарий | REST | Normalizer | Stream Consistency | Result |
|
||
|-----------|------|------------|--------------------|--------|
|
||
| Полное восстановление | ✓ | ✓ | ✓ | ✓ |
|
||
| Пустой диапазон | ✓ | ✓ | — | ✓ |
|
||
| Полные дубликаты | ✓ | ✓ | ✓ | ✓ |
|
||
| Частичное восстановление | ✓ | ✓ | ✓ | ✓ |
|
||
| ASCENDING | ✓ | ✓ | ✓ | ✓ |
|
||
| DESCENDING | ✓ | ✓ | ✓ | ✓ |
|
||
| Смешанный порядок | ✓ | ✗ | — | ✗ |
|
||
| Ошибка Parser | ✗ | — | — | ✗ |
|
||
| Ошибка Validation | ✗ | — | — | ✗ |
|
||
| Ошибка Mapper | ✗ | — | — | ✗ |
|
||
| Ошибка Consistency | ✓ | ✓ | ✗ | ✗ |
|
||
| Ошибка REST | ✗ | — | — | ✗ |
|
||
|
||
---
|
||
|
||
# Использование сценариев
|
||
|
||
Настоящие сценарии являются нормативной моделью поведения Recovery.
|
||
|
||
Они используются как основа для:
|
||
|
||
- Unit-тестирования;
|
||
- Integration-тестирования;
|
||
- проверки архитектурных инвариантов;
|
||
- регрессионного тестирования последующих Build серии 060.
|
||
|
||
Каждый новый сценарий, добавляемый в Recovery, должен сопровождаться соответствующим дополнением настоящего приложения.
|
||
|
||
---
|
||
|
||
# Итоги приложения D
|
||
|
||
Настоящее приложение описывает эталонное поведение подсистемы Recovery в наиболее важных эксплуатационных ситуациях.
|
||
|
||
Любая реализация Build 060.19 должна демонстрировать поведение, полностью соответствующее данным сценариям.
|
||
|
||
Отклонение от описанных сценариев допускается только после внесения изменений в архитектурную спецификацию и утверждения нового Architecture Decision Record (ADR).
|
||
|
||
---
|
||
|
||
# Приложение E. План реализации Build 060.19
|
||
|
||
---
|
||
|
||
# Назначение приложения
|
||
|
||
Настоящее приложение определяет официальный план реализации Build 060.19.
|
||
|
||
Основной документ фиксирует архитектуру Recovery.
|
||
|
||
Настоящее приложение определяет последовательность разработки, которая обеспечивает:
|
||
|
||
- минимальные изменения существующего кода;
|
||
- отсутствие нарушения архитектуры предыдущих Build;
|
||
- возможность тестирования каждого этапа отдельно;
|
||
- безопасную интеграцию Recovery в существующую систему.
|
||
|
||
Все этапы должны выполняться строго последовательно.
|
||
|
||
Переход к следующему этапу допускается только после полного завершения предыдущего.
|
||
|
||
---
|
||
|
||
# Общая последовательность реализации
|
||
|
||
```text
|
||
Этап 1
|
||
|
||
Recovery Domain Models
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Этап 2
|
||
|
||
Recovery Normalizer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Этап 3
|
||
|
||
Recovery Controller
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Этап 4
|
||
|
||
Unit Tests
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Этап 5
|
||
|
||
Integration Tests
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Build Complete
|
||
```
|
||
|
||
---
|
||
|
||
# Этап 1
|
||
|
||
# Recovery Domain Models
|
||
|
||
---
|
||
|
||
## Цель
|
||
|
||
Создать все доменные модели Build 060.19.
|
||
|
||
На данном этапе не реализуется никакая бизнес-логика.
|
||
|
||
Создаются исключительно структуры данных.
|
||
|
||
---
|
||
|
||
## Новые сущности
|
||
|
||
Минимальный состав моделей:
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryError
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryRequestError
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryNormalizationError
|
||
```
|
||
|
||
---
|
||
|
||
## Требования
|
||
|
||
Все модели должны быть:
|
||
|
||
- immutable;
|
||
- полностью типизированными;
|
||
- независимыми от Runtime;
|
||
- независимыми от REST.
|
||
|
||
---
|
||
|
||
## Не допускается
|
||
|
||
На данном этапе запрещается:
|
||
|
||
- выполнять Recovery;
|
||
- обращаться к REST;
|
||
- создавать Controller;
|
||
- изменять существующий Pipeline.
|
||
|
||
---
|
||
|
||
## Критерии завершения
|
||
|
||
Этап считается завершённым, если:
|
||
|
||
- все модели реализованы;
|
||
- модели проходят статическую проверку типов;
|
||
- модели покрыты Unit-тестами.
|
||
|
||
---
|
||
|
||
# Этап 2
|
||
|
||
# TradeRecoveryNormalizer
|
||
|
||
---
|
||
|
||
## Цель
|
||
|
||
Реализовать алгоритм нормализации порядка сделок.
|
||
|
||
---
|
||
|
||
## Функциональность
|
||
|
||
Normalizer обязан:
|
||
|
||
- принимать tuple[Trade];
|
||
- определять направление последовательности;
|
||
- возвращать ASCENDING поток;
|
||
- обнаруживать смешанный порядок;
|
||
- генерировать TradeRecoveryNormalizationError.
|
||
|
||
---
|
||
|
||
## Не допускается
|
||
|
||
Normalizer не должен:
|
||
|
||
- обращаться к REST;
|
||
- использовать Runtime;
|
||
- использовать Controller;
|
||
- выполнять дедупликацию;
|
||
- изменять Trade.
|
||
|
||
---
|
||
|
||
## Минимальный набор тестов
|
||
|
||
Проверяются сценарии:
|
||
|
||
- пустой поток;
|
||
- одна сделка;
|
||
- ASCENDING;
|
||
- DESCENDING;
|
||
- смешанный порядок.
|
||
|
||
---
|
||
|
||
## Критерии завершения
|
||
|
||
Этап считается завершённым после успешного прохождения всех Unit-тестов Normalizer.
|
||
|
||
---
|
||
|
||
# Этап 3
|
||
|
||
# TradeRecoveryController
|
||
|
||
---
|
||
|
||
## Цель
|
||
|
||
Реализовать центральный компонент Recovery.
|
||
|
||
---
|
||
|
||
## Функциональность
|
||
|
||
Controller обязан:
|
||
|
||
- принять Recovery Request;
|
||
- вызвать существующий REST Pipeline;
|
||
- вызвать Normalizer;
|
||
- передать сделки в TradeStreamConsistencyController;
|
||
- сформировать TradeRecoveryResult.
|
||
|
||
---
|
||
|
||
## Используемые компоненты
|
||
|
||
Controller использует только существующие компоненты системы.
|
||
|
||
Никаких новых транспортных механизмов не создаётся.
|
||
|
||
---
|
||
|
||
## Не допускается
|
||
|
||
Controller не должен:
|
||
|
||
- вычислять диапазон времени;
|
||
- выполнять Retry;
|
||
- работать с Runtime;
|
||
- работать с WebSocket;
|
||
- выполнять дедупликацию.
|
||
|
||
---
|
||
|
||
## Критерии завершения
|
||
|
||
Controller успешно проходит все Unit-тесты.
|
||
|
||
---
|
||
|
||
# Этап 4
|
||
|
||
# Unit Testing
|
||
|
||
---
|
||
|
||
## Цель
|
||
|
||
Проверить корректность каждого компонента Recovery изолированно.
|
||
|
||
---
|
||
|
||
## Проверяемые компоненты
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryNormalizer
|
||
```
|
||
|
||
---
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
```
|
||
|
||
---
|
||
|
||
## Требования
|
||
|
||
Unit-тесты не должны обращаться:
|
||
|
||
- к REST API;
|
||
- к Runtime;
|
||
- к WebSocket.
|
||
|
||
Все внешние зависимости заменяются Mock-объектами.
|
||
|
||
---
|
||
|
||
## Критерии завершения
|
||
|
||
Все Unit-тесты успешно проходят.
|
||
|
||
Покрываются все архитектурные сценарии, определённые приложением D.
|
||
|
||
---
|
||
|
||
# Этап 5
|
||
|
||
# Integration Testing
|
||
|
||
---
|
||
|
||
## Цель
|
||
|
||
Проверить корректность работы Recovery как единой подсистемы Acquisition Layer.
|
||
|
||
На данном этапе тестируются взаимодействия между уже существующими компонентами системы.
|
||
|
||
Проверяется не отдельная логика компонентов, а корректность всей цепочки обработки исторических сделок.
|
||
|
||
---
|
||
|
||
## Интегрируемые компоненты
|
||
|
||
В интеграционных тестах участвуют:
|
||
|
||
```text
|
||
DzengiTradesDocumentSource
|
||
```
|
||
|
||
↓
|
||
|
||
```text
|
||
REST Adapter
|
||
```
|
||
|
||
↓
|
||
|
||
```text
|
||
TradeRecoveryNormalizer
|
||
```
|
||
|
||
↓
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
↓
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
---
|
||
|
||
## Проверяемые сценарии
|
||
|
||
Минимальный набор интеграционных тестов включает:
|
||
|
||
### Сценарий 1
|
||
|
||
Полное успешное восстановление.
|
||
|
||
---
|
||
|
||
### Сценарий 2
|
||
|
||
Пустой диапазон.
|
||
|
||
---
|
||
|
||
### Сценарий 3
|
||
|
||
Полное дублирование.
|
||
|
||
---
|
||
|
||
### Сценарий 4
|
||
|
||
Частичное восстановление.
|
||
|
||
---
|
||
|
||
### Сценарий 5
|
||
|
||
REST возвращает DESCENDING поток.
|
||
|
||
---
|
||
|
||
### Сценарий 6
|
||
|
||
REST возвращает ASCENDING поток.
|
||
|
||
---
|
||
|
||
### Сценарий 7
|
||
|
||
REST возвращает смешанную последовательность.
|
||
|
||
---
|
||
|
||
### Сценарий 8
|
||
|
||
TradeStreamConsistencyController обнаруживает ошибку порядка.
|
||
|
||
---
|
||
|
||
### Сценарий 9
|
||
|
||
REST Pipeline завершается исключением.
|
||
|
||
---
|
||
|
||
## Критерии завершения
|
||
|
||
Этап считается завершённым при выполнении следующих условий.
|
||
|
||
- Все интеграционные тесты успешно проходят.
|
||
- Все архитектурные инварианты подтверждены.
|
||
- Ни один существующий Build не требует изменения своей архитектуры.
|
||
- Recovery полностью совместим с существующим Canonical Trade Stream.
|
||
|
||
---
|
||
|
||
# Проверка соответствия архитектуре
|
||
|
||
После завершения реализации выполняется архитектурная проверка Build.
|
||
|
||
Проверяются следующие требования.
|
||
|
||
---
|
||
|
||
## Проверка №1
|
||
|
||
Recovery использует существующий REST Pipeline.
|
||
|
||
---
|
||
|
||
## Проверка №2
|
||
|
||
Recovery использует существующий TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
## Проверка №3
|
||
|
||
Recovery не содержит собственной реализации Deduplication.
|
||
|
||
---
|
||
|
||
## Проверка №4
|
||
|
||
Recovery не содержит собственной реализации Ordering.
|
||
|
||
---
|
||
|
||
## Проверка №5
|
||
|
||
Recovery не зависит от Runtime.
|
||
|
||
---
|
||
|
||
## Проверка №6
|
||
|
||
Recovery не зависит от WebSocket Transport.
|
||
|
||
---
|
||
|
||
## Проверка №7
|
||
|
||
Recovery не изменяет Canonical Trade Model.
|
||
|
||
---
|
||
|
||
## Проверка №8
|
||
|
||
Recovery не изменяет Parser.
|
||
|
||
---
|
||
|
||
## Проверка №9
|
||
|
||
Recovery не изменяет Mapper.
|
||
|
||
---
|
||
|
||
## Проверка №10
|
||
|
||
Recovery не изменяет Value Validation.
|
||
|
||
---
|
||
|
||
## Проверка №11
|
||
|
||
Recovery не нарушает архитектурные решения Build 060.18.
|
||
|
||
---
|
||
|
||
# Definition of Done (DoD)
|
||
|
||
Build 060.19 считается завершённым только при выполнении всех перечисленных условий.
|
||
|
||
---
|
||
|
||
## Архитектура
|
||
|
||
- Архитектура полностью соответствует настоящему документу.
|
||
- Все ADR соблюдены.
|
||
- Все архитектурные инварианты сохранены.
|
||
|
||
---
|
||
|
||
## Код
|
||
|
||
- Реализованы все модели Recovery.
|
||
- Реализован TradeRecoveryNormalizer.
|
||
- Реализован TradeRecoveryController.
|
||
- Не изменены существующие публичные контракты предыдущих Build без отдельного ADR.
|
||
|
||
---
|
||
|
||
## Качество
|
||
|
||
- Код проходит статическую проверку типов.
|
||
- Код соответствует принятому стилю проекта.
|
||
- Новые сущности имеют уникальные имена файлов.
|
||
- Не допущено дублирование существующей функциональности.
|
||
|
||
---
|
||
|
||
## Тестирование
|
||
|
||
- Все Unit-тесты проходят успешно.
|
||
- Все Integration-тесты проходят успешно.
|
||
- Покрыты все сценарии, описанные в Приложении D.
|
||
|
||
---
|
||
|
||
## Документация
|
||
|
||
Подготовлены и согласованы:
|
||
|
||
- `build_060_19_architecture.md`;
|
||
- `build_060_19.md` (Engineering Report);
|
||
- комментарии в коде (при необходимости);
|
||
- обновлены внутренние ссылки на связанные Build.
|
||
|
||
---
|
||
|
||
# Зависимости от последующих Build
|
||
|
||
Build 060.19 сознательно ограничивает собственную область ответственности.
|
||
|
||
Следующие задачи не входят в его Scope и будут реализованы позже.
|
||
|
||
---
|
||
|
||
## Build 060.20
|
||
|
||
Trade Recovery Registry.
|
||
|
||
Хранение и управление состоянием восстановления для нескольких символов.
|
||
|
||
---
|
||
|
||
## Build 060.21
|
||
|
||
Recovery Acquisition Protocol.
|
||
|
||
Определение протокола взаимодействия Runtime и Recovery.
|
||
|
||
---
|
||
|
||
## Build 060.22
|
||
|
||
Recovery Acquisition Service.
|
||
|
||
Высокоуровневый сервис восстановления, объединяющий Registry и Controller.
|
||
|
||
---
|
||
|
||
## Build 060.23
|
||
|
||
Runtime Integration.
|
||
|
||
Интеграция Recovery с жизненным циклом Runtime.
|
||
|
||
---
|
||
|
||
## Build 060.24
|
||
|
||
Reconnect Integration.
|
||
|
||
Автоматический запуск Recovery после восстановления WebSocket-соединения.
|
||
|
||
---
|
||
|
||
## Build 060.25
|
||
|
||
Полная интеграция Trades Feed.
|
||
|
||
Объединение потоковых и исторических сделок в единый производственный Pipeline.
|
||
|
||
---
|
||
|
||
## Build 060.26
|
||
|
||
Финальная документация, регрессионный аудит и подтверждение соответствия всей серии Build 060.
|
||
|
||
---
|
||
|
||
# Заключение
|
||
|
||
Build 060.19 завершает следующий важный этап развития подсистемы Trades Feed.
|
||
|
||
После его реализации система впервые получает архитектурно корректный механизм восстановления исторических сделок, построенный на уже существующих компонентах без нарушения их ответственности.
|
||
|
||
Recovery не создаёт альтернативную модель обработки данных и не дублирует ранее реализованную функциональность. Вместо этого он объединяет:
|
||
|
||
- существующий REST Pipeline;
|
||
- существующую Canonical Trade Model;
|
||
- существующий TradeStreamConsistencyController;
|
||
|
||
в единый механизм восстановления истории.
|
||
|
||
Это решение обеспечивает:
|
||
|
||
- единый источник истины для проверки согласованности потока;
|
||
- минимальную связанность подсистем;
|
||
- высокую тестируемость;
|
||
- возможность безопасного масштабирования архитектуры в следующих Build.
|
||
|
||
Настоящий документ завершает архитектурное проектирование Build 060.19 и является нормативной спецификацией, на основании которой должна выполняться реализация.
|
||
|
||
---
|
||
|
||
# Приложение F. Архитектурная совместимость и дальнейшее развитие Build 060.19
|
||
|
||
---
|
||
|
||
# Назначение приложения
|
||
|
||
Настоящее приложение определяет место Build 060.19 в архитектуре Dzentra серии Build 060.
|
||
|
||
Оно фиксирует:
|
||
|
||
- архитектурные зависимости;
|
||
- совместимость с предыдущими Build;
|
||
- влияние на существующие подсистемы;
|
||
- направления дальнейшего развития Recovery;
|
||
- гарантии обратной совместимости.
|
||
|
||
Данное приложение служит контрольной точкой эволюции архитектуры Trades Feed.
|
||
|
||
---
|
||
|
||
# Место Build 060.19 в серии Build 060
|
||
|
||
Build 060.19 является первым Build серии, реализующим механизм восстановления исторических сделок (Trade Recovery).
|
||
|
||
Recovery не создаёт новую модель обработки данных.
|
||
|
||
Recovery использует существующие архитектурные компоненты и объединяет их в единый процесс восстановления истории.
|
||
|
||
Таким образом Build 060.19 является интеграционным Build, а не Build, изменяющим фундаментальную архитектуру Trades Feed.
|
||
|
||
---
|
||
|
||
# Архитектурные зависимости
|
||
|
||
Build 060.19 непосредственно зависит только от следующих компонентов.
|
||
|
||
```text
|
||
Canonical Trade Model
|
||
```
|
||
|
||
↓
|
||
|
||
```text
|
||
REST Trade Pipeline
|
||
```
|
||
|
||
↓
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
Других обязательных архитектурных зависимостей Recovery не имеет.
|
||
|
||
---
|
||
|
||
# Совместимость с предыдущими Build
|
||
|
||
Build 060.19 полностью совместим со всеми ранее утверждёнными Build серии 060.
|
||
|
||
| Build | Назначение | Совместимость |
|
||
|--------|------------|---------------|
|
||
| Build 060.1 | Canonical Trade Model | Полная |
|
||
| Build 060.2–060.8 | Базовые доменные модели и инфраструктура | Полная |
|
||
| Build 060.9 | REST Trade Pipeline | Полная |
|
||
| Build 060.10–060.16 | Парсинг, Validation, Mapping | Полная |
|
||
| Build 060.17 | Stream Consistency | Полная |
|
||
| Build 060.18 | REST Pipeline Integration | Полная |
|
||
|
||
Ни один из перечисленных Build не требует модификации для внедрения Recovery.
|
||
|
||
---
|
||
|
||
# Build, которые Recovery не изменяет
|
||
|
||
Build 060.19 сознательно не изменяет архитектуру следующих компонентов.
|
||
|
||
- Canonical Trade;
|
||
- REST Parser;
|
||
- Value Validation;
|
||
- REST Mapper;
|
||
- REST Adapter;
|
||
- TradeStreamConsistencyController;
|
||
- Transport Layer;
|
||
- Exchange Integration;
|
||
- Runtime.
|
||
|
||
Все перечисленные подсистемы используются без изменения их ответственности.
|
||
|
||
---
|
||
|
||
# Build, использующие Recovery
|
||
|
||
После завершения Build 060.19 механизм Recovery становится частью архитектурного фундамента следующих Build.
|
||
|
||
| Build | Использование Recovery |
|
||
|--------|------------------------|
|
||
| Build 060.20 | Registry хранения состояния Recovery |
|
||
| Build 060.21 | Acquisition Protocol |
|
||
| Build 060.22 | Acquisition Service |
|
||
| Build 060.23 | Runtime Integration |
|
||
| Build 060.24 | Reconnect Integration |
|
||
| Build 060.25 | Полная интеграция Trades Feed |
|
||
| Build 060.26 | Финальная документация и аудит |
|
||
|
||
Таким образом Recovery становится базовым компонентом всей последующей серии Build.
|
||
|
||
---
|
||
|
||
# Архитектурные гарантии
|
||
|
||
Настоящий Build гарантирует следующие свойства.
|
||
|
||
## Единственный источник проверки согласованности
|
||
|
||
Проверка порядка сделок выполняется исключительно через:
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
Recovery не реализует собственную проверку согласованности.
|
||
|
||
---
|
||
|
||
## Единственная Canonical Model
|
||
|
||
Во всей системе существует только одна модель сделки:
|
||
|
||
```text
|
||
Trade
|
||
```
|
||
|
||
Recovery не создаёт альтернативных представлений Trade.
|
||
|
||
---
|
||
|
||
## Единственный REST Pipeline
|
||
|
||
Recovery использует исключительно существующий REST Pipeline.
|
||
|
||
Создание второго Pipeline запрещается.
|
||
|
||
---
|
||
|
||
## Единственный механизм нормализации
|
||
|
||
Нормализация порядка выполняется только в пределах Recovery и исключительно перед передачей сделок в Stream Consistency.
|
||
|
||
После прохождения данного этапа все последующие компоненты работают только с Canonical ASCENDING-потоком.
|
||
|
||
---
|
||
|
||
# Архитектурные ограничения
|
||
|
||
Build 060.19 сознательно не решает следующие задачи.
|
||
|
||
- автоматический запуск Recovery;
|
||
- вычисление диапазона восстановления;
|
||
- управление несколькими символами;
|
||
- повторные попытки получения истории;
|
||
- планирование Recovery;
|
||
- мониторинг Recovery;
|
||
- сбор метрик;
|
||
- журналирование Recovery;
|
||
- хранение состояния Recovery.
|
||
|
||
Все перечисленные задачи относятся к следующим Build серии 060.
|
||
|
||
---
|
||
|
||
# Гарантии обратной совместимости
|
||
|
||
После реализации Build 060.19 гарантируется следующее.
|
||
|
||
- Все существующие Build продолжают работать без изменений.
|
||
- Существующие публичные API остаются совместимыми.
|
||
- Existing REST Pipeline продолжает использоваться без модификации.
|
||
- Canonical Trade Model остаётся неизменной.
|
||
- TradeStreamConsistencyController остаётся единственным владельцем правил согласованности Trade Stream.
|
||
|
||
Таким образом внедрение Recovery не нарушает существующую архитектуру проекта.
|
||
|
||
---
|
||
|
||
# Правила дальнейшего развития
|
||
|
||
Любое развитие Recovery должно соответствовать следующим принципам.
|
||
|
||
## Не допускается
|
||
|
||
- создание альтернативного Trade Pipeline;
|
||
- создание второго Controller согласованности;
|
||
- создание альтернативной модели Trade;
|
||
- изменение ответственности RecoveryController;
|
||
- дублирование логики TradeStreamConsistencyController.
|
||
|
||
---
|
||
|
||
## Допускается
|
||
|
||
- добавление Retry Policy;
|
||
- добавление Metrics;
|
||
- добавление Telemetry;
|
||
- добавление Tracing;
|
||
- добавление Performance Monitoring;
|
||
- расширение RecoveryResult;
|
||
- добавление новых диагностических возможностей.
|
||
|
||
Расширение допускается только при сохранении существующего публичного API.
|
||
|
||
---
|
||
|
||
# Матрица архитектурной эволюции
|
||
|
||
| Build | Статус | Роль |
|
||
|--------|--------|------|
|
||
| 060.1 | ✅ Завершён | Canonical Trade Model |
|
||
| 060.2–060.8 | ✅ Завершены | Базовая инфраструктура |
|
||
| 060.9 | ✅ Завершён | REST Trade Pipeline |
|
||
| 060.10–060.16 | ✅ Завершены | Parser / Validation / Mapper |
|
||
| 060.17 | ✅ Завершён | Stream Consistency |
|
||
| 060.18 | ✅ Завершён | REST Pipeline Integration |
|
||
| **060.19** | **🟢 Текущий Build** | **Trade Recovery** |
|
||
| 060.20 | ⏳ Следующий | Recovery Registry |
|
||
| 060.21 | ⏳ Планируется | Acquisition Protocol |
|
||
| 060.22 | ⏳ Планируется | Acquisition Service |
|
||
| 060.23 | ⏳ Планируется | Runtime Integration |
|
||
| 060.24 | ⏳ Планируется | Reconnect Integration |
|
||
| 060.25 | ⏳ Планируется | Trades Feed Integration |
|
||
| 060.26 | ⏳ Планируется | Финальный архитектурный аудит |
|
||
|
||
---
|
||
|
||
# Архитектурная зрелость Build
|
||
|
||
После завершения Build 060.19 подсистема Trades Feed получает завершённый фундамент для работы с историческими сделками.
|
||
|
||
В результате серия Build 060 впервые включает полный жизненный цикл обработки Trade.
|
||
|
||
```text
|
||
REST
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Canonical Trade
|
||
|
||
↓
|
||
|
||
Recovery Normalizer
|
||
|
||
↓
|
||
|
||
TradeStreamConsistencyController
|
||
|
||
↓
|
||
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
Следующие Build будут расширять данный Pipeline, не изменяя его фундаментальную архитектуру.
|
||
|
||
---
|
||
|
||
# Итоги приложения F
|
||
|
||
Настоящее приложение фиксирует архитектурное положение Build 060.19 в общей системе Dzentra и определяет правила его дальнейшей эволюции.
|
||
|
||
После утверждения настоящего приложения Build 060.19 рассматривается как стабильный архитектурный фундамент для всех последующих работ по подсистеме Trades Feed.
|
||
|
||
Любые изменения, затрагивающие описанные в данном приложении архитектурные гарантии, должны сопровождаться новым Architecture Decision Record (ADR) и обновлением архитектурной спецификации. |