75 KiB
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 уже обеспечивала полный цикл построения канонической модели сделки и формирования согласованного потока.
Конвейер обработки выглядел следующим образом.
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-конвейер получения агрегированных сделок.
Аудит подтвердил наличие уже завершённой цепочки обработки.
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 извне.
Таким образом обе подсистемы используют единое состояние обработки сделок.
Архитектура взаимодействия принимает следующий вид.
Trade Stream
│
▼
TradeStreamConsistencyController
▲ ▲
│ │
│ │
WebSocket Feed Trade Recovery
Подобное решение обеспечивает несколько важных преимуществ.
Во-первых, независимо от источника получения сделки используются абсолютно одинаковые правила проверки последовательности.
Во-вторых, отсутствует необходимость синхронизации нескольких внутренних состояний.
В-третьих, Recovery становится полностью независимым от внутренней реализации механизма согласованности.
Он работает исключительно через публичный контракт TradeStreamConsistencyProtocol.
Это полностью соответствует принципу Dependency Inversion, принятому в архитектуре Dzentra.
Анализ порядка восстановления
Следующим результатом архитектурного аудита стало исследование порядка поступления исторических сделок через REST API.
Проверка показала, что транспортный уровень не должен рассматриваться как источник архитектурных гарантий.
Даже если конкретная реализация биржи в настоящий момент возвращает сделки в возрастающем порядке, подобное поведение не должно использоваться в качестве архитектурного инварианта системы.
Recovery обязан самостоятельно формировать каноническую последовательность перед передачей сделок в Trade Stream Consistency.
Поэтому было принято решение добавить специализированный уровень нормализации восстановленных данных.
Recovery Normalizer
В рамках настоящего Build появляется отдельный компонент
Trade Recovery Normalizer
Его задача предельно проста.
Компонент принимает последовательность уже построенных канонических объектов Trade и возвращает новую неизменяемую последовательность, отсортированную по trade_id.
Нормализатор сознательно не выполняет:
- дедупликацию;
- проверку порядка;
- проверку корректности данных;
- обнаружение конфликтующих повторов.
Все перечисленные задачи полностью принадлежат Trade Stream Consistency.
Подобное разделение обязанностей исключает дублирование бизнес-логики между двумя подсистемами.
Архитектурное решение
По результатам проведённого аудита было принято решение реализовать подсистему Trade Recovery как самостоятельный orchestration-слой, использующий уже существующие компоненты Acquisition Layer.
Recovery не содержит собственного Parser.
Recovery не содержит собственного Mapper.
Recovery не содержит собственного механизма проверки согласованности.
Все перечисленные обязанности делегируются уже существующим специализированным компонентам системы.
После завершения Build архитектура принимает следующий вид.
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 и каноническим потоком появляется отдельный уровень восстановления.
Архитектура обработки принимает следующий вид.
REST API
│
▼
Trade Recovery
│
▼
Trade Stream Consistency
│
▼
Canonical Trade Stream
Появление данного уровня является принципиальным расширением возможностей Acquisition Layer.
Теперь система способна безопасно повторно получать ранее пропущенные сделки, не нарушая уже сформированные архитектурные инварианты.
Архитектурное решение
Во время проектирования рассматривались несколько вариантов организации Recovery Pipeline.
Первый вариант предполагал реализацию всей логики восстановления внутри одного большого компонента.
Подобный компонент самостоятельно выполнял бы:
- получение данных;
- обработку транспортного документа;
- сортировку сделок;
- проверку согласованности;
- формирование результата.
После анализа архитектуры данный подход был отклонён.
Он нарушал принцип единственной ответственности и неизбежно приводил к дублированию уже существующих компонентов Acquisition Layer.
Поэтому было принято другое решение.
Trade Recovery становится исключительно координационным уровнем.
Он объединяет уже реализованные сервисы, не заменяя их.
Каждый существующий компонент продолжает выполнять только собственную специализированную задачу.
Архитектура новой подсистемы
В рамках Build реализованы шесть новых компонентов.
TradeRecoveryProtocol
TradeRecoveryController
TradeRecoveryRequest
TradeRecoveryResult
TradeRecoveryNormalizer
Trade Recovery Exceptions
Каждый компонент обладает собственной строго определённой областью ответственности.
Ни один из компонентов не дублирует обязанности другого.
Подобное разделение полностью соответствует принципу Single Responsibility, принятому в архитектуре Dzentra.
TradeRecoveryProtocol
Одной из целей настоящего Build являлось формирование полноценного контрактного уровня новой подсистемы.
До начала Build соответствующий Protocol отсутствовал.
В рамках реализации был добавлен новый контракт.
TradeRecoveryProtocol
Protocol определяет единственную публичную операцию.
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 становится появление специализированного компонента
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 становится
TradeRecoveryController
Именно он завершает построение новой подсистемы Trade Recovery.
Следует подчеркнуть, что Controller не является компонентом обработки данных.
Он представляет собой исключительно orchestration-службу.
Controller не анализирует транспортные документы.
Не выполняет проверку схемы.
Не преобразует транспортные модели.
Не реализует проверку согласованности.
Все перечисленные задачи уже принадлежат специализированным компонентам Acquisition Layer.
Controller лишь организует их совместную работу.
Архитектура Controller
Конструкция Controller намеренно сделана максимально компактной.
Он использует всего две основные зависимости.
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 становится полностью детерминированным.
Каждый запрос восстановления проходит одну и ту же последовательность этапов.
Ни один шаг не зависит от конкретной реализации биржи или транспортного уровня.
Полный алгоритм обработки выглядит следующим образом.
TradeRecoveryRequest
│
▼
Получение REST-документа
│
▼
Schema Validation
│
▼
REST Trade Adapter
│
▼
TradeRecoveryNormalizer
│
▼
TradeStreamConsistencyController
│
▼
Формирование TradeRecoveryResult
Каждый этап выполняет строго одну задачу.
Ни один уровень не повторяет обязанности другого.
Подобная последовательность полностью соответствует принципу конвейерной обработки данных, принятому в архитектуре Dzentra.
Использование существующего REST Pipeline
Одним из главных архитектурных результатов настоящего Build стало повторное использование уже реализованного REST-конвейера.
Recovery не содержит собственной реализации получения сделок.
После получения запроса Controller передаёт управление существующей инфраструктуре.
Используется следующая последовательность.
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 получает детерминированный порядок обработки.
Trade #501
Trade #503
Trade #502
преобразуется в
Trade #501
Trade #502
Trade #503
Никакие другие изменения последовательности не выполняются.
Normalizer не удаляет элементы.
Не объединяет записи.
Не изменяет содержимое сделок.
Благодаря этому Recovery остаётся полностью независимым от правил проверки согласованности.
Передача сделок в Trade Stream Consistency
После завершения нормализации каждая сделка последовательно передаётся в существующий экземпляр
TradeStreamConsistencyController
Для каждой сделки выполняется стандартная операция.
accept(trade)
│
▼
Trade | None
Recovery сознательно не интерпретирует внутреннюю логику Controller.
Если сделка публикуется впервые, она включается в итоговый результат восстановления.
Если обнаружен идентичный повтор, Controller возвращает None.
Если обнаруживается нарушение архитектурных инвариантов, генерируется соответствующее специализированное исключение.
Recovery не подавляет подобные ошибки.
Они передаются вызывающему компоненту без изменения.
Такое решение гарантирует единообразное поведение независимо от источника получения сделок.
Формирование результата восстановления
После завершения обработки всей последовательности Controller формирует итоговый объект
TradeRecoveryResult
В результирующую коллекцию попадают только сделки, успешно принятые механизмом Trade Stream Consistency.
Идентичные дубликаты автоматически исключаются из результата.
Конфликтующие повторы не публикуются вовсе.
Если восстановленный диапазон не содержит новых сделок, Recovery возвращает корректный пустой результат.
Подобное поведение рассматривается как полностью штатная ситуация.
Отсутствие новых сделок не является ошибкой восстановления.
Новые доменные исключения
Следующим результатом Build становится появление специализированной иерархии исключений подсистемы Trade Recovery.
До настоящего Build подобные ошибки отсутствовали.
В рамках реализации добавлены следующие классы.
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.
Все изменения были сознательно локализованы внутри нового каталога
src/market_data/acquisition/recovery/
Подобное решение позволило полностью изолировать новую функциональность от уже существующих компонентов Acquisition Layer.
Ни один ранее реализованный Parser, Mapper, Adapter, Validator или Controller не потребовал изменения собственной бизнес-логики.
Новая подсистема была встроена посредством повторного использования уже существующей архитектуры.
Trade Recovery Protocol
src/market_data/acquisition/recovery/trade_recovery_protocol.py
Добавлен новый Protocol.
TradeRecoveryProtocol
Protocol определяет единственный публичный контракт новой подсистемы.
recover(request: TradeRecoveryRequest) -> TradeRecoveryResult
Благодаря этому все последующие компоненты Runtime смогут зависеть исключительно от абстракции Recovery, а не от конкретной реализации Controller.
Trade Recovery Request
src/market_data/acquisition/recovery/trade_recovery_request.py
Добавлена специализированная immutable-модель запроса восстановления.
Модель инкапсулирует все параметры Recovery.
Выполняется встроенная проверка:
- торгового символа;
- диапазона времени;
- максимального размера окна;
- ограничения
limit; - корректности типов входных данных.
Таким образом Controller никогда не начинает выполнение с некорректным запросом.
Trade Recovery Result
src/market_data/acquisition/recovery/trade_recovery_result.py
Добавлена immutable-модель результата восстановления.
Result содержит:
- symbol;
- requested_start_time;
- requested_end_time;
- tuple восстановленных сделок.
Также реализованы вычисляемые свойства:
recovered_count
is_empty
first_trade
last_trade
Подобный подход позволяет избежать повторной обработки коллекции вызывающим кодом.
Trade Recovery Exceptions
src/market_data/acquisition/recovery/trade_recovery_exceptions.py
Добавлена специализированная иерархия доменных исключений Recovery.
Реализованы классы:
TradeRecoveryError
TradeRecoveryWindowError
TradeRecoveryLimitError
TradeRecoveryNormalizationError
TradeRecoveryControllerError
Разделение ошибок позволяет более точно диагностировать причины возникновения исключительных ситуаций без использования универсального класса ошибок.
Trade Recovery Normalizer
src/market_data/acquisition/recovery/trade_recovery_normalizer.py
Добавлен специализированный компонент нормализации.
Normalizer отвечает исключительно за сортировку восстановленной последовательности по trade_id.
Компонент сознательно не выполняет:
- дедупликацию;
- проверку порядка;
- изменение объектов
Trade; - фильтрацию сделок.
Подобное разделение обязанностей полностью соответствует архитектуре Acquisition Layer.
Trade Recovery Controller
src/market_data/acquisition/recovery/trade_recovery_controller.py
Реализован центральный компонент новой подсистемы.
Controller отвечает исключительно за координацию процесса восстановления.
В частности он выполняет:
- получение транспортного документа;
- запуск существующего REST Pipeline;
- нормализацию восстановленных сделок;
- последовательную передачу сделок в
TradeStreamConsistencyController; - формирование объекта
TradeRecoveryResult.
Внутри Controller отсутствует собственная бизнес-логика обработки транспортных данных.
Вся обработка делегируется уже существующим специализированным компонентам.
Добавленные unit-тесты
Настоящий Build сопровождается полноценным покрытием новой подсистемы unit-тестами.
Все тесты написаны исключительно через публичные интерфейсы компонентов.
Ни один тест не зависит от внутренней реализации Recovery.
Подобный подход позволяет свободно изменять внутреннее устройство подсистемы без изменения существующего набора тестов.
TradeRecoveryRequest
Добавлен новый файл.
tests/unit/market_data/acquisition/recovery/test_trade_recovery_request.py
Проверяются следующие сценарии.
Создание корректного запроса
Подтверждается успешное создание объекта с допустимыми параметрами.
Проверка symbol
Подтверждается невозможность создания запроса с пустым символом.
Проверка диапазона времени
Проверяется невозможность задания диапазона, в котором начало превышает окончание.
Проверка отрицательных временных меток
Подтверждается генерация исключения при использовании отрицательных значений.
Проверка максимального окна восстановления
Подтверждается невозможность создания запроса с временным диапазоном, превышающим допустимый предел.
Проверка limit
Проверяются допустимые и недопустимые значения ограничения количества сделок.
TradeRecoveryResult
Добавлен новый файл.
tests/unit/market_data/acquisition/recovery/test_trade_recovery_result.py
Проверяются:
- корректное формирование результата;
- вычисляемое количество сделок;
- пустой результат;
- первая сделка;
- последняя сделка;
- неизменяемость модели.
TradeRecoveryNormalizer
Добавлен новый файл.
tests/unit/market_data/acquisition/recovery/test_trade_recovery_normalizer.py
Проверяются следующие сценарии.
Сортировка последовательности
Подтверждается корректная сортировка сделок по trade_id.
Работа с пустой коллекцией
Подтверждается корректное поведение при отсутствии сделок.
Работа с уже отсортированной последовательностью
Подтверждается отсутствие побочных эффектов.
Возврат immutable-последовательности
Подтверждается, что результат всегда представлен в виде tuple.
TradeRecoveryController
Добавлен новый файл.
tests/unit/market_data/acquisition/recovery/test_trade_recovery_controller.py
Проверяются следующие сценарии.
Использование существующего TradeStreamConsistencyController
Подтверждается, что Controller использует переданный экземпляр механизма согласованности, не создавая собственный.
Последовательная обработка восстановленных сделок
Подтверждается передача всех сделок в порядке возрастания trade_id.
Исключение идентичных повторов
Подтверждается корректная обработка ситуации, когда accept() возвращает None.
Формирование TradeRecoveryResult
Проверяется корректное формирование итогового результата восстановления.
Работа с пустым диапазоном
Подтверждается корректное формирование пустого результата.
Передача исключений
Проверяется, что Recovery не подавляет исключения, возникающие внутри Trade Stream Consistency.
Результаты тестирования
После завершения реализации был выполнен запуск полного набора unit-тестов новой подсистемы Recovery.
Сначала была выполнена проверка моделей и вспомогательных компонентов.
Использовались команды.
python -m pytest -q \
tests/unit/market_data/acquisition/recovery/
Результат.
70 passed
Все предусмотренные сценарии успешно пройдены.
Тестирование подтвердило:
- корректность валидации
TradeRecoveryRequest; - корректность формирования
TradeRecoveryResult; - детерминированную работу Recovery Normalizer;
- корректную координацию Recovery Controller;
- корректную интеграцию с Trade Stream Consistency.
После этого был выполнен запуск полного набора тестов подсистемы Market Data Acquisition.
Использовалась команда.
python -m pytest -q tests/unit/market_data/acquisition
Результат.
1039 passed
Ни одного регрессионного отклонения обнаружено не было.
Все ранее реализованные компоненты Acquisition Layer продолжают работать без изменений.
Полная регрессионная проверка проекта
Заключительным этапом Build стал запуск полного набора unit-тестов всего проекта.
Использовалась команда.
python -m pytest -q
Получен следующий результат.
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.
Теперь общая архитектура обработки исторических сделок принимает следующий вид.
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 станет обязательной частью общего конвейера обработки сделок.
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 сформировал Trade Runtime Architecture, а владение состоянием было скорректировано в Build 060.20.1. Этапы Runtime Protocol, Service и Acquisition Integration выполнены в 060.21–060.23, затем 060.24 завершил Runtime Recovery, 060.25 — Production Runtime, 060.26 — Integration & Regression, 060.27–060.29 — Storage/Checkpoint/Access, а 060.30 — Final Documentation. После 060.30 утверждён только 061.00; номера следующих Feed-веток ещё не назначены. Актуальная последовательность: Master Roadmap.
Build 060.20 — Trade Recovery Registry