1638 lines
75 KiB
Markdown
1638 lines
75 KiB
Markdown
# Build 060.19 — Trade Recovery
|
||
|
||
**Engineering Migration Report**
|
||
|
||
---
|
||
|
||
# Контроль документа
|
||
|
||
| Свойство | Значение |
|
||
|----------|----------|
|
||
| Build | 060.19 |
|
||
| Название | Trade Recovery |
|
||
| Статус | Completed |
|
||
| Проект | Dzentra |
|
||
| Подсистема | Market Data Acquisition |
|
||
| Компонент | Trade Recovery |
|
||
| Версия | 1.0 |
|
||
|
||
---
|
||
|
||
# Связанные документы
|
||
|
||
- build_060_19_architecture.md — архитектурная спецификация Build.
|
||
- build_060_18.md — Engineering Migration Report предыдущего Build.
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
Build 060.18 завершил построение самостоятельной подсистемы **Trade Stream Consistency**, которая стала единственной точкой формирования канонического потока сделок внутри подсистемы **Market Data Acquisition**.
|
||
|
||
После завершения предыдущего этапа система уже обеспечивала:
|
||
|
||
- построение канонической модели `Trade`;
|
||
- проверку порядка поступления сделок;
|
||
- обнаружение повторной доставки сообщений;
|
||
- обнаружение конфликтующих дубликатов;
|
||
- формирование единственного согласованного потока сделок.
|
||
|
||
Тем самым была решена задача обеспечения внутренней согласованности непрерывного потока данных.
|
||
|
||
Однако даже наличие механизма проверки согласованности не решает проблему восстановления истории.
|
||
|
||
В реальных условиях эксплуатации поток рыночных данных может быть нарушен по множеству причин.
|
||
|
||
Например:
|
||
|
||
- кратковременный разрыв WebSocket-соединения;
|
||
- задержка доставки сообщений транспортным уровнем;
|
||
- временная недоступность биржи;
|
||
- повторная подписка после реконнекта;
|
||
- восстановление работы процесса после перезапуска.
|
||
|
||
Во всех подобных ситуациях система должна иметь возможность повторно получить отсутствующие сделки через REST API и безопасно встроить их в уже существующий поток.
|
||
|
||
При этом процесс восстановления не должен:
|
||
|
||
- дублировать уже существующие сделки;
|
||
- нарушать порядок последовательности;
|
||
- обходить существующие проверки согласованности;
|
||
- использовать отдельные правила проверки.
|
||
|
||
Главной задачей настоящего Build становится построение специализированной подсистемы **Trade Recovery**, обеспечивающей безопасное восстановление последовательности сделок с использованием уже существующей архитектуры Acquisition Layer.
|
||
|
||
После завершения Build система получает:
|
||
|
||
- специализированный `TradeRecoveryProtocol`;
|
||
- специализированный `TradeRecoveryController`;
|
||
- специализированную модель `TradeRecoveryRequest`;
|
||
- специализированную модель `TradeRecoveryResult`;
|
||
- компонент нормализации восстановленных сделок;
|
||
- специализированные исключения Recovery;
|
||
- полноценное unit-тестирование новой подсистемы.
|
||
|
||
При этом Build принципиально не затрагивает:
|
||
|
||
- каноническую модель `Trade`;
|
||
- Parser;
|
||
- Mapper;
|
||
- Value Validation;
|
||
- REST Adapter;
|
||
- Trade Stream Consistency;
|
||
- WebSocket Runtime;
|
||
- Trades Feed;
|
||
- Runtime Orchestration;
|
||
- Gap Detection;
|
||
- Recovery Registry.
|
||
|
||
Все перечисленные задачи относятся к следующим этапам развития подсистемы Trades Feed.
|
||
|
||
---
|
||
|
||
# Предпосылки
|
||
|
||
К началу Build архитектура Acquisition Layer уже обеспечивала полный цикл построения канонической модели сделки и формирования согласованного потока.
|
||
|
||
Конвейер обработки выглядел следующим образом.
|
||
|
||
```text
|
||
Transport Message
|
||
│
|
||
▼
|
||
Schema Validation
|
||
│
|
||
▼
|
||
Parser
|
||
│
|
||
▼
|
||
Value Validation
|
||
│
|
||
▼
|
||
Mapper
|
||
│
|
||
▼
|
||
Trade
|
||
│
|
||
▼
|
||
Trade Stream Consistency
|
||
│
|
||
▼
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
Каждый уровень обладал собственной строго определённой областью ответственности.
|
||
|
||
Schema Validation отвечала исключительно за корректность транспортного документа.
|
||
|
||
Parser извлекал необходимые поля транспортной модели.
|
||
|
||
Value Validation проверяла допустимость отдельных значений.
|
||
|
||
Mapper строил каноническую модель предметной области.
|
||
|
||
Trade Stream Consistency обеспечивал корректность последовательности сделок.
|
||
|
||
Таким образом к началу настоящего Build система уже умела гарантировать корректность потока при условии, что все сделки были успешно получены транспортным уровнем.
|
||
|
||
Однако отсутствовал механизм восстановления уже пропущенных сделок.
|
||
|
||
Например, если во время обработки непрерывного потока несколько сообщений были потеряны, существующая архитектура не содержала специализированного компонента, способного безопасно получить отсутствующие сделки посредством REST API и встроить их в уже существующую последовательность.
|
||
|
||
Попытка реализовать подобную функциональность непосредственно внутри WebSocket Feed или Runtime привела бы к нарушению принципа единственной ответственности.
|
||
|
||
Восстановление истории представляет собой самостоятельную архитектурную задачу.
|
||
|
||
Именно её решает Build 060.19.
|
||
|
||
---
|
||
|
||
# Результаты архитектурного аудита
|
||
|
||
Перед началом реализации Build был выполнен полный аудит существующей подсистемы **Market Data Acquisition**.
|
||
|
||
Целью аудита являлась проверка соответствия фактической реализации архитектурной модели, утверждённой в `build_060_19_architecture.md`, а также определение оптимальных точек интеграции новой подсистемы **Trade Recovery**.
|
||
|
||
Особое внимание уделялось уже существующей инфраструктуре получения исторических сделок через REST API.
|
||
|
||
Первоначально предполагалось, что для реализации Recovery потребуется собственный конвейер обработки транспортных данных.
|
||
|
||
Однако проведённый аудит показал, что подобное решение привело бы к дублированию уже существующей бизнес-логики Acquisition Layer.
|
||
|
||
В результате архитектура Recovery была существенно упрощена.
|
||
|
||
---
|
||
|
||
## Анализ существующего REST Pipeline
|
||
|
||
В ходе проверки был полностью проанализирован существующий REST-конвейер получения агрегированных сделок.
|
||
|
||
Аудит подтвердил наличие уже завершённой цепочки обработки.
|
||
|
||
```text
|
||
REST API
|
||
│
|
||
▼
|
||
DzengiTradesDocumentSource
|
||
│
|
||
▼
|
||
Schema Validation
|
||
│
|
||
▼
|
||
REST Trade Adapter
|
||
│
|
||
▼
|
||
Parser
|
||
│
|
||
▼
|
||
Value Validation
|
||
│
|
||
▼
|
||
Mapper
|
||
│
|
||
▼
|
||
Trade
|
||
```
|
||
|
||
Каждый уровень уже обладал собственной областью ответственности.
|
||
|
||
`DzengiTradesDocumentSource` отвечал исключительно за получение транспортного документа.
|
||
|
||
Schema Validation подтверждала соответствие документа транспортной схеме.
|
||
|
||
REST Adapter преобразовывал транспортную модель к внутреннему представлению.
|
||
|
||
Parser извлекал необходимые значения.
|
||
|
||
Value Validation выполняла проверку корректности отдельных полей.
|
||
|
||
Mapper строил канонический объект предметной области.
|
||
|
||
Получаемый объект `Trade` уже полностью соответствовал требованиям всей последующей архитектуры системы.
|
||
|
||
Таким образом Build 060.17 фактически завершил создание универсального REST-конвейера получения сделок.
|
||
|
||
---
|
||
|
||
## Отказ от дублирования REST Pipeline
|
||
|
||
После завершения аудита рассматривались два возможных варианта реализации Recovery.
|
||
|
||
Первый вариант предполагал создание собственного конвейера обработки REST-документов.
|
||
|
||
В подобной архитектуре Recovery самостоятельно выполнял бы:
|
||
|
||
- разбор транспортного документа;
|
||
- проверку схемы;
|
||
- извлечение полей;
|
||
- проверку значений;
|
||
- построение модели `Trade`.
|
||
|
||
Подобный подход был признан ошибочным.
|
||
|
||
Он приводил сразу к нескольким серьёзным недостаткам.
|
||
|
||
Во-первых, происходило полное дублирование уже существующего REST Pipeline.
|
||
|
||
Во-вторых, возникал риск расхождения поведения между обычным REST Feed и Recovery.
|
||
|
||
В-третьих, любое изменение транспортной схемы пришлось бы синхронно реализовывать сразу в двух различных подсистемах.
|
||
|
||
Подобное решение противоречило базовым архитектурным принципам Dzentra.
|
||
|
||
Поэтому было принято решение полностью отказаться от собственного конвейера обработки.
|
||
|
||
Recovery использует уже существующую инфраструктуру без каких-либо изменений её внутренней логики.
|
||
|
||
---
|
||
|
||
## Анализ подсистемы Trade Stream Consistency
|
||
|
||
Отдельной задачей архитектурного аудита стала проверка взаимодействия Recovery с новой подсистемой **Trade Stream Consistency**, реализованной в Build 060.18.
|
||
|
||
Первоначально рассматривалась возможность создания отдельного экземпляра контроллера согласованности исключительно для процесса восстановления истории.
|
||
|
||
Подобный вариант казался естественным, поскольку Recovery представляет собой самостоятельный сценарий получения данных.
|
||
|
||
Однако детальный анализ показал, что такое решение нарушает фундаментальные инварианты всей Acquisition Layer.
|
||
|
||
Главная задача Trade Stream Consistency заключается в формировании единственного канонического потока сделок.
|
||
|
||
Если Recovery использовал бы собственный экземпляр контроллера согласованности, в системе одновременно существовали бы два независимых состояния одного и того же торгового символа.
|
||
|
||
Это неизбежно привело бы к расхождению истории обработки и нарушению единственности канонического потока.
|
||
|
||
---
|
||
|
||
## Использование общего состояния потока
|
||
|
||
По результатам аудита было принято окончательное архитектурное решение.
|
||
|
||
Recovery не создаёт собственный экземпляр `TradeStreamConsistencyController`.
|
||
|
||
Вместо этого контроллер согласованности передаётся в Recovery извне.
|
||
|
||
Таким образом обе подсистемы используют единое состояние обработки сделок.
|
||
|
||
Архитектура взаимодействия принимает следующий вид.
|
||
|
||
```text
|
||
Trade Stream
|
||
│
|
||
▼
|
||
TradeStreamConsistencyController
|
||
▲ ▲
|
||
│ │
|
||
│ │
|
||
WebSocket Feed Trade Recovery
|
||
```
|
||
|
||
Подобное решение обеспечивает несколько важных преимуществ.
|
||
|
||
Во-первых, независимо от источника получения сделки используются абсолютно одинаковые правила проверки последовательности.
|
||
|
||
Во-вторых, отсутствует необходимость синхронизации нескольких внутренних состояний.
|
||
|
||
В-третьих, Recovery становится полностью независимым от внутренней реализации механизма согласованности.
|
||
|
||
Он работает исключительно через публичный контракт `TradeStreamConsistencyProtocol`.
|
||
|
||
Это полностью соответствует принципу **Dependency Inversion**, принятому в архитектуре Dzentra.
|
||
|
||
---
|
||
|
||
## Анализ порядка восстановления
|
||
|
||
Следующим результатом архитектурного аудита стало исследование порядка поступления исторических сделок через REST API.
|
||
|
||
Проверка показала, что транспортный уровень не должен рассматриваться как источник архитектурных гарантий.
|
||
|
||
Даже если конкретная реализация биржи в настоящий момент возвращает сделки в возрастающем порядке, подобное поведение не должно использоваться в качестве архитектурного инварианта системы.
|
||
|
||
Recovery обязан самостоятельно формировать каноническую последовательность перед передачей сделок в Trade Stream Consistency.
|
||
|
||
Поэтому было принято решение добавить специализированный уровень нормализации восстановленных данных.
|
||
|
||
---
|
||
|
||
## Recovery Normalizer
|
||
|
||
В рамках настоящего Build появляется отдельный компонент
|
||
|
||
```text
|
||
Trade Recovery Normalizer
|
||
```
|
||
|
||
Его задача предельно проста.
|
||
|
||
Компонент принимает последовательность уже построенных канонических объектов `Trade` и возвращает новую неизменяемую последовательность, отсортированную по `trade_id`.
|
||
|
||
Нормализатор сознательно не выполняет:
|
||
|
||
- дедупликацию;
|
||
- проверку порядка;
|
||
- проверку корректности данных;
|
||
- обнаружение конфликтующих повторов.
|
||
|
||
Все перечисленные задачи полностью принадлежат Trade Stream Consistency.
|
||
|
||
Подобное разделение обязанностей исключает дублирование бизнес-логики между двумя подсистемами.
|
||
|
||
---
|
||
|
||
# Архитектурное решение
|
||
|
||
По результатам проведённого аудита было принято решение реализовать подсистему **Trade Recovery** как самостоятельный orchestration-слой, использующий уже существующие компоненты Acquisition Layer.
|
||
|
||
Recovery не содержит собственного Parser.
|
||
|
||
Recovery не содержит собственного Mapper.
|
||
|
||
Recovery не содержит собственного механизма проверки согласованности.
|
||
|
||
Все перечисленные обязанности делегируются уже существующим специализированным компонентам системы.
|
||
|
||
После завершения Build архитектура принимает следующий вид.
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
│
|
||
▼
|
||
REST Document Source
|
||
│
|
||
▼
|
||
Schema Validation
|
||
│
|
||
▼
|
||
REST Trade Adapter
|
||
│
|
||
▼
|
||
Recovery Normalizer
|
||
│
|
||
▼
|
||
TradeStreamConsistencyController
|
||
│
|
||
▼
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Таким образом Build 060.19 не создаёт новую независимую цепочку обработки данных.
|
||
|
||
Он объединяет уже реализованные архитектурные уровни в специализированный сценарий восстановления истории сделок.
|
||
|
||
Подобное решение обеспечивает слабую связанность компонентов, повторное использование существующей инфраструктуры и полностью соответствует принципам модульной архитектуры Dzentra.
|
||
|
||
---
|
||
|
||
# Почему Recovery не интегрируется непосредственно в WebSocket
|
||
|
||
Во время архитектурного проектирования отдельно анализировался вопрос о месте расположения новой подсистемы Recovery.
|
||
|
||
На первый взгляд естественным выглядело решение встроить восстановление истории непосредственно в компонент WebSocket Trades Feed.
|
||
|
||
В подобной архитектуре именно Feed отвечал бы за обнаружение необходимости восстановления, выполнение REST-запросов и публикацию восстановленных сделок.
|
||
|
||
Однако детальный анализ показал, что подобный подход нарушает сразу несколько фундаментальных принципов архитектуры Dzentra.
|
||
|
||
Во-первых, WebSocket Feed отвечает исключительно за получение непрерывного потока транспортных сообщений.
|
||
|
||
Во-вторых, Recovery представляет собой самостоятельный сценарий получения исторических данных посредством REST API.
|
||
|
||
Несмотря на то что оба компонента работают со сделками, они решают различные задачи.
|
||
|
||
Попытка объединить их внутри одного класса привела бы к смешению различных уровней ответственности.
|
||
|
||
Поэтому было принято решение полностью разделить эти подсистемы.
|
||
|
||
WebSocket Feed остаётся источником непрерывного потока.
|
||
|
||
Trade Recovery становится отдельным сервисом восстановления истории.
|
||
|
||
Их совместная работа будет организована позднее посредством Runtime Orchestration.
|
||
|
||
---
|
||
|
||
# Новая подсистема Trade Recovery
|
||
|
||
Главным результатом настоящего Build становится появление в составе **Market Data Acquisition** нового архитектурного уровня — **Trade Recovery**.
|
||
|
||
До начала Build Acquisition Layer уже обеспечивал получение сделок и проверку их согласованности.
|
||
|
||
Однако отсутствовал специализированный механизм восстановления исторических данных.
|
||
|
||
После завершения Build между REST API и каноническим потоком появляется отдельный уровень восстановления.
|
||
|
||
Архитектура обработки принимает следующий вид.
|
||
|
||
```text
|
||
REST API
|
||
│
|
||
▼
|
||
Trade Recovery
|
||
│
|
||
▼
|
||
Trade Stream Consistency
|
||
│
|
||
▼
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
Появление данного уровня является принципиальным расширением возможностей Acquisition Layer.
|
||
|
||
Теперь система способна безопасно повторно получать ранее пропущенные сделки, не нарушая уже сформированные архитектурные инварианты.
|
||
|
||
---
|
||
|
||
# Архитектурное решение
|
||
|
||
Во время проектирования рассматривались несколько вариантов организации Recovery Pipeline.
|
||
|
||
Первый вариант предполагал реализацию всей логики восстановления внутри одного большого компонента.
|
||
|
||
Подобный компонент самостоятельно выполнял бы:
|
||
|
||
- получение данных;
|
||
- обработку транспортного документа;
|
||
- сортировку сделок;
|
||
- проверку согласованности;
|
||
- формирование результата.
|
||
|
||
После анализа архитектуры данный подход был отклонён.
|
||
|
||
Он нарушал принцип единственной ответственности и неизбежно приводил к дублированию уже существующих компонентов Acquisition Layer.
|
||
|
||
Поэтому было принято другое решение.
|
||
|
||
Trade Recovery становится исключительно координационным уровнем.
|
||
|
||
Он объединяет уже реализованные сервисы, не заменяя их.
|
||
|
||
Каждый существующий компонент продолжает выполнять только собственную специализированную задачу.
|
||
|
||
---
|
||
|
||
# Архитектура новой подсистемы
|
||
|
||
В рамках Build реализованы шесть новых компонентов.
|
||
|
||
```text
|
||
TradeRecoveryProtocol
|
||
|
||
TradeRecoveryController
|
||
|
||
TradeRecoveryRequest
|
||
|
||
TradeRecoveryResult
|
||
|
||
TradeRecoveryNormalizer
|
||
|
||
Trade Recovery Exceptions
|
||
```
|
||
|
||
Каждый компонент обладает собственной строго определённой областью ответственности.
|
||
|
||
Ни один из компонентов не дублирует обязанности другого.
|
||
|
||
Подобное разделение полностью соответствует принципу **Single Responsibility**, принятому в архитектуре Dzentra.
|
||
|
||
---
|
||
|
||
# TradeRecoveryProtocol
|
||
|
||
Одной из целей настоящего Build являлось формирование полноценного контрактного уровня новой подсистемы.
|
||
|
||
До начала Build соответствующий Protocol отсутствовал.
|
||
|
||
В рамках реализации был добавлен новый контракт.
|
||
|
||
```text
|
||
TradeRecoveryProtocol
|
||
```
|
||
|
||
Protocol определяет единственную публичную операцию.
|
||
|
||
```text
|
||
recover(request)
|
||
│
|
||
▼
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Контракт сознательно не определяет:
|
||
|
||
- источник получения данных;
|
||
- используемый REST API;
|
||
- механизм нормализации;
|
||
- алгоритмы проверки согласованности;
|
||
- внутреннее состояние Recovery.
|
||
|
||
Все перечисленные детали относятся исключительно к реализации Controller.
|
||
|
||
Благодаря подобному разделению любые последующие компоненты смогут зависеть только от публичного контракта, а не от конкретной реализации Recovery.
|
||
|
||
Это полностью соответствует принципу **Dependency Inversion**.
|
||
|
||
---
|
||
|
||
# Почему выбран минимальный Protocol
|
||
|
||
Во время проектирования анализировались различные варианты публичного интерфейса Recovery.
|
||
|
||
В частности рассматривались варианты:
|
||
|
||
- передачи отдельных параметров вместо объекта запроса;
|
||
- возврата списка сделок;
|
||
- возврата генератора;
|
||
- публикации событий вместо возврата результата.
|
||
|
||
После анализа было принято решение использовать специализированные модели запроса и результата.
|
||
|
||
Подобный контракт оказался наиболее устойчивым к дальнейшему развитию системы.
|
||
|
||
Он позволяет расширять Recovery без изменения публичного API.
|
||
|
||
Кроме того, наличие отдельных моделей запроса и результата значительно упрощает unit-тестирование и обеспечивает более высокую читаемость кода.
|
||
|
||
В результате публичный интерфейс Recovery остаётся минимальным, но при этом полностью готовым к дальнейшему развитию.
|
||
|
||
---
|
||
|
||
# TradeRecoveryRequest
|
||
|
||
Следующим результатом Build становится появление специализированной модели запроса восстановления.
|
||
|
||
До настоящего Build параметры восстановления передавались бы в виде набора независимых аргументов.
|
||
|
||
Подобный подход быстро привёл бы к росту сложности публичного интерфейса.
|
||
|
||
Поэтому было принято решение инкапсулировать все параметры восстановления в отдельную неизменяемую модель.
|
||
|
||
`TradeRecoveryRequest` содержит:
|
||
|
||
- торговый символ;
|
||
- начало временного интервала;
|
||
- конец временного интервала;
|
||
- необязательное ограничение количества сделок.
|
||
|
||
Корректность параметров проверяется непосредственно при создании объекта.
|
||
|
||
Таким образом Recovery никогда не начинает выполнение с некорректными входными данными.
|
||
|
||
---
|
||
|
||
# Принципы валидации запроса
|
||
|
||
При проектировании модели запроса отдельно анализировался вопрос распределения ответственности между Request и Controller.
|
||
|
||
Было принято решение максимально рано проверять все архитектурные инварианты.
|
||
|
||
Поэтому `TradeRecoveryRequest` самостоятельно валидирует:
|
||
|
||
- непустой символ;
|
||
- типы входных значений;
|
||
- корректность временного диапазона;
|
||
- отсутствие отрицательных временных меток;
|
||
- максимально допустимый размер окна восстановления;
|
||
- допустимый диапазон значения `limit`.
|
||
|
||
Благодаря этому Controller получает гарантированно корректный объект запроса и может сосредоточиться исключительно на координации процесса восстановления.
|
||
|
||
---
|
||
|
||
# TradeRecoveryResult
|
||
|
||
Следующим новым компонентом подсистемы становится специализированная модель результата восстановления.
|
||
|
||
Во время проектирования рассматривались различные варианты возвращаемого значения метода `recover()`.
|
||
|
||
В частности анализировались следующие подходы:
|
||
|
||
- возврат обычного списка сделок;
|
||
- возврат кортежа сделок;
|
||
- возврат генератора;
|
||
- возврат только количества восстановленных сделок.
|
||
|
||
После анализа было принято решение использовать отдельную доменную модель результата.
|
||
|
||
Такое решение оказалось наиболее гибким.
|
||
|
||
Оно позволяет в дальнейшем расширять результат Recovery без изменения публичного контракта Protocol.
|
||
|
||
В рамках настоящего Build `TradeRecoveryResult` содержит:
|
||
|
||
- торговый символ;
|
||
- запрошенную нижнюю границу временного диапазона;
|
||
- запрошенную верхнюю границу временного диапазона;
|
||
- неизменяемую последовательность восстановленных сделок.
|
||
|
||
Дополнительно модель предоставляет вычисляемые свойства.
|
||
|
||
Например:
|
||
|
||
- количество восстановленных сделок;
|
||
- признак пустого результата;
|
||
- первую восстановленную сделку;
|
||
- последнюю восстановленную сделку.
|
||
|
||
Подобные свойства позволяют вызывающему коду получать наиболее часто используемую информацию без необходимости повторной обработки коллекции.
|
||
|
||
---
|
||
|
||
# Почему Recovery использует immutable-модели
|
||
|
||
Во время проектирования отдельно рассматривался вопрос изменяемости моделей Recovery.
|
||
|
||
На первый взгляд использование обычных изменяемых структур данных могло показаться более простым.
|
||
|
||
Однако подобный подход создавал бы несколько потенциальных проблем.
|
||
|
||
Во-первых, восстановленные сделки могли бы случайно изменяться после завершения Recovery Pipeline.
|
||
|
||
Во-вторых, различные компоненты системы могли бы совместно использовать одну и ту же коллекцию.
|
||
|
||
В-третьих, unit-тестирование существенно усложнялось бы из-за появления побочных эффектов.
|
||
|
||
Поэтому было принято решение использовать неизменяемые модели.
|
||
|
||
И `TradeRecoveryRequest`, и `TradeRecoveryResult` являются immutable-объектами.
|
||
|
||
Последовательность восстановленных сделок также хранится в виде `tuple`.
|
||
|
||
Это полностью соответствует архитектурным принципам Dzentra, согласно которым канонические данные не должны изменяться после публикации.
|
||
|
||
---
|
||
|
||
# TradeRecoveryNormalizer
|
||
|
||
Следующим результатом Build становится появление специализированного компонента
|
||
|
||
```text
|
||
TradeRecoveryNormalizer
|
||
```
|
||
|
||
Его назначение принципиально отличается от задач Trade Stream Consistency.
|
||
|
||
Normalizer не занимается проверкой корректности данных.
|
||
|
||
Он не анализирует содержимое сделок.
|
||
|
||
Он не принимает решений относительно повторов.
|
||
|
||
Он не выполняет дедупликацию.
|
||
|
||
Единственной обязанностью компонента является формирование детерминированной последовательности сделок перед передачей их в механизм проверки согласованности.
|
||
|
||
---
|
||
|
||
## Ответственность Normalizer
|
||
|
||
Во время проектирования отдельно анализировалось распределение обязанностей между Recovery и Trade Stream Consistency.
|
||
|
||
Было принято решение максимально разделить эти два уровня.
|
||
|
||
Normalizer отвечает исключительно за сортировку последовательности по `trade_id`.
|
||
|
||
После завершения нормализации все сделки располагаются в возрастающем порядке.
|
||
|
||
Именно такая последовательность передаётся далее в `TradeStreamConsistencyController`.
|
||
|
||
Любые дополнительные проверки сознательно отсутствуют.
|
||
|
||
Подобное решение позволяет избежать дублирования бизнес-логики между двумя независимыми подсистемами.
|
||
|
||
---
|
||
|
||
## Почему Normalizer не выполняет дедупликацию
|
||
|
||
Во время проектирования отдельно рассматривалась возможность удаления повторов непосредственно на этапе нормализации.
|
||
|
||
На первый взгляд подобное решение могло показаться логичным.
|
||
|
||
Однако оно нарушало бы один из фундаментальных принципов архитектуры Acquisition Layer.
|
||
|
||
Единственным компонентом системы, имеющим право принимать решения относительно повторной доставки сделок, является Trade Stream Consistency.
|
||
|
||
Если бы Normalizer самостоятельно удалял повторы, часть бизнес-логики проверки согласованности оказалась бы распределена между двумя различными подсистемами.
|
||
|
||
Это неизбежно привело бы к расхождению поведения Recovery и WebSocket Pipeline.
|
||
|
||
Поэтому было принято окончательное решение.
|
||
|
||
Normalizer выполняет исключительно сортировку.
|
||
|
||
Все остальные решения принимает только `TradeStreamConsistencyController`.
|
||
|
||
---
|
||
|
||
# TradeRecoveryController
|
||
|
||
Центральным компонентом настоящего Build становится
|
||
|
||
```text
|
||
TradeRecoveryController
|
||
```
|
||
|
||
Именно он завершает построение новой подсистемы Trade Recovery.
|
||
|
||
Следует подчеркнуть, что Controller не является компонентом обработки данных.
|
||
|
||
Он представляет собой исключительно orchestration-службу.
|
||
|
||
Controller не анализирует транспортные документы.
|
||
|
||
Не выполняет проверку схемы.
|
||
|
||
Не преобразует транспортные модели.
|
||
|
||
Не реализует проверку согласованности.
|
||
|
||
Все перечисленные задачи уже принадлежат специализированным компонентам Acquisition Layer.
|
||
|
||
Controller лишь организует их совместную работу.
|
||
|
||
---
|
||
|
||
## Архитектура Controller
|
||
|
||
Конструкция Controller намеренно сделана максимально компактной.
|
||
|
||
Он использует всего две основные зависимости.
|
||
|
||
```text
|
||
DzengiTradesDocumentSource
|
||
|
||
TradeStreamConsistencyProtocol
|
||
```
|
||
|
||
Первая отвечает за получение транспортных документов.
|
||
|
||
Вторая обеспечивает формирование единственного канонического потока сделок.
|
||
|
||
Обе зависимости передаются извне.
|
||
|
||
Таким образом Recovery не создаёт собственные экземпляры сервисов и полностью соответствует принципу внедрения зависимостей.
|
||
|
||
---
|
||
|
||
## Ответственность Controller
|
||
|
||
Во время проектирования особое внимание уделялось разделению ответственности между Recovery и остальными компонентами Acquisition Layer.
|
||
|
||
В результате Controller получил исключительно координационные обязанности.
|
||
|
||
Он отвечает за:
|
||
|
||
- выполнение запроса восстановления;
|
||
- получение транспортного документа;
|
||
- запуск существующего REST-конвейера;
|
||
- вызов Recovery Normalizer;
|
||
- последовательную передачу сделок в `TradeStreamConsistencyController`;
|
||
- формирование итогового `TradeRecoveryResult`.
|
||
|
||
При этом Controller сознательно не реализует:
|
||
|
||
- REST Parser;
|
||
- REST Mapper;
|
||
- проверку схемы;
|
||
- проверку значений;
|
||
- дедупликацию;
|
||
- проверку порядка;
|
||
- хранение внутреннего состояния.
|
||
|
||
Подобное разделение существенно упрощает сопровождение Recovery и делает его независимым от внутренней реализации остальных подсистем.
|
||
|
||
---
|
||
|
||
# Stateless-архитектура Recovery
|
||
|
||
Одним из важнейших архитектурных решений настоящего Build становится полный отказ от собственного состояния внутри Recovery.
|
||
|
||
После завершения выполнения метода `recover()` Controller не сохраняет никакой информации о предыдущем вызове.
|
||
|
||
Не сохраняются:
|
||
|
||
- последний диапазон восстановления;
|
||
- последняя обработанная сделка;
|
||
- внутренние кэши;
|
||
- история восстановления.
|
||
|
||
Все необходимые сведения уже содержатся внутри Trade Stream Consistency.
|
||
|
||
Именно эта подсистема является единственным владельцем состояния потока.
|
||
|
||
Recovery остаётся полностью stateless-сервисом.
|
||
|
||
Подобное решение существенно упрощает последующую интеграцию с Runtime и позволяет безопасно использовать один экземпляр Recovery в течение всего жизненного цикла приложения.
|
||
|
||
---
|
||
|
||
# Алгоритм восстановления сделок
|
||
|
||
После завершения реализации Build поведение подсистемы Recovery становится полностью детерминированным.
|
||
|
||
Каждый запрос восстановления проходит одну и ту же последовательность этапов.
|
||
|
||
Ни один шаг не зависит от конкретной реализации биржи или транспортного уровня.
|
||
|
||
Полный алгоритм обработки выглядит следующим образом.
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
│
|
||
▼
|
||
Получение REST-документа
|
||
│
|
||
▼
|
||
Schema Validation
|
||
│
|
||
▼
|
||
REST Trade Adapter
|
||
│
|
||
▼
|
||
TradeRecoveryNormalizer
|
||
│
|
||
▼
|
||
TradeStreamConsistencyController
|
||
│
|
||
▼
|
||
Формирование TradeRecoveryResult
|
||
```
|
||
|
||
Каждый этап выполняет строго одну задачу.
|
||
|
||
Ни один уровень не повторяет обязанности другого.
|
||
|
||
Подобная последовательность полностью соответствует принципу конвейерной обработки данных, принятому в архитектуре Dzentra.
|
||
|
||
---
|
||
|
||
# Использование существующего REST Pipeline
|
||
|
||
Одним из главных архитектурных результатов настоящего Build стало повторное использование уже реализованного REST-конвейера.
|
||
|
||
Recovery не содержит собственной реализации получения сделок.
|
||
|
||
После получения запроса Controller передаёт управление существующей инфраструктуре.
|
||
|
||
Используется следующая последовательность.
|
||
|
||
```text
|
||
DzengiTradesDocumentSource
|
||
│
|
||
▼
|
||
validate_rest_agg_trades_schema()
|
||
│
|
||
▼
|
||
adapt_rest_agg_trades_document()
|
||
│
|
||
▼
|
||
Parser
|
||
│
|
||
▼
|
||
Value Validation
|
||
│
|
||
▼
|
||
Mapper
|
||
│
|
||
▼
|
||
Trade
|
||
```
|
||
|
||
Таким образом Build 060.19 не создаёт нового способа построения канонической модели.
|
||
|
||
Любая сделка независимо от сценария её получения проходит абсолютно одинаковую обработку.
|
||
|
||
Это гарантирует идентичное поведение всей Acquisition Layer.
|
||
|
||
---
|
||
|
||
# Нормализация восстановленных сделок
|
||
|
||
После завершения REST Pipeline Recovery получает последовательность уже построенных канонических объектов `Trade`.
|
||
|
||
Следующим этапом становится нормализация этой последовательности.
|
||
|
||
Единственной задачей Normalizer является сортировка сделок по `trade_id`.
|
||
|
||
После завершения нормализации Recovery получает детерминированный порядок обработки.
|
||
|
||
```text
|
||
Trade #501
|
||
|
||
Trade #503
|
||
|
||
Trade #502
|
||
```
|
||
|
||
преобразуется в
|
||
|
||
```text
|
||
Trade #501
|
||
|
||
Trade #502
|
||
|
||
Trade #503
|
||
```
|
||
|
||
Никакие другие изменения последовательности не выполняются.
|
||
|
||
Normalizer не удаляет элементы.
|
||
|
||
Не объединяет записи.
|
||
|
||
Не изменяет содержимое сделок.
|
||
|
||
Благодаря этому Recovery остаётся полностью независимым от правил проверки согласованности.
|
||
|
||
---
|
||
|
||
# Передача сделок в Trade Stream Consistency
|
||
|
||
После завершения нормализации каждая сделка последовательно передаётся в существующий экземпляр
|
||
|
||
```text
|
||
TradeStreamConsistencyController
|
||
```
|
||
|
||
Для каждой сделки выполняется стандартная операция.
|
||
|
||
```text
|
||
accept(trade)
|
||
│
|
||
▼
|
||
Trade | None
|
||
```
|
||
|
||
Recovery сознательно не интерпретирует внутреннюю логику Controller.
|
||
|
||
Если сделка публикуется впервые, она включается в итоговый результат восстановления.
|
||
|
||
Если обнаружен идентичный повтор, Controller возвращает `None`.
|
||
|
||
Если обнаруживается нарушение архитектурных инвариантов, генерируется соответствующее специализированное исключение.
|
||
|
||
Recovery не подавляет подобные ошибки.
|
||
|
||
Они передаются вызывающему компоненту без изменения.
|
||
|
||
Такое решение гарантирует единообразное поведение независимо от источника получения сделок.
|
||
|
||
---
|
||
|
||
# Формирование результата восстановления
|
||
|
||
После завершения обработки всей последовательности Controller формирует итоговый объект
|
||
|
||
```text
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
В результирующую коллекцию попадают только сделки, успешно принятые механизмом Trade Stream Consistency.
|
||
|
||
Идентичные дубликаты автоматически исключаются из результата.
|
||
|
||
Конфликтующие повторы не публикуются вовсе.
|
||
|
||
Если восстановленный диапазон не содержит новых сделок, Recovery возвращает корректный пустой результат.
|
||
|
||
Подобное поведение рассматривается как полностью штатная ситуация.
|
||
|
||
Отсутствие новых сделок не является ошибкой восстановления.
|
||
|
||
---
|
||
|
||
# Новые доменные исключения
|
||
|
||
Следующим результатом Build становится появление специализированной иерархии исключений подсистемы Trade Recovery.
|
||
|
||
До настоящего Build подобные ошибки отсутствовали.
|
||
|
||
В рамках реализации добавлены следующие классы.
|
||
|
||
```text
|
||
TradeRecoveryError
|
||
|
||
TradeRecoveryWindowError
|
||
|
||
TradeRecoveryLimitError
|
||
|
||
TradeRecoveryNormalizationError
|
||
|
||
TradeRecoveryControllerError
|
||
```
|
||
|
||
Каждый класс отражает отдельный тип архитектурной ошибки Recovery Pipeline.
|
||
|
||
Разделение исключений позволяет вызывающему компоненту принимать различные решения в зависимости от характера возникшей проблемы.
|
||
|
||
Например, неверный диапазон восстановления и ошибка внутренней координации имеют различную природу и требуют различной реакции.
|
||
|
||
Поэтому использование одного общего исключения было признано нецелесообразным.
|
||
|
||
Все новые классы наследуются от общей иерархии исключений подсистемы Acquisition Layer и полностью соответствуют принятой архитектуре проекта.
|
||
|
||
---
|
||
|
||
# Атомарность операций
|
||
|
||
Одним из фундаментальных требований настоящего Build являлось обеспечение атомарности процесса восстановления.
|
||
|
||
Каждый вызов метода `recover()` представляет собой законченную независимую операцию.
|
||
|
||
Если в процессе восстановления возникает исключение, Recovery немедленно прекращает выполнение.
|
||
|
||
При этом Controller Recovery не содержит собственного изменяемого состояния.
|
||
|
||
Следовательно, после возникновения ошибки внутри Recovery отсутствует необходимость выполнять откат внутренних структур данных.
|
||
|
||
Единственным компонентом, изменяющим состояние потока, остаётся Trade Stream Consistency.
|
||
|
||
Он уже обеспечивает собственную атомарность, реализованную в Build 060.18.
|
||
|
||
Таким образом Build 060.19 полностью наследует гарантии согласованности предыдущего этапа, не усложняя архитектуру дополнительными механизмами отката.
|
||
|
||
---
|
||
|
||
# Производительность
|
||
|
||
Во время проектирования новой подсистемы одним из обязательных требований являлось сохранение линейной сложности процесса восстановления.
|
||
|
||
Recovery не выполняет ресурсоёмких операций над уже полученной последовательностью сделок.
|
||
|
||
Основные этапы обработки имеют следующую вычислительную сложность.
|
||
|
||
| Операция | Средняя сложность |
|
||
|----------|-------------------|
|
||
| Получение REST-документа | зависит от транспортного уровня |
|
||
| Schema Validation | O(n) |
|
||
| Построение канонических Trade | O(n) |
|
||
| Сортировка последовательности | O(n log n) |
|
||
| Передача в Trade Stream Consistency | O(n) |
|
||
| Формирование результата | O(n) |
|
||
|
||
Единственной операцией, сложность которой превышает линейную, является сортировка восстановленных сделок.
|
||
|
||
Однако она выполняется один раз для каждого запроса восстановления и обеспечивает детерминированное поведение всей последующей обработки.
|
||
|
||
Подобный компромисс был признан полностью оправданным.
|
||
|
||
---
|
||
|
||
# Изменённые файлы
|
||
|
||
В рамках Build были добавлены шесть новых компонентов подсистемы **Trade Recovery**.
|
||
|
||
Все изменения были сознательно локализованы внутри нового каталога
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/
|
||
```
|
||
|
||
Подобное решение позволило полностью изолировать новую функциональность от уже существующих компонентов Acquisition Layer.
|
||
|
||
Ни один ранее реализованный Parser, Mapper, Adapter, Validator или Controller не потребовал изменения собственной бизнес-логики.
|
||
|
||
Новая подсистема была встроена посредством повторного использования уже существующей архитектуры.
|
||
|
||
---
|
||
|
||
## Trade Recovery Protocol
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/trade_recovery_protocol.py
|
||
```
|
||
|
||
Добавлен новый Protocol.
|
||
|
||
```text
|
||
TradeRecoveryProtocol
|
||
```
|
||
|
||
Protocol определяет единственный публичный контракт новой подсистемы.
|
||
|
||
```python
|
||
recover(request: TradeRecoveryRequest) -> TradeRecoveryResult
|
||
```
|
||
|
||
Благодаря этому все последующие компоненты Runtime смогут зависеть исключительно от абстракции Recovery, а не от конкретной реализации Controller.
|
||
|
||
---
|
||
|
||
## Trade Recovery Request
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/trade_recovery_request.py
|
||
```
|
||
|
||
Добавлена специализированная immutable-модель запроса восстановления.
|
||
|
||
Модель инкапсулирует все параметры Recovery.
|
||
|
||
Выполняется встроенная проверка:
|
||
|
||
- торгового символа;
|
||
- диапазона времени;
|
||
- максимального размера окна;
|
||
- ограничения `limit`;
|
||
- корректности типов входных данных.
|
||
|
||
Таким образом Controller никогда не начинает выполнение с некорректным запросом.
|
||
|
||
---
|
||
|
||
## Trade Recovery Result
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/trade_recovery_result.py
|
||
```
|
||
|
||
Добавлена immutable-модель результата восстановления.
|
||
|
||
Result содержит:
|
||
|
||
- symbol;
|
||
- requested_start_time;
|
||
- requested_end_time;
|
||
- tuple восстановленных сделок.
|
||
|
||
Также реализованы вычисляемые свойства:
|
||
|
||
```text
|
||
recovered_count
|
||
|
||
is_empty
|
||
|
||
first_trade
|
||
|
||
last_trade
|
||
```
|
||
|
||
Подобный подход позволяет избежать повторной обработки коллекции вызывающим кодом.
|
||
|
||
---
|
||
|
||
## Trade Recovery Exceptions
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/trade_recovery_exceptions.py
|
||
```
|
||
|
||
Добавлена специализированная иерархия доменных исключений Recovery.
|
||
|
||
Реализованы классы:
|
||
|
||
```text
|
||
TradeRecoveryError
|
||
|
||
TradeRecoveryWindowError
|
||
|
||
TradeRecoveryLimitError
|
||
|
||
TradeRecoveryNormalizationError
|
||
|
||
TradeRecoveryControllerError
|
||
```
|
||
|
||
Разделение ошибок позволяет более точно диагностировать причины возникновения исключительных ситуаций без использования универсального класса ошибок.
|
||
|
||
---
|
||
|
||
## Trade Recovery Normalizer
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/trade_recovery_normalizer.py
|
||
```
|
||
|
||
Добавлен специализированный компонент нормализации.
|
||
|
||
Normalizer отвечает исключительно за сортировку восстановленной последовательности по `trade_id`.
|
||
|
||
Компонент сознательно не выполняет:
|
||
|
||
- дедупликацию;
|
||
- проверку порядка;
|
||
- изменение объектов `Trade`;
|
||
- фильтрацию сделок.
|
||
|
||
Подобное разделение обязанностей полностью соответствует архитектуре Acquisition Layer.
|
||
|
||
---
|
||
|
||
## Trade Recovery Controller
|
||
|
||
```text
|
||
src/market_data/acquisition/recovery/trade_recovery_controller.py
|
||
```
|
||
|
||
Реализован центральный компонент новой подсистемы.
|
||
|
||
Controller отвечает исключительно за координацию процесса восстановления.
|
||
|
||
В частности он выполняет:
|
||
|
||
- получение транспортного документа;
|
||
- запуск существующего REST Pipeline;
|
||
- нормализацию восстановленных сделок;
|
||
- последовательную передачу сделок в `TradeStreamConsistencyController`;
|
||
- формирование объекта `TradeRecoveryResult`.
|
||
|
||
Внутри Controller отсутствует собственная бизнес-логика обработки транспортных данных.
|
||
|
||
Вся обработка делегируется уже существующим специализированным компонентам.
|
||
|
||
---
|
||
|
||
# Добавленные unit-тесты
|
||
|
||
Настоящий Build сопровождается полноценным покрытием новой подсистемы unit-тестами.
|
||
|
||
Все тесты написаны исключительно через публичные интерфейсы компонентов.
|
||
|
||
Ни один тест не зависит от внутренней реализации Recovery.
|
||
|
||
Подобный подход позволяет свободно изменять внутреннее устройство подсистемы без изменения существующего набора тестов.
|
||
|
||
---
|
||
|
||
## TradeRecoveryRequest
|
||
|
||
Добавлен новый файл.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/recovery/test_trade_recovery_request.py
|
||
```
|
||
|
||
Проверяются следующие сценарии.
|
||
|
||
---
|
||
|
||
### Создание корректного запроса
|
||
|
||
Подтверждается успешное создание объекта с допустимыми параметрами.
|
||
|
||
---
|
||
|
||
### Проверка symbol
|
||
|
||
Подтверждается невозможность создания запроса с пустым символом.
|
||
|
||
---
|
||
|
||
### Проверка диапазона времени
|
||
|
||
Проверяется невозможность задания диапазона, в котором начало превышает окончание.
|
||
|
||
---
|
||
|
||
### Проверка отрицательных временных меток
|
||
|
||
Подтверждается генерация исключения при использовании отрицательных значений.
|
||
|
||
---
|
||
|
||
### Проверка максимального окна восстановления
|
||
|
||
Подтверждается невозможность создания запроса с временным диапазоном, превышающим допустимый предел.
|
||
|
||
---
|
||
|
||
### Проверка limit
|
||
|
||
Проверяются допустимые и недопустимые значения ограничения количества сделок.
|
||
|
||
---
|
||
|
||
## TradeRecoveryResult
|
||
|
||
Добавлен новый файл.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/recovery/test_trade_recovery_result.py
|
||
```
|
||
|
||
Проверяются:
|
||
|
||
- корректное формирование результата;
|
||
- вычисляемое количество сделок;
|
||
- пустой результат;
|
||
- первая сделка;
|
||
- последняя сделка;
|
||
- неизменяемость модели.
|
||
|
||
---
|
||
|
||
## TradeRecoveryNormalizer
|
||
|
||
Добавлен новый файл.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/recovery/test_trade_recovery_normalizer.py
|
||
```
|
||
|
||
Проверяются следующие сценарии.
|
||
|
||
---
|
||
|
||
### Сортировка последовательности
|
||
|
||
Подтверждается корректная сортировка сделок по `trade_id`.
|
||
|
||
---
|
||
|
||
### Работа с пустой коллекцией
|
||
|
||
Подтверждается корректное поведение при отсутствии сделок.
|
||
|
||
---
|
||
|
||
### Работа с уже отсортированной последовательностью
|
||
|
||
Подтверждается отсутствие побочных эффектов.
|
||
|
||
---
|
||
|
||
### Возврат immutable-последовательности
|
||
|
||
Подтверждается, что результат всегда представлен в виде `tuple`.
|
||
|
||
---
|
||
|
||
## TradeRecoveryController
|
||
|
||
Добавлен новый файл.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/recovery/test_trade_recovery_controller.py
|
||
```
|
||
|
||
Проверяются следующие сценарии.
|
||
|
||
---
|
||
|
||
### Использование существующего TradeStreamConsistencyController
|
||
|
||
Подтверждается, что Controller использует переданный экземпляр механизма согласованности, не создавая собственный.
|
||
|
||
---
|
||
|
||
### Последовательная обработка восстановленных сделок
|
||
|
||
Подтверждается передача всех сделок в порядке возрастания `trade_id`.
|
||
|
||
---
|
||
|
||
### Исключение идентичных повторов
|
||
|
||
Подтверждается корректная обработка ситуации, когда `accept()` возвращает `None`.
|
||
|
||
---
|
||
|
||
### Формирование TradeRecoveryResult
|
||
|
||
Проверяется корректное формирование итогового результата восстановления.
|
||
|
||
---
|
||
|
||
### Работа с пустым диапазоном
|
||
|
||
Подтверждается корректное формирование пустого результата.
|
||
|
||
---
|
||
|
||
### Передача исключений
|
||
|
||
Проверяется, что Recovery не подавляет исключения, возникающие внутри Trade Stream Consistency.
|
||
|
||
---
|
||
|
||
# Результаты тестирования
|
||
|
||
После завершения реализации был выполнен запуск полного набора unit-тестов новой подсистемы Recovery.
|
||
|
||
Сначала была выполнена проверка моделей и вспомогательных компонентов.
|
||
|
||
Использовались команды.
|
||
|
||
```bash
|
||
python -m pytest -q \
|
||
tests/unit/market_data/acquisition/recovery/
|
||
```
|
||
|
||
Результат.
|
||
|
||
```text
|
||
70 passed
|
||
```
|
||
|
||
Все предусмотренные сценарии успешно пройдены.
|
||
|
||
Тестирование подтвердило:
|
||
|
||
- корректность валидации `TradeRecoveryRequest`;
|
||
- корректность формирования `TradeRecoveryResult`;
|
||
- детерминированную работу Recovery Normalizer;
|
||
- корректную координацию Recovery Controller;
|
||
- корректную интеграцию с Trade Stream Consistency.
|
||
|
||
После этого был выполнен запуск полного набора тестов подсистемы **Market Data Acquisition**.
|
||
|
||
Использовалась команда.
|
||
|
||
```bash
|
||
python -m pytest -q tests/unit/market_data/acquisition
|
||
```
|
||
|
||
Результат.
|
||
|
||
```text
|
||
1039 passed
|
||
```
|
||
|
||
Ни одного регрессионного отклонения обнаружено не было.
|
||
|
||
Все ранее реализованные компоненты Acquisition Layer продолжают работать без изменений.
|
||
|
||
---
|
||
|
||
## Полная регрессионная проверка проекта
|
||
|
||
Заключительным этапом Build стал запуск полного набора unit-тестов всего проекта.
|
||
|
||
Использовалась команда.
|
||
|
||
```bash
|
||
python -m pytest -q
|
||
```
|
||
|
||
Получен следующий результат.
|
||
|
||
```text
|
||
1360 passed
|
||
```
|
||
|
||
Полная регрессионная проверка подтвердила:
|
||
|
||
- отсутствие нарушения обратной совместимости;
|
||
- отсутствие конфликтов между новой подсистемой Recovery и существующими компонентами;
|
||
- корректную интеграцию Recovery в архитектуру Acquisition Layer;
|
||
- сохранение стабильности всего проекта после завершения Build.
|
||
|
||
Настоящий Build считается полностью завершённым.
|
||
|
||
---
|
||
|
||
# Итоги Build
|
||
|
||
Build 060.19 полностью завершает построение самостоятельной подсистемы **Trade Recovery** внутри Market Data Acquisition.
|
||
|
||
До начала настоящего Build система обеспечивала получение сделок и контроль их согласованности.
|
||
|
||
После завершения Build система дополнительно получила возможность безопасного восстановления исторических сделок через REST API.
|
||
|
||
Таким образом ответственность Acquisition Layer была расширена.
|
||
|
||
Теперь подсистема обеспечивает:
|
||
|
||
- получение исторических сделок через REST API;
|
||
- повторное использование существующего REST Pipeline;
|
||
- детерминированную нормализацию восстановленных данных;
|
||
- безопасную интеграцию восстановленных сделок в существующий поток;
|
||
- повторное использование Trade Stream Consistency;
|
||
- формирование специализированного результата восстановления.
|
||
|
||
При этом все существующие компоненты системы продолжают работать без каких-либо изменений.
|
||
|
||
Build не нарушил обратную совместимость и не потребовал модификации Parser, Mapper, Adapter, Validator или Trade Stream Consistency.
|
||
|
||
---
|
||
|
||
# Архитектурный результат
|
||
|
||
Главным архитектурным результатом настоящего Build становится появление нового самостоятельного уровня Acquisition Layer.
|
||
|
||
Теперь общая архитектура обработки исторических сделок принимает следующий вид.
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
│
|
||
▼
|
||
REST Document Source
|
||
│
|
||
▼
|
||
Schema Validation
|
||
│
|
||
▼
|
||
REST Trade Adapter
|
||
│
|
||
▼
|
||
Trade Recovery Normalizer
|
||
│
|
||
▼
|
||
Trade Stream Consistency
|
||
│
|
||
▼
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
Новая подсистема полностью изолирована от транспортной реализации.
|
||
|
||
Recovery работает исключительно с канонической моделью `Trade` и публичным контрактом Trade Stream Consistency.
|
||
|
||
Это означает, что независимо от конкретной реализации REST API механизм восстановления использует единые архитектурные правила обработки сделок.
|
||
|
||
Подобное решение исключает дублирование бизнес-логики и гарантирует единообразное поведение всей системы.
|
||
|
||
---
|
||
|
||
# Что сознательно НЕ реализовано
|
||
|
||
В соответствии с утверждёнными границами Build 060.19 ряд задач был сознательно оставлен за пределами реализации.
|
||
|
||
Настоящий Build **не включает**:
|
||
|
||
- автоматическое обнаружение пропусков последовательности (`Gap Detection`);
|
||
- автоматический запуск Recovery;
|
||
- интеграцию Recovery в Runtime;
|
||
- централизованный Trade Recovery Registry;
|
||
- планирование и координацию восстановления;
|
||
- повторную синхронизацию WebSocket после Recovery;
|
||
- механизм управления жизненным циклом Recovery;
|
||
- публикацию событий после завершения восстановления.
|
||
|
||
Все перечисленные возможности относятся к последующим этапам развития подсистемы Trades Feed и будут использовать уже реализованный слой Trade Recovery в качестве готового архитектурного фундамента.
|
||
|
||
---
|
||
|
||
# Влияние на последующие Build
|
||
|
||
Реализация настоящего Build существенно упрощает дальнейшее развитие подсистемы Trades Feed.
|
||
|
||
Следующие этапы смогут использовать уже готовый механизм восстановления истории и сосредоточиться исключительно на его интеграции в Runtime.
|
||
|
||
В частности Recovery станет обязательной частью общего конвейера обработки сделок.
|
||
|
||
```text
|
||
WebSocket Feed
|
||
│
|
||
▼
|
||
Gap Detection
|
||
│
|
||
▼
|
||
Trade Recovery
|
||
│
|
||
▼
|
||
Trade Stream Consistency
|
||
│
|
||
▼
|
||
Canonical Trade Stream
|
||
```
|
||
|
||
При этом сама подсистема Recovery останется неизменной.
|
||
|
||
Последующие Build будут заниматься исключительно организацией взаимодействия уже реализованных компонентов.
|
||
|
||
Это полностью соответствует принципу построения системы из независимых архитектурных модулей.
|
||
|
||
---
|
||
|
||
# Заключение
|
||
|
||
Build 060.19 успешно достиг всех поставленных целей.
|
||
|
||
В рамках реализации:
|
||
|
||
- сформирована самостоятельная подсистема **Trade Recovery**;
|
||
- реализован контракт `TradeRecoveryProtocol`;
|
||
- реализован координирующий `TradeRecoveryController`;
|
||
- реализованы immutable-модели `TradeRecoveryRequest` и `TradeRecoveryResult`;
|
||
- реализован специализированный `TradeRecoveryNormalizer`;
|
||
- введена отдельная иерархия доменных исключений Recovery;
|
||
- выполнено полное покрытие новой подсистемы unit-тестами;
|
||
- подтверждено соответствие реализации утверждённой архитектурной спецификации;
|
||
- выполнена успешная регрессионная проверка всего проекта (`1360 passed`).
|
||
|
||
Полученные результаты формируют завершённый слой восстановления исторических сделок и создают прочную основу для следующего этапа развития Acquisition Layer — централизованного управления экземплярами Recovery через единый **Trade Recovery Registry**.
|
||
|
||
---
|
||
|
||
# Следующий Build
|
||
|
||
> **Ретроспективное уточнение 060.30.5 (2026-08-03).**
|
||
>
|
||
> Прогноз ниже был уточнён после архитектурного аудита. Фактический
|
||
> [Build 060.20](build_060_20.md) сформировал Trade Runtime Architecture,
|
||
> а владение состоянием было скорректировано в
|
||
> [Build 060.20.1](build_060_20_1.md). Этапы Runtime Protocol, Service и
|
||
> Acquisition Integration выполнены в
|
||
> [060.21](build_060_21.md)–[060.23](build_060_23.md), затем
|
||
> [060.24](build_060_24.md) завершил Runtime Recovery,
|
||
> [060.25](build_060_25.md) — Production Runtime,
|
||
> [060.26](build_060_26.md) — Integration & Regression,
|
||
> [060.27](build_060_27.md)–[060.29](build_060_29.md) —
|
||
> Storage/Checkpoint/Access, а [060.30](build_060_30_architecture.md) —
|
||
> Final Documentation. После 060.30 утверждён только 061.00; номера
|
||
> следующих Feed-веток ещё не назначены. Актуальная последовательность:
|
||
> [Master Roadmap](../roadmap/master-roadmap.md).
|
||
|
||
**Build 060.20 — Trade Recovery Registry**
|