Files
dzentra_bot/docs/migrations/build_060_19_architecture.md

155 KiB
Raw Blame History

Build 060.19 — Trade Recovery Architecture

Статус: Architecture Specification
Build: 060.19
Ветка: Trades Feed (Time & Sales)
Документ: build_060_19_architecture.md
Связанные документы: build_060_19.md — план реализации данного Build.
Предыдущий Build: Build 060.18 — Trade Stream Consistency Controller.


Назначение документа

Настоящий документ является официальной архитектурной спецификацией Build 060.19 и определяет построение подсистемы восстановления потока сделок (Trade Recovery).

Документ фиксирует все архитектурные решения, принятые до начала реализации, и является единственным источником истины (Single Source of Truth) при разработке данного Build.

Все решения, описанные ниже, считаются утверждёнными до начала реализации и не должны изменяться в процессе написания кода без подготовки нового ADR.

Настоящий документ подготовлен на основании:

  • архитектуры предыдущих Build серии 060;
  • экспериментального исследования REST API биржи;
  • полного аудита существующей реализации Acquisition Pipeline;
  • анализа существующих транспортных адаптеров, Feed, Handler, Runtime и Consistency Layer.

Никакие положения настоящего документа не основаны на предположениях о поведении биржи. Все архитектурные решения принимаются исключительно на основании подтверждённого поведения существующей системы и экспериментально проверенного контракта REST API.


Статус Build

Build 060.19 является непосредственным продолжением Build 060.18.

К моменту начала данного Build в системе уже существуют:

  • Canonical Trade Model;
  • Parser;
  • Mapper;
  • Value Validation;
  • REST Adapter;
  • WebSocket Adapter;
  • Trades Feed;
  • Trades Handler;
  • Trade Stream Consistency Controller;
  • Canonical Trade Stream.

Таким образом, система уже умеет:

  • получать сделки через REST;
  • получать сделки через WebSocket;
  • преобразовывать транспортные структуры в Canonical Trade;
  • формировать согласованный поток сделок.

Однако система ещё не умеет восстанавливать пропущенный участок истории после временной потери WebSocket-потока.

Именно эту задачу решает Build 060.19.


Контекст

После завершения Build 060.18 система гарантирует согласованность каждой опубликованной сделки.

Однако согласованность ещё не означает полноту потока.

Рассмотрим пример.

Trade #100

Trade #101

(WebSocket отключился)

Trade #110

С точки зрения Build 060.18:

  • Trade #110 является корректной;
  • порядок не нарушен;
  • конфликтующих повторов нет.

Следовательно, сделка будет успешно опубликована.

Однако фактически поток потерял сделки:

102

103

104

105

106

107

108

109

Следовательно, после появления Canonical Trade Stream возникает новая архитектурная задача.

Необходимо восстановить отсутствующий участок истории до продолжения нормальной обработки WebSocket.

Именно этот механизм вводится Build 060.19.


Предпосылки

Настоящий Build опирается на архитектурные решения предыдущих Build серии 060.

Build 057

Определены фундаментальные принципы Acquisition Layer.

Разделены транспортный и доменный уровни.


Build 060.1

Построена Canonical Trade Model.

Все транспортные источники приводятся к единому объекту Trade.


Build 060.4

Завершено разделение Parser и Mapper.

Parser отвечает исключительно за транспортный уровень.

Mapper отвечает исключительно за построение Domain Model.


Build 060.9

Построена система Value Validation.

Корректность отдельных значений гарантируется до создания объекта Trade.


Build 060.11

Определены транспортные адаптеры REST и WebSocket.


Build 060.17

Построен Trades Feed.

Получение сделок полностью функционирует.


Build 060.18

Построен Canonical Trade Stream.

Введён TradeStreamConsistencyController, обеспечивающий:

  • дедупликацию;
  • контроль порядка;
  • обнаружение конфликтующих дублей;
  • формирование единственного согласованного потока сделок.

Build 060.19 обязан использовать данный механизм без изменения его архитектуры.

Recovery не имеет права реализовывать:

  • собственную дедупликацию;
  • собственную проверку порядка;
  • собственную модель Trade;
  • собственный поток сделок.

Проблема

После потери WebSocket-соединения поток сделок становится неполным.

При этом существующий Consistency Controller не способен самостоятельно восстановить отсутствующий участок истории.

Это не является недостатком Build 060.18.

Напротив, Build 060.18 сознательно ограничивает собственную ответственность исключительно проверкой согласованности уже поступающих сделок.

Следовательно, необходим отдельный компонент, который сможет:

  • получить исторические сделки через REST;
  • преобразовать их в Canonical Trade;
  • безопасно объединить их с текущим потоком;
  • использовать уже существующий механизм проверки согласованности.

Такой компонент должен быть полностью независимым от Runtime, WebSocket Transport и транспортного уровня биржи.

Именно таким компонентом становится Trade Recovery.


Основная идея Build

Главная идея Build заключается в строгом разделении двух независимых задач.

Первая задача:

Получение исторических сделок.

Вторая задача:

Проверка согласованности потока.

Получение истории не должно знать, каким образом выполняется дедупликация.

Контроллер согласованности, в свою очередь, не должен знать, каким способом были получены сделки.

Следовательно, Recovery становится исключительно связующим звеном между существующим REST Pipeline и существующим Stream Consistency Layer.

Он не изменяет их архитектуру и не дублирует их функциональность.


Цель Build

Build обязан обеспечить безопасное восстановление отсутствующего участка истории торгов без нарушения уже существующих архитектурных инвариантов.

После завершения Build система должна обладать следующими свойствами.

  • Recovery использует существующий REST Pipeline.
  • Recovery использует существующий TradeStreamConsistencyController.
  • Recovery не создаёт собственную модель Trade.
  • Recovery не создаёт собственную модель потока.
  • Recovery не выполняет собственную дедупликацию.
  • Recovery не реализует собственные правила Ordering.
  • Recovery не зависит от Runtime.
  • Recovery не зависит от WebSocket Transport.
  • Recovery не зависит от механизма Reconnect.

Таким образом Recovery становится полностью автономным компонентом Acquisition Layer.


Что НЕ входит в Scope Build

Настоящий Build сознательно НЕ реализует:

  • автоматическое обнаружение потери WebSocket;
  • обнаружение Gap;
  • принятие решения о запуске Recovery;
  • управление переподключением;
  • Runtime Integration;
  • Composition Root;
  • сохранение состояния между перезапусками;
  • журналирование Recovery;
  • Retry Policy;
  • стратегию многократного Backfill;
  • телеметрию;
  • метрики.

Все перечисленные задачи относятся к следующим Build серии 060.

Build 060.19 реализует исключительно механизм безопасного восстановления исторических сделок по уже подготовленному диапазону времени.


Архитектурные принципы

При реализации Build 060.19 используются следующие фундаментальные принципы.


1. Canonical First

Recovery никогда не работает с транспортными структурами данных.

Любая информация, полученная через REST API, должна пройти существующий Pipeline:

REST Document

↓

Parser

↓

Value Validation

↓

Mapper

↓

Canonical Trade

Только после появления объекта Trade начинается работа Recovery.

Recovery никогда не анализирует JSON-документ REST напрямую.


2. Existing Pipeline Reuse

Build 060.19 не создаёт собственный REST Pipeline.

Recovery полностью переиспользует существующие компоненты:

  • REST Transport;
  • REST Parser;
  • REST Value Validation;
  • REST Mapper;
  • REST Adapter.

Во время проектирования была рассмотрена возможность реализации отдельного Recovery Adapter.

Данный вариант отклонён.

Причина:

это привело бы к появлению двух различных способов преобразования одного и того же REST-документа в Canonical Trade, что нарушило бы принцип единственной модели преобразования данных.


3. Single Domain Model

Во всей системе продолжает существовать единственная доменная модель сделки:

Trade

Recovery не имеет собственной модели сделки.

Recovery не создаёт дополнительных DTO доменного уровня.

Все операции выполняются исключительно над существующим объектом Trade.


4. Existing Consistency First

Recovery никогда самостоятельно не принимает решения о корректности сделки.

После нормализации последовательности каждая сделка обязательно передаётся в существующий:

TradeStreamConsistencyController

Именно данный компонент остаётся единственным владельцем правил:

  • Ordering;
  • Deduplication;
  • Conflict Detection.

Recovery не имеет права дублировать эти проверки.


5. Stateless Recovery

Recovery не хранит собственного состояния потока.

Он не знает:

  • последний обработанный trade_id;
  • последний опубликованный Trade;
  • состояние дедупликации;
  • историю уже обработанных сделок.

Recovery является stateless-компонентом.

Всё состояние системы по-прежнему принадлежит только Build 060.18.


6. Runtime Independence

Recovery не зависит от Runtime.

Он не знает:

  • произошло ли переподключение;
  • был ли разрыв WebSocket;
  • почему потребовалось восстановление;
  • сколько времени отсутствовало соединение.

Recovery получает уже сформированный запрос восстановления и выполняет только его.


7. Time-Based Recovery

Recovery использует исключительно временные диапазоны.

Во время проектирования были рассмотрены различные варианты построения курсора восстановления.

Использование trade_id было отклонено после экспериментального исследования REST API.

Официальным механизмом навигации Build 060.19 являются:

start_time

↓

end_time

Других типов курсоров Build не предусматривает.


8. One Recovery Direction

REST API возвращает сделки в порядке:

DESCENDING

Однако Canonical Trade Stream существует исключительно в порядке:

ASCENDING

Следовательно Build 060.19 вводит обязательный этап нормализации порядка.

Никакая сделка не может быть передана в Consistency Layer до завершения данной нормализации.


9. Separation Of Responsibilities

Recovery отвечает исключительно за:

  • получение истории;
  • нормализацию порядка;
  • передачу сделок в Consistency Layer.

Recovery сознательно не отвечает за:

  • принятие решения о запуске;
  • вычисление диапазона восстановления;
  • повторные попытки;
  • работу WebSocket;
  • Runtime Integration.

Все перечисленные обязанности относятся к следующим Build серии 060.


10. Unique File Naming

Во всём проекте Dzentra продолжает действовать правило уникальности имён файлов.

Новые файлы Build 060.19 обязаны иметь уникальные имена во всём репозитории.

Допускается единственное исключение:

__init__.py

Использование общих имён файлов запрещается.

Например, не допускаются:

controller.py

request.py

normalizer.py

exceptions.py

Файлы должны отражать своё назначение.

Например:

trade_recovery_controller.py

trade_recovery_request.py

trade_recovery_normalizer.py

trade_recovery_protocol.py

trade_recovery_exceptions.py

Архитектурный фундамент Recovery

После завершения Build 060.18 система получила понятие:

Canonical Trade Stream

Build 060.19 не изменяет данную модель.

Вместо этого появляется новая независимая архитектурная сущность.

Trade Recovery

Recovery не становится частью Stream Consistency.

Recovery располагается перед ним.

Архитектурно система принимает следующий вид.

REST

↓

Trade Recovery

↓

Trade Stream Consistency

↓

Canonical Trade Stream

Таким образом Build 060.19 не расширяет обязанности Consistency Controller.

Он вводит новый независимый уровень Acquisition Pipeline.


Место Recovery в Acquisition Pipeline

До Build 060.19 существовала следующая архитектурная схема.

REST / WebSocket

↓

Parser

↓

Value Validation

↓

Mapper

↓

Trade

↓

TradeStreamConsistencyController

↓

Canonical Trade Stream

После завершения Build 060.19 появляется дополнительный путь обработки исторических сделок.

REST

↓

Parser

↓

Value Validation

↓

Mapper

↓

Trade Recovery

↓

TradeStreamConsistencyController

↓

Canonical Trade Stream

При этом путь обработки WebSocket-сделок остаётся неизменным.

Recovery является дополнительной веткой Acquisition Pipeline и не изменяет существующую архитектуру получения потоковых сделок.


Экспериментальное исследование REST API

Перед проектированием Recovery было выполнено отдельное инженерное исследование поведения REST API биржи.

Целью исследования являлось подтверждение фактического поведения endpoint получения исторических сделок и исключение архитектурных решений, основанных исключительно на документации биржи.

Исследование выполнялось специализированным диагностическим инструментом:

check_trade_backfill_api_final_test.py

Все архитектурные решения настоящего Build принимаются исключительно на основании подтверждённого поведения API.


Подтверждённый контракт REST API

Исследование подтвердило следующие свойства endpoint:

GET /api/v2/aggTrades

Все перечисленные ниже свойства считаются частью архитектурного контракта Build 060.19.


Endpoint

Экспериментально подтверждено:

  • endpoint доступен;
  • endpoint стабилен;
  • endpoint детерминирован;
  • повторные запросы возвращают предсказуемый результат.

Recovery полностью опирается на данный контракт.


Порядок выдачи

REST API возвращает сделки в порядке:

DESCENDING

то есть

newest

↓

oldest

Это фундаментальное свойство Build.

Recovery никогда не имеет права передавать данный поток непосредственно в Stream Consistency.

Перед публикацией последовательность обязательно нормализуется.

REST

DESCENDING

↓

Recovery Normalizer

ASCENDING

↓

TradeStreamConsistencyController

Timestamp

Экспериментально подтверждено:

при уменьшении trade_id

уменьшается

executed_at

Следовательно:

  • timestamp согласован с порядком выдачи;
  • дополнительная сортировка по времени не требуется.

Limit

Подтверждены следующие ограничения.

Корректно работают значения:

1

...

1000

Запрос

limit = 1001

возвращает

HTTP 400

Следовательно Build фиксирует официальный диапазон.

1 <= limit <= 1000

Recovery не имеет права нарушать данный инвариант.


Time Filters

Экспериментально подтверждена корректная работа параметров:

startTime
endTime

а также их совместного использования.

Recovery использует исключительно временные диапазоны.

Поддержка навигации по времени считается официальной частью архитектурного контракта.


Ограничение временного диапазона

При одновременном использовании:

startTime

+

endTime

биржа требует, чтобы длина диапазона была меньше одного часа.

Следовательно Recovery принимает следующий инвариант.

end_time > start_time

и

end_time - start_time < 1 hour

Нарушение данного условия считается ошибкой формирования Recovery Request.


fromId

Во время исследования была проведена серия диагностических тестов параметра:

fromId

Исследовались:

  • исторические значения;
  • глубокие исторические значения;
  • отрицательные значения;
  • будущие значения.

Во всех случаях подтверждена одинаковая картина.

REST продолжает возвращать последнюю страницу сделок.

Следовательно использование fromId как курсора восстановления экспериментально не подтверждено.

Build 060.19 полностью исключает любую зависимость от данного параметра.


Trade IDs

Экспериментально подтверждено:

trade_id

не образует арифметически непрерывную последовательность.

Например:

100

101

118

141

205

является полностью корректной последовательностью.

Следовательно Recovery никогда не делает предположений вида:

next_trade_id = previous_trade_id + 1

Пропуски идентификаторов сами по себе не являются ошибкой.


Repeatability

Повторный запрос одного и того же диапазона времени возвращает одинаковую последовательность trade_id.

Это означает:

  • результаты детерминированы;
  • повторное получение диапазона безопасно;
  • Recovery может повторно использовать один и тот же диапазон без риска появления новых сделок внутри уже завершённого исторического окна.

Выводы исследования

На основании проведённого исследования Build принимает следующие архитектурные решения.

Recovery:

  • не использует fromId;
  • использует только временные диапазоны;
  • никогда не предполагает непрерывность trade_id;
  • всегда нормализует порядок выдачи;
  • полностью доверяет существующему REST Pipeline;
  • не выполняет дополнительную сортировку по времени.

Все перечисленные положения считаются официальными архитектурными инвариантами Build 060.19.


Аудит существующей реализации

После завершения исследования REST API был выполнен полный аудит существующей реализации Acquisition Layer.

Цель аудита состояла в определении минимального объёма изменений, необходимых для реализации Recovery без нарушения уже существующей архитектуры.

В ходе аудита были исследованы:

  • REST Source;
  • REST Adapter;
  • Parser;
  • Mapper;
  • Value Validation;
  • Trades Feed;
  • Trades Handler;
  • Trade Stream Consistency;
  • Runtime;
  • Subscription Layer;
  • WebSocket Protocol.

Результаты аудита определяют архитектурные границы настоящего Build.


Вывод №1

REST Pipeline уже полностью реализован.

Существующий транспортный источник поддерживает получение исторических сделок по временным диапазонам.

Build 060.19 не создаёт нового REST клиента.


Вывод №2

REST Adapter уже полностью реализован.

Pipeline:

REST Document

↓

Parser

↓

Value Validation

↓

Mapper

↓

Trade

уже существует.

Recovery полностью переиспользует данный Pipeline.

Создание второго Adapter запрещается.


Вывод №3

Canonical Trade полностью соответствует требованиям Recovery.

Build не изменяет:

  • модель Trade;
  • Value Validation;
  • Parser;
  • Mapper.

Recovery использует существующий доменный объект без каких-либо изменений.


Вывод №4

TradeStreamConsistencyController полностью готов к интеграции.

Recovery обязан использовать существующий публичный контракт:

accept(trade: Trade) -> Trade | None

Никаких дополнительных методов Controller Build 060.19 не требует.


Вывод №5

Trades Feed не требует изменений архитектуры.

Recovery не интегрируется непосредственно в Feed.

Интеграция с Runtime будет реализована отдельным Build.

Следовательно существующая архитектура Feed остаётся неизменной.


Вывод №6

Trades Handler не требует изменения ответственности.

Во время аудита подтверждено, что Handler уже выполняет исключительно транспортные обязанности:

  • проверку структуры входящего документа;
  • вызов существующего Adapter;
  • получение канонических объектов Trade.

Recovery не переносит в Handler никакой дополнительной логики.

Handler остаётся полностью stateless.


Вывод №7

Runtime уже содержит архитектурные сущности, необходимые для последующей интеграции Recovery.

Во время аудита обнаружены отдельные Runtime-компоненты:

  • Runtime Events;
  • Runtime Commands;
  • Subscription Layer;
  • Runtime Protocol.

Однако Build 060.19 сознательно не использует их напрямую.

Recovery остаётся полностью независимым компонентом.

Интеграция с Runtime переносится на последующие Build серии 060.


Вывод №8

Recovery не отвечает за управление подписками.

Во время аудита подтверждено, что существующий Subscription Layer уже определяет архитектуру подписок WebSocket.

Следовательно Recovery не:

  • создаёт подписки;
  • восстанавливает подписки;
  • отслеживает подписки;
  • управляет жизненным циклом подписок.

Данные обязанности принадлежат Runtime Layer.


Вывод №9

Recovery не зависит от WebSocket Transport.

Во время аудита подтверждено, что существующий WebSocket Protocol определяет исключительно транспортные контракты.

Recovery никогда не работает с:

  • WebSocket Message;
  • Subscription Event;
  • Transport DTO.

Recovery получает уже готовый запрос восстановления.

Таким образом между Recovery и WebSocket отсутствует прямая зависимость.


Вывод №10

Существующий Runtime ещё не содержит реализации Recovery.

Во время аудита подтверждено, что Build 060.19 может быть реализован как полностью автономная подсистема.

Это позволяет:

  • реализовать Recovery;
  • полностью протестировать его;
  • завершить Build;

до появления Runtime Integration.

Данное решение существенно уменьшает связанность системы.


Архитектура Recovery

После завершения аудита необходимо определить архитектурную модель Recovery.

Build вводит новую внутреннюю подсистему Acquisition Layer.

Она состоит из нескольких независимых компонентов.

TradeRecoveryController

        │

        ▼

TradeRecoveryNormalizer

        │

        ▼

TradeStreamConsistencyController

Все перечисленные компоненты относятся исключительно к Build 060.19.

Никакие существующие подсистемы не изменяют собственную ответственность.


Общая архитектурная схема

Полный путь восстановления исторических сделок выглядит следующим образом.

TradeRecoveryRequest

        │

        ▼

DzengiTradesDocumentSource

        │

        ▼

REST Document

        │

        ▼

REST Parser

        │

        ▼

Value Validation

        │

        ▼

REST Mapper

        │

        ▼

tuple[Trade]

        │

        ▼

TradeRecoveryNormalizer

        │

        ▼

ASCENDING Trade Stream

        │

        ▼

TradeStreamConsistencyController

        │

        ▼

TradeRecoveryResult

Данная схема становится официальной архитектурой Recovery Pipeline.


Граница Recovery

Recovery располагается между существующим REST Adapter и существующим Stream Consistency.

До Recovery существует только набор независимых объектов Trade.

REST

↓

Parser

↓

Validation

↓

Mapper

↓

Trade

После Recovery появляется уже согласованный поток.

Trade

↓

Recovery

↓

TradeStreamConsistencyController

↓

Canonical Trade Stream

Recovery никогда не публикует сделки самостоятельно.

Публикация возможна только после успешного прохождения Stream Consistency.


TradeRecoveryController

Назначение

TradeRecoveryController является центральным компонентом Build 060.19.

Именно он управляет полным процессом восстановления исторического диапазона.

Контроллер отвечает исключительно за оркестрацию существующих компонентов.

Он не реализует:

  • транспорт;
  • парсинг;
  • дедупликацию;
  • Ordering;
  • Runtime.

Ответственность Controller

TradeRecoveryController отвечает за:

  • получение Recovery Request;
  • вызов существующего REST Source;
  • вызов существующего REST Adapter;
  • передачу полученного потока в Recovery Normalizer;
  • последовательную передачу сделок в Stream Consistency;
  • формирование итогового результата восстановления.

Controller НЕ отвечает

TradeRecoveryController сознательно не отвечает за:

  • вычисление диапазона времени;
  • выбор момента запуска;
  • Retry Policy;
  • обнаружение Gap;
  • работу WebSocket;
  • управление подписками;
  • Runtime Integration;
  • хранение состояния.

Все перечисленные обязанности относятся к следующим Build.


Главный принцип Controller

TradeRecoveryController не принимает доменных решений.

Все решения уже принадлежат существующим компонентам системы.

Контроллер лишь организует их совместную работу.

Именно поэтому RecoveryController рассматривается как orchestration component, а не как Domain Service.


TradeRecoveryRequest

Назначение

Recovery выполняется исключительно по заранее подготовленному запросу.

Build сознательно разделяет:

  • вычисление диапазона;
  • выполнение восстановления.

Следовательно Recovery получает готовый объект запроса.


Содержимое Recovery Request

Минимальная модель запроса включает:

symbol

start_time

end_time

limit

Этого достаточно для выполнения одного Recovery Pipeline.

Никакие дополнительные поля Build 060.19 не требует.


Почему отсутствует last_processed_timestamp

Во время проектирования рассматривался вариант хранения внутри Recovery собственного курсора:

last_processed_timestamp

Данный вариант отклонён.

Причины:

  • появление внутреннего состояния;
  • зависимость от Runtime;
  • нарушение принципа Stateless Recovery;
  • смешение ответственности Runtime и Recovery.

Recovery никогда самостоятельно не вычисляет диапазон восстановления.

Он лишь выполняет уже подготовленный запрос.


TradeRecoveryRequest

Инварианты

Каждый экземпляр TradeRecoveryRequest обязан удовлетворять следующим требованиям.


Инвариант №1

Запрос относится ровно к одному торговому инструменту.

Например:

BTCUSDT

Recovery никогда не выполняет восстановление нескольких символов одновременно.


Инвариант №2

Временной диапазон обязан быть корректным.

Всегда должно выполняться условие:

start_time < end_time

Нарушение данного условия считается ошибкой формирования Recovery Request.


Инвариант №3

Продолжительность диапазона не должна превышать ограничение REST API.

end_time - start_time < 1 hour

Recovery не выполняет автоматическое разбиение диапазона.

Данная задача относится к следующим Build.


Инвариант №4

Поле limit, если оно указано, обязано удовлетворять диапазону:

1 <= limit <= 1000

Recovery не корректирует ошибочные значения автоматически.


Инвариант №5

Recovery Request является неизменяемым объектом.

После создания его содержимое никогда не изменяется.


Почему используется готовый Request

Во время проектирования рассматривались несколько вариантов.


Вариант 1

Передавать только:

symbol

Отклонён.

Recovery пришлось бы самостоятельно вычислять диапазон восстановления.

Это нарушает разделение ответственности.


Вариант 2

Передавать:

last_trade_id

Отклонён.

Экспериментально подтверждено, что Recovery не должен строиться вокруг trade_id.

Кроме того, существующий REST API не предоставляет надёжного механизма навигации через fromId.


Вариант 3

Передавать:

start_time

end_time

limit

Принят.

Recovery полностью независим от Runtime и выполняет исключительно уже сформированный запрос.


TradeRecoveryNormalizer

Назначение

TradeRecoveryNormalizer является вторым компонентом Recovery Pipeline.

Его единственная задача —

привести последовательность сделок, полученную от REST API, к каноническому порядку публикации.

RecoveryNormalizer не анализирует содержимое сделок.

Он работает исключительно с их последовательностью.


Почему необходим отдельный компонент

Во время проектирования рассматривалась возможность выполнения нормализации непосредственно внутри Controller.

Данный вариант был отклонён.

Причины:

  • смешение обязанностей;
  • ухудшение тестируемости;
  • усложнение Controller;
  • невозможность повторного использования алгоритма.

Разделение Controller и Normalizer соответствует общим архитектурным принципам серии Build 060.


Ответственность Normalizer

TradeRecoveryNormalizer отвечает исключительно за:

  • анализ направления последовательности;
  • нормализацию порядка сделок;
  • проверку корректности направления потока.

Никаких других обязанностей компонент не имеет.


Normalizer НЕ отвечает

TradeRecoveryNormalizer сознательно не отвечает за:

  • REST;
  • Parser;
  • Mapper;
  • Value Validation;
  • Deduplication;
  • Ordering;
  • Runtime;
  • Recovery Request.

Контракт Normalizer

Normalizer получает:

tuple[Trade]

и возвращает:

tuple[Trade]

Количество сделок никогда не изменяется.

Normalizer никогда:

  • не удаляет сделки;
  • не добавляет сделки;
  • не модифицирует сделки.

Изменяется исключительно порядок их следования.


Модель нормализации

Во время исследования REST API было подтверждено следующее поведение.

REST API возвращает сделки в порядке:

DESCENDING

Следовательно Recovery обязан преобразовать поток в:

ASCENDING

Только после этого сделки могут быть переданы в Stream Consistency.


Допустимые варианты последовательности

Build 060.19 формально определяет допустимые варианты входной последовательности.


Вариант №1

Последовательность уже является возрастающей.

Например:

100

105

121

140

В этом случае RecoveryNormalizer возвращает её без изменений.


Вариант №2

Последовательность является убывающей.

Например:

140

121

105

100

В этом случае выполняется нормализация.

Результат:

100

105

121

140

Вариант №3

Последовательность имеет смешанный порядок.

Например:

100

150

120

180

Такой поток считается архитектурно недопустимым.

Normalizer обязан завершить обработку ошибкой.

Никакие сделки не передаются в Stream Consistency.


Почему запрещается смешанный порядок

Смешанная последовательность означает нарушение фундаментального контракта транспортного уровня.

Recovery не должен самостоятельно исправлять подобные ошибки.

Подобная ситуация рассматривается как нарушение архитектурных инвариантов Acquisition Layer.

Следовательно единственно допустимым поведением является генерация доменного исключения.


Пустая последовательность

Если REST Pipeline возвращает:

()

Normalizer возвращает ту же пустую последовательность.

Ошибки не возникает.

Это означает отсутствие сделок внутри указанного диапазона времени.


Последовательность из одной сделки

Если получена единственная сделка:

Trade

никакая нормализация не требуется.

Trade передаётся в Stream Consistency без изменений.


Неизменяемость Trade

Во время нормализации запрещается изменять объект Trade.

Допускается изменение исключительно порядка следования элементов внутри коллекции.

Canonical Trade остаётся полностью immutable.


Передача сделок в Stream Consistency

После завершения нормализации Recovery начинает публикацию сделок в существующий TradeStreamConsistencyController.

Передача выполняется строго последовательно.

Для каждой сделки вызывается существующий публичный контракт:

accept(trade: Trade) -> Trade | None

Recovery никогда не обходит данный интерфейс.


Последовательность обработки

После получения нормализованной последовательности обработка всегда выполняется по одной и той же схеме.

Trade #1

↓

accept()

↓

Trade #2

↓

accept()

↓

Trade #3

↓

accept()

↓

...

Recovery никогда не передаёт контроллеру сразу всю коллекцию.

Контроллер продолжает работать исключительно со входящим потоком отдельных сделок.

Это сохраняет единый механизм обработки как для REST, так и для WebSocket.


Почему используется последовательная обработка

Во время проектирования рассматривалась возможность добавить новый метод вида:

accept_many(...)

Данный вариант был отклонён.

Причины:

  • появление второго публичного API;
  • дублирование логики;
  • нарушение единой модели обработки потока;
  • увеличение сложности тестирования.

Build 060.19 сохраняет существующий контракт без изменений.


Роль TradeStreamConsistencyController

Recovery не принимает решений относительно результата обработки сделки.

Все решения принадлежат исключительно существующему Controller.

Для каждой сделки возможны только три сценария.


Сценарий №1

Controller возвращает:

Trade

Это означает успешное прохождение проверки согласованности.

Recovery включает сделку в итоговый результат восстановления.


Сценарий №2

Controller возвращает:

None

Это означает обнаружение корректного дубликата.

Recovery не считает подобную ситуацию ошибкой.

Такая сделка просто не включается в итоговый поток восстановления.


Сценарий №3

Controller генерирует исключение.

Например:

TradeConsistencyError

или

TradeOrderingError

Recovery немедленно прекращает выполнение.

Итог восстановления считается неуспешным.


Почему Recovery не перехватывает ошибки Consistency

Во время проектирования рассматривался вариант автоматического продолжения восстановления после ошибок согласованности.

Данный вариант отклонён.

Причины:

  • нарушение архитектурных инвариантов;
  • сокрытие ошибок потока;
  • появление недостоверного результата восстановления.

Recovery никогда не скрывает ошибки Controller.

Исключения распространяются вверх без изменения их смысла.


Формирование результата Recovery

После обработки всех сделок Recovery формирует единый результат выполнения.

Build 060.19 вводит отдельную доменную модель результата.

TradeRecoveryResult

Данный объект не относится к транспортному уровню.

Он описывает исключительно итог работы Recovery.


Почему вводится отдельный Result

Во время проектирования рассматривались несколько вариантов.


Вариант №1

Возвращать:

tuple[Trade]

Отклонён.

По коллекции невозможно определить:

  • была ли выполнена обработка полностью;
  • сколько сделок было отброшено как дубликаты;
  • завершилось ли восстановление успешно.

Вариант №2

Возвращать:

list[Trade]

Отклонён по тем же причинам.

Кроме того, Canonical Pipeline использует неизменяемые коллекции.


Вариант №3

Использовать специализированный объект результата.

Принят.

Именно он становится официальным контрактом Recovery.


Назначение TradeRecoveryResult

TradeRecoveryResult описывает завершённую операцию восстановления.

Он не является журналом выполнения.

Он не содержит внутреннего состояния Recovery.

Он лишь фиксирует итог уже завершённой операции.


Минимальный состав Result

Build 060.19 определяет следующий минимальный набор информации.

Recovered Trades

Skipped Duplicates

Этого достаточно для оценки результата работы Recovery.

Расширенные диагностические поля будут добавлены в следующих Build.


Почему Result не содержит ошибки

Во время проектирования рассматривался вариант хранения исключения внутри объекта результата.

Например:

error

или

exception

Данный вариант отклонён.

Recovery использует стандартную модель обработки ошибок Python.

При возникновении исключения объект результата не создаётся.


Обработка пустого восстановления

Если REST не возвращает ни одной сделки,

Recovery успешно завершается.

Результат содержит:

Recovered Trades = 0

Skipped Duplicates = 0

Подобная ситуация считается полностью корректной.


Обработка полного дублирования

Если все полученные сделки уже присутствуют в Canonical Stream,

Controller вернёт:

None

для каждой сделки.

Recovery завершится успешно.

Результат будет иметь вид:

Recovered Trades = 0

Skipped Duplicates = N

Ошибки не возникает.


Обработка частичного восстановления

Наиболее типичный сценарий.

Например:

REST вернул:

100

105

121

140

Controller определил:

100 -> duplicate

105 -> accepted

121 -> accepted

140 -> accepted

Итог Recovery:

Recovered Trades = 3

Skipped Duplicates = 1

Именно подобный сценарий считается основной моделью работы Recovery.


Завершение Recovery

Recovery считается успешно завершённым только после выполнения всех условий:

  • REST Pipeline завершился без ошибок;
  • Normalizer успешно обработал последовательность;
  • все сделки переданы в Stream Consistency;
  • Controller не сгенерировал исключений;
  • сформирован TradeRecoveryResult.

Только после этого операция восстановления считается завершённой.


Исключения Recovery

Build 060.19 вводит собственный набор доменных исключений Recovery.

Они описывают ошибки исключительно уровня восстановления истории и не заменяют существующие исключения других компонентов системы.

Recovery никогда не создаёт новые исключения для задач:

  • Parser;
  • Mapper;
  • Value Validation;
  • Trade Stream Consistency.

Каждый уровень системы продолжает использовать собственную модель ошибок.


Принцип распространения исключений

Recovery придерживается принципа Fail Fast.

Если любой нижележащий компонент завершает работу исключением,

Recovery немедленно прекращает выполнение.

Никакие ошибки не:

  • скрываются;
  • преобразуются;
  • игнорируются;
  • журналируются внутри Recovery.

Ответственность Recovery ограничивается только распространением ошибки вызывающему компоненту.


Собственные ошибки Recovery

Build 060.19 предусматривает появление специализированных исключений Recovery.

Например:

TradeRecoveryError

базовое исключение Recovery.

От него могут наследоваться специализированные ошибки.

Например:

TradeRecoveryNormalizationError

Ошибка нормализации последовательности.


TradeRecoveryRequestError

Некорректный Recovery Request.


TradeRecoveryConfigurationError

Некорректная конфигурация Recovery.


Данный перечень может быть расширен в последующих Build без изменения публичной архитектуры Recovery.


Ошибки, которые Recovery не создаёт

Recovery никогда не создаёт:

TradeConsistencyError

или

TradeOrderingError

Данные исключения принадлежат исключительно Build 060.18.

Recovery лишь распространяет их без изменения.


Обработка транспортных ошибок

Если REST Source завершает работу исключением,

например:

HTTP Error

Network Error

Timeout

Recovery не пытается выполнить повторный запрос.

Retry Policy не входит в Scope Build 060.19.

Исключение распространяется вызывающему компоненту.


Обработка ошибок Parser

Если Parser обнаруживает нарушение транспортного контракта,

Recovery не вмешивается.

Исключение считается критическим.

Восстановление прекращается.


Обработка ошибок Value Validation

Если хотя бы одна сделка не проходит существующую систему Validation,

Recovery завершается ошибкой.

Продолжение восстановления после подобных ошибок запрещается.


Обработка ошибок Mapper

Если Mapper не способен сформировать Canonical Trade,

Recovery немедленно прекращает выполнение.

Никакие частично обработанные результаты не публикуются.


Обработка ошибок Normalizer

Если Normalizer обнаруживает смешанный порядок последовательности,

генерируется:

TradeRecoveryNormalizationError

После возникновения данной ошибки:

  • ни одна сделка не передаётся в Stream Consistency;
  • результат Recovery не создаётся;
  • выполнение прекращается.

Обработка ошибок Stream Consistency

Если TradeStreamConsistencyController обнаруживает нарушение инвариантов потока,

Recovery не выполняет никаких дополнительных действий.

Исключение распространяется вверх без изменения.

Таким образом единственным владельцем логики проверки согласованности остаётся Build 060.18.


Тестируемость Recovery

Recovery проектируется как полностью детерминированная подсистема.

При одинаковых входных данных Recovery всегда обязан выдавать одинаковый результат.

Никакие внешние факторы не должны влиять на результат выполнения.


Принцип детерминированности

При фиксированных:

  • Recovery Request;
  • ответе REST;
  • состоянии Stream Consistency;

результат Recovery всегда обязан быть идентичным.

Это значительно упрощает автоматическое тестирование.


Изоляция компонентов

Каждый компонент Recovery должен тестироваться независимо.

Например:

TradeRecoveryNormalizer может быть протестирован без:

  • REST;
  • Runtime;
  • Stream Consistency.

TradeRecoveryController может быть протестирован с использованием Mock Source и Mock ConsistencyController.

Подобное разделение является обязательным архитектурным требованием.


Unit-тестирование

Минимальный набор Unit-тестов должен покрывать:

Recovery Request

  • корректное создание;
  • нарушение временного диапазона;
  • нарушение ограничения limit;
  • неизменяемость объекта.

Recovery Normalizer

  • пустая последовательность;
  • одна сделка;
  • ASCENDING;
  • DESCENDING;
  • смешанный порядок;
  • отсутствие изменения объектов Trade.

Recovery Controller

  • успешное восстановление;
  • пустое восстановление;
  • полное дублирование;
  • частичное восстановление;
  • ошибка REST;
  • ошибка Parser;
  • ошибка Mapper;
  • ошибка Validation;
  • ошибка Consistency Controller.

Интеграционные тесты

После завершения Unit-тестирования Build предусматривает отдельный набор интеграционных сценариев.

Минимально должны быть проверены:

  • получение истории через существующий REST Pipeline;
  • передача результата в Recovery;
  • нормализация последовательности;
  • взаимодействие с существующим TradeStreamConsistencyController;
  • формирование итогового TradeRecoveryResult.

Архитектурные инварианты Build

После завершения Build 060.19 система обязана удовлетворять следующим инвариантам.


Инвариант №1

Recovery никогда не работает с транспортными структурами.


Инвариант №2

Recovery использует исключительно существующий REST Pipeline.


Инвариант №3

Recovery использует исключительно существующий TradeStreamConsistencyController.


Инвариант №4

Recovery никогда не реализует собственную дедупликацию.


Инвариант №5

Recovery никогда не реализует собственную проверку порядка сделок.


Инвариант №6

Recovery никогда не изменяет объект Trade.


Инвариант №7

Recovery никогда не вычисляет диапазон восстановления самостоятельно.


Инвариант №8

Recovery полностью независим от Runtime.


Инвариант №9

Recovery полностью независим от WebSocket Transport.


Инвариант №10

Recovery не изменяет архитектуру существующих Build серии 060.

Он исключительно дополняет Acquisition Layer новой автономной подсистемой восстановления истории.


Итоги Build 060.19

После реализации Build 060.19 система впервые получает полноценный механизм безопасного восстановления исторических сделок.

При этом сохраняются все архитектурные принципы серии Build 060:

  • единая Canonical Trade Model;
  • единый REST Pipeline;
  • единая система Validation;
  • единый Mapper;
  • единый Trade Stream Consistency Controller;
  • единый Canonical Trade Stream.

Recovery не создаёт альтернативную архитектуру обработки сделок.

Напротив, он органично встраивается в уже существующий Acquisition Pipeline и использует ранее построенные компоненты без дублирования их ответственности.

Именно этот подход обеспечивает минимальную связанность подсистем, высокую тестируемость, предсказуемость поведения и возможность дальнейшего развития Runtime, Reconnect и Backfill-механизмов в последующих Build без изменения фундаментальной архитектуры Recovery.


Приложение A. Architecture Decision Records (ADR)


Назначение приложения

Настоящее приложение фиксирует все ключевые архитектурные решения, принятые при проектировании Build 060.19.

Основной документ описывает итоговую архитектуру системы.

ADR, в свою очередь, отвечает на другой вопрос:

Почему архитектура выглядит именно так?

Каждый ADR фиксирует:

  • исходную проблему;
  • рассмотренные варианты;
  • принятое решение;
  • причины выбора;
  • последствия данного решения.

После утверждения ADR считается частью архитектурного контракта системы.

Изменение любого ADR требует подготовки нового архитектурного решения и не допускается в процессе реализации Build.


ADR-060.19-001

Recovery использует существующий TradeStreamConsistencyController

Статус

Accepted

Контекст

После появления механизма восстановления истории необходимо определить, каким образом проверяется корректность восстановленного потока.

К моменту начала Build уже существует полноценный компонент:

TradeStreamConsistencyController

который гарантирует:

  • дедупликацию;
  • контроль порядка;
  • обнаружение конфликтующих дублей;
  • защиту Canonical Trade Stream.

Возникает вопрос:

должен ли Recovery реализовывать аналогичную функциональность самостоятельно?


Рассмотренные варианты

Вариант 1

Recovery реализует собственную дедупликацию.

Преимущества:

  • независимость.

Недостатки:

  • дублирование логики;
  • появление второго источника истины;
  • риск расхождения алгоритмов;
  • двойное сопровождение.

Вариант 2

Recovery реализует собственную проверку порядка.

Преимущества:

локальная автономность.

Недостатки:

  • две различные реализации Ordering;
  • вероятность различного поведения REST и WebSocket;
  • нарушение принципа единственного владельца бизнес-правил.

Вариант 3

Recovery полностью использует существующий TradeStreamConsistencyController.

Преимущества:

  • единая логика проверки;
  • единая дедупликация;
  • единая модель Ordering;
  • отсутствие дублирования;
  • минимальная связанность.

Недостатков не обнаружено.


Принятое решение

Build 060.19 использует исключительно существующий публичный контракт:

accept(trade: Trade) -> Trade | None

Recovery никогда не реализует собственную проверку согласованности.


Последствия

Во всей системе существует только один компонент, отвечающий за:

  • Ordering;
  • Deduplication;
  • Conflict Detection.

Таким компонентом является:

TradeStreamConsistencyController

Recovery остаётся исключительно оркестратором.


ADR-060.19-002

Recovery использует временные диапазоны вместо trade_id

Статус

Accepted

Контекст

Необходимо определить способ получения исторических сделок.

Первоначально рассматривались два варианта:

  • восстановление по trade_id;
  • восстановление по времени.

Исследование

Перед принятием решения было выполнено экспериментальное исследование REST API.

Подтверждено:

  • startTime работает корректно;
  • endTime работает корректно;
  • совместное использование поддерживается;
  • результаты детерминированы.

Одновременно было установлено:

использование:

fromId

не обеспечивает надёжного позиционирования внутри истории.

Во многих случаях REST возвращает последнюю страницу сделок независимо от указанного значения.

Следовательно построение архитектуры Recovery вокруг trade_id признано небезопасным.


Рассмотренные варианты

Вариант 1

Использовать:

fromId

Отклонён.

Причина:

контракт REST API не подтверждён экспериментально.


Вариант 2

Использовать:

trade_id

как внутренний курсор Runtime.

Отклонён.

Причины:

  • привязка Recovery к внутреннему состоянию;
  • невозможность гарантировать корректное восстановление;
  • зависимость от неподтверждённого поведения биржи.

Вариант 3

Использовать исключительно временной диапазон.

Принят.


Принятое решение

Recovery использует только:

start_time

↓

end_time

Все остальные механизмы навигации исключены из архитектуры Build.


Последствия

Recovery становится полностью независимым от внутренней структуры идентификаторов сделок.

Даже если биржа изменит механизм формирования trade_id, архитектура Recovery останется корректной.


ADR-060.19-003

Recovery не хранит собственного состояния

Статус

Accepted

Контекст

Во время проектирования возник вопрос:

должен ли Recovery хранить информацию о последнем успешно восстановленном диапазоне?

Например:

last_trade_id

или

last_timestamp

Рассмотренные варианты

Stateful Recovery

Recovery самостоятельно сохраняет:

  • последний trade_id;
  • последний timestamp;
  • информацию о последнем запуске.

Преимущества:

локальная автономность.

Недостатки:

  • необходимость хранения состояния;
  • усложнение тестирования;
  • зависимость от Runtime;
  • необходимость восстановления собственного состояния после перезапуска.

Stateless Recovery

Recovery ничего не хранит.

Все необходимые параметры приходят внутри Recovery Request.

Преимущества:

  • простая архитектура;
  • отсутствие собственного состояния;
  • высокая тестируемость;
  • независимость от Runtime;
  • отсутствие необходимости синхронизации.

Недостатков не выявлено.


Принятое решение

Recovery является полностью Stateless-компонентом.

Любая информация, необходимая для восстановления, передаётся исключительно через:

TradeRecoveryRequest

Последствия

Recovery становится полностью детерминированным.

При одинаковом запросе он всегда выполняет одинаковые действия независимо от предыдущих запусков.


ADR-060.19-004

Recovery полностью независим от Runtime

Статус

Accepted

Контекст

После появления Recovery возник вопрос:

должен ли Recovery самостоятельно взаимодействовать с Runtime?

Например:

  • получать Runtime Events;
  • определять момент восстановления;
  • инициировать переподключение;
  • принимать решение о завершении Recovery.

Рассмотренные варианты

Вариант 1

Recovery становится частью Runtime.

Преимущества:

  • тесная интеграция;
  • меньше промежуточных компонентов.

Недостатки:

  • высокая связанность;
  • невозможность автономного тестирования;
  • сложность повторного использования;
  • нарушение принципа разделения ответственности.

Вариант 2

Recovery представляет собой независимый сервис.

Runtime лишь вызывает его.

Преимущества:

  • слабая связанность;
  • простое тестирование;
  • возможность автономного использования;
  • отсутствие циклических зависимостей.

Недостатков не обнаружено.


Принятое решение

Recovery ничего не знает о Runtime.

Recovery не знает:

  • почему произошло восстановление;
  • почему был потерян WebSocket;
  • сколько времени отсутствовало соединение;
  • требуется ли последующее переподключение.

Recovery выполняет только одну операцию:

TradeRecoveryRequest

↓

TradeRecoveryResult

Последствия

Runtime становится владельцем жизненного цикла Recovery.

Recovery остаётся полностью независимым компонентом Acquisition Layer.

Интеграция между ними выполняется исключительно через публичный API.


ADR-060.19-005

Controller и Normalizer разделены

Статус

Accepted

Контекст

Во время проектирования Recovery возник вопрос:

следует ли выполнять нормализацию последовательности непосредственно внутри Controller?


Рассмотренные варианты

Вариант 1

Controller самостоятельно выполняет:

  • получение истории;
  • нормализацию;
  • передачу в Consistency.

Преимущество:

меньшее количество компонентов.

Недостатки:

  • смешение обязанностей;
  • увеличение размера Controller;
  • снижение тестируемости;
  • невозможность повторного использования алгоритма нормализации.

Вариант 2

Выделить отдельный компонент:

TradeRecoveryNormalizer

Преимущества:

  • Single Responsibility;
  • независимое тестирование;
  • простая модификация алгоритма;
  • повторное использование.

Недостатков не выявлено.


Принятое решение

Recovery состоит минимум из двух независимых компонентов.

TradeRecoveryController

↓

TradeRecoveryNormalizer

Controller отвечает исключительно за оркестрацию.

Normalizer отвечает исключительно за порядок сделок.


Последствия

Каждый компонент имеет одну ответственность.

Изменение алгоритма нормализации не требует изменения Controller.


ADR-060.19-006

RecoveryResult является отдельной Domain Model

Статус

Accepted

Контекст

После завершения Recovery необходимо определить способ возврата результата.

Рассматривались различные модели.


Рассмотренные варианты

Вариант 1

Вернуть:

tuple[Trade]

Недостатки:

  • отсутствует информация о количестве пропущенных дублей;
  • невозможно отличить пустой диапазон от полного дублирования;
  • отсутствует описание завершённой операции.

Вариант 2

Вернуть:

list[Trade]

Недостатки аналогичны.

Кроме того, нарушается использование immutable-коллекций.


Вариант 3

Создать отдельную доменную модель результата.

Преимущества:

  • расширяемость;
  • единый контракт;
  • возможность добавления диагностической информации;
  • отсутствие изменения публичного API в будущем.

Принятое решение

Recovery возвращает исключительно:

TradeRecoveryResult

Этот объект описывает уже завершённую операцию восстановления.


Последствия

Публичный API Recovery остаётся стабильным.

В следующих Build возможно расширение модели результата без изменения сигнатур Controller.


ADR-060.19-007

Recovery не реализует Retry Policy

Статус

Accepted

Контекст

Во время проектирования возник вопрос:

следует ли Recovery автоматически повторять REST-запросы при временных ошибках сети?


Рассмотренные варианты

Вариант 1

Автоматический Retry внутри Recovery.

Преимущества:

  • уменьшение количества временных ошибок.

Недостатки:

  • появление внутреннего состояния;
  • усложнение Recovery;
  • необходимость настройки стратегий ожидания;
  • смешение обязанностей;
  • невозможность централизованного управления повторными попытками.

Вариант 2

Retry полностью принадлежит Runtime.

Recovery выполняет единственную попытку.

При ошибке возвращает управление вызывающему компоненту.

Преимущества:

  • простая архитектура;
  • единая стратегия повторных попыток;
  • отсутствие дублирования Retry между компонентами;
  • соответствие принципу Single Responsibility.

Недостатков не выявлено.


Принятое решение

Build 060.19 не реализует Retry Policy.

Recovery выполняет одну попытку получения исторических данных.

Любая транспортная ошибка немедленно распространяется вызывающему компоненту.


Последствия

Recovery остаётся простым и детерминированным.

Все стратегии:

  • Retry;
  • Exponential Backoff;
  • Circuit Breaker;
  • ограничение количества попыток;
  • таймеры ожидания;

будут реализованы на уровне Runtime в последующих Build.


Итоги приложения A

Настоящие ADR фиксируют фундаментальные архитектурные решения Build 060.19.

Они определяют не только текущее устройство Recovery, но и границы его дальнейшего развития.

Любая будущая модификация Recovery должна проверяться на соответствие данным решениям.

Если новое архитектурное решение противоречит одному из настоящих ADR, оно не может быть реализовано без подготовки нового Architecture Decision Record и пересмотра архитектурной спецификации Build.


Приложение B. Диаграммы последовательностей


Назначение приложения

Настоящее приложение фиксирует последовательности взаимодействия компонентов Recovery.

В отличие от основной части документа, описывающей архитектурные сущности и их ответственность, настоящее приложение показывает:

  • порядок вызова компонентов;
  • направление передачи данных;
  • точки возникновения исключений;
  • завершение успешных и неуспешных сценариев.

Все диаграммы являются частью архитектурного контракта Build 060.19.


Обозначения

Во всех диаграммах используются одинаковые обозначения.

↓

Синхронный вызов

←

Возврат результата

X

Возникновение исключения

✓

Успешное завершение этапа

Диаграмма 1

Полный успешный сценарий Recovery

                    TradeRecoveryController

                               │

                               ▼

                 DzengiTradesDocumentSource

                               │

                 REST Request (start/end)

                               │

                               ▼

                     REST Response (Document)

                               │

                               ▼

                          REST Parser

                               │

                               ▼

                      Value Validation

                               │

                               ▼

                          REST Mapper

                               │

                               ▼

                         tuple[Trade]

                               │

                               ▼

                  TradeRecoveryNormalizer

                               │

                               ▼

                     ASCENDING tuple

                               │

                               ▼

             TradeStreamConsistencyController

                               │

                   accept(trade #1)

                               │

                               ▼

                         accepted

                               │

                   accept(trade #2)

                               │

                               ▼

                         accepted

                               │

                             ...

                               │

                               ▼

                  TradeRecoveryResult

                               │

                               ▼

                        Recovery Done

Основные свойства сценария

В данном сценарии:

  • REST завершился успешно;
  • Parser завершился успешно;
  • Validation завершилась успешно;
  • Mapper завершился успешно;
  • Normalizer завершился успешно;
  • все сделки прошли Stream Consistency.

Recovery завершается формированием результата.


Диаграмма 2

Восстановление пустого диапазона

TradeRecoveryController

        │

        ▼

REST Source

        │

        ▼

REST Response

        │

        ▼

()

        │

        ▼

TradeRecoveryNormalizer

        │

        ▼

()

        │

        ▼

TradeRecoveryResult

Recovered Trades = 0

Skipped Duplicates = 0

Основные свойства сценария

Пустой диапазон не считается ошибкой.

Recovery завершается успешно.

TradeStreamConsistencyController не вызывается.


Диаграмма 3

Полное дублирование

REST

↓

tuple[Trade]

↓

Normalizer

↓

Trade #1

↓

Consistency

↓

None

↓

Trade #2

↓

Consistency

↓

None

↓

Trade #3

↓

Consistency

↓

None

↓

TradeRecoveryResult

Recovered = 0

Duplicates = 3

Основные свойства сценария

Все сделки уже присутствуют в Canonical Trade Stream.

Recovery:

  • не генерирует ошибку;
  • не публикует сделки;
  • успешно завершается.

Диаграмма 4

Частичное восстановление

REST

↓

Trade #100

↓

Consistency

↓

None

────────────

Trade #105

↓

Consistency

↓

Trade

────────────

Trade #121

↓

Consistency

↓

Trade

────────────

Trade #140

↓

Consistency

↓

Trade

────────────

TradeRecoveryResult

Recovered = 3

Duplicates = 1

Основные свойства сценария

Это основной рабочий сценарий Recovery.

Часть сделок уже существует.

Остальные успешно публикуются.

Recovery завершается успешно.


Диаграмма 5

REST возвращает DESCENDING последовательность

REST

↓

140

↓

121

↓

105

↓

100

↓

TradeRecoveryNormalizer

↓

100

↓

105

↓

121

↓

140

↓

TradeStreamConsistencyController

Основные свойства сценария

Нормализация выполняется полностью внутри RecoveryNormalizer.

TradeStreamConsistencyController получает уже канонический поток.

Recovery никогда не передаёт Controller последовательность в обратном порядке.


Диаграмма 6

REST возвращает ASCENDING последовательность

REST

↓

100

↓

105

↓

121

↓

140

↓

TradeRecoveryNormalizer

↓

100

↓

105

↓

121

↓

140

↓

TradeStreamConsistencyController

Основные свойства сценария

Несмотря на то, что текущий контракт REST API предусматривает выдачу сделок в порядке DESCENDING, RecoveryNormalizer проектируется универсальным.

Если в будущем REST API начнёт возвращать последовательность уже в каноническом порядке, Recovery не потребует изменений архитектуры.

Normalizer обнаружит, что последовательность уже соответствует Canonical Trade Stream, и вернёт её без изменений.


Диаграмма 7

REST возвращает смешанный порядок

REST

↓

100

↓

140

↓

121

↓

180

↓

TradeRecoveryNormalizer

↓

X

TradeRecoveryNormalizationError

Основные свойства сценария

Смешанный порядок рассматривается как нарушение транспортного контракта.

Recovery не предпринимает попыток:

  • отсортировать последовательность;
  • определить правильный порядок;
  • восстановить повреждённые данные.

Работа немедленно прекращается.

Ни одна сделка не передаётся в TradeStreamConsistencyController.


Диаграмма 8

Ошибка Parser

TradeRecoveryController

↓

REST Source

↓

REST Parser

↓

X

ParserError

Основные свойства сценария

Recovery не вмешивается в работу Parser.

Parser остаётся единственным владельцем транспортной валидации структуры документа.

Recovery лишь распространяет возникшее исключение вызывающему компоненту.


Диаграмма 9

Ошибка Value Validation

REST

↓

Parser

↓

Value Validation

↓

X

ValidationError

Основные свойства сценария

Если хотя бы одна сделка не проходит существующую систему проверки значений,

Recovery немедленно прекращает выполнение.

TradeRecoveryResult не создаётся.


Диаграмма 10

Ошибка Mapper

REST

↓

Parser

↓

Validation

↓

Mapper

↓

X

MappingError

Основные свойства сценария

Recovery никогда не работает с частично сформированными объектами.

Если Mapper не смог построить корректный объект Trade,

дальнейшая обработка невозможна.


Диаграмма 11

Ошибка Stream Consistency

REST

↓

Parser

↓

Validation

↓

Mapper

↓

TradeRecoveryNormalizer

↓

TradeStreamConsistencyController

↓

Trade #100

↓

accepted

↓

Trade #105

↓

X

TradeOrderingError

Основные свойства сценария

Recovery не перехватывает исключения Consistency Layer.

После возникновения ошибки:

  • дальнейшая обработка прекращается;
  • TradeRecoveryResult не создаётся;
  • исключение распространяется вверх.

Таким образом владельцем логики проверки потока остаётся исключительно Build 060.18.


Диаграмма 12

Полный жизненный цикл Recovery

TradeRecoveryRequest

        │

        ▼

TradeRecoveryController

        │

        ▼

DzengiTradesDocumentSource

        │

        ▼

REST Document

        │

        ▼

Parser

        │

        ▼

Value Validation

        │

        ▼

Mapper

        │

        ▼

tuple[Trade]

        │

        ▼

TradeRecoveryNormalizer

        │

        ▼

Normalized tuple[Trade]

        │

        ▼

TradeStreamConsistencyController

        │

        ▼

TradeRecoveryResult

        │

        ▼

Caller

Архитектурные выводы

Все приведённые диаграммы подтверждают несколько фундаментальных принципов Build 060.19.


Принцип №1

Recovery никогда не работает с транспортными структурами после завершения этапа Mapping.

Начиная с момента формирования объекта Trade, Recovery использует исключительно каноническую доменную модель.


Принцип №2

Recovery не изменяет существующий Acquisition Pipeline.

Он расширяет его, добавляя дополнительный этап между REST Adapter и TradeStreamConsistencyController.


Принцип №3

Recovery не принимает доменных решений.

Все решения относительно:

  • корректности сделки;
  • порядка сделок;
  • дубликатов;
  • конфликтов;

по-прежнему принадлежат TradeStreamConsistencyController.


Принцип №4

Recovery является полностью линейным Pipeline.

Каждый компонент вызывается строго один раз.

Обратные переходы отсутствуют.

Циклические зависимости отсутствуют.


Принцип №5

Любое исключение немедленно завершает выполнение Recovery.

Частично завершённое восстановление не считается успешным.

TradeRecoveryResult формируется только после успешного прохождения всех этапов Pipeline.


Итоги приложения B

Последовательности взаимодействия, приведённые в настоящем приложении, являются нормативным описанием поведения Recovery.

Любая реализация Build 060.19 должна соответствовать данным диаграммам.

Изменение порядка взаимодействия компонентов, появление дополнительных зависимостей или перенос ответственности между компонентами допускаются только после подготовки нового архитектурного решения (ADR) и внесения соответствующих изменений в архитектурную спецификацию.


Приложение C. Контракты публичного API


Назначение приложения

Настоящее приложение фиксирует официальный публичный API подсистемы Trade Recovery.

Основной документ описывает архитектуру Recovery.

Настоящее приложение определяет:

  • публичные классы;
  • публичные методы;
  • входные параметры;
  • возвращаемые значения;
  • предусловия;
  • постусловия;
  • архитектурные инварианты.

Любой внешний компонент системы имеет право взаимодействовать с Recovery исключительно через описанные здесь контракты.

Все остальные классы Recovery считаются внутренней реализацией Build 060.19.


Общие принципы публичного API

Recovery строится на следующих принципах.


Минимальность

Публичный API должен содержать только действительно необходимые точки входа.

Recovery не предоставляет вспомогательных методов.


Детерминированность

При одинаковых входных данных любой публичный метод обязан возвращать одинаковый результат.


Отсутствие побочных эффектов

Ни один публичный метод Recovery не изменяет:

  • состояние Runtime;
  • состояние WebSocket;
  • состояние подписок.

Recovery воздействует исключительно на Canonical Trade Stream через существующий TradeStreamConsistencyController.


Иммутабельность

Все входные модели Recovery являются неизменяемыми.

Recovery никогда не модифицирует переданные объекты.


Публичный класс

TradeRecoveryController


Назначение

TradeRecoveryController является единственной точкой входа в Recovery Pipeline.

Никакой другой компонент Recovery не должен вызываться напрямую внешними подсистемами.


Ответственность

Контроллер отвечает исключительно за выполнение полного цикла восстановления.

Он:

  • получает Recovery Request;
  • инициирует получение исторических сделок;
  • выполняет нормализацию;
  • передаёт сделки в Stream Consistency;
  • формирует итоговый результат.

Публичный контракт

recover(
    request: TradeRecoveryRequest,
) -> TradeRecoveryResult

Входные параметры

TradeRecoveryRequest

Запрос восстановления.


Возвращаемое значение

TradeRecoveryResult

Описание завершённой операции восстановления.


Возможные исключения

Контроллер может распространять:

TradeRecoveryRequestError

TradeRecoveryNormalizationError

TradeConsistencyError

TradeOrderingError

а также исключения существующих компонентов:

  • REST;
  • Parser;
  • Mapper;
  • Value Validation.

Предусловия

Перед вызовом метода должны выполняться следующие условия.

  • Recovery Request успешно создан.
  • Все обязательные поля заполнены.
  • Диапазон времени корректен.
  • RecoveryController полностью сконфигурирован.

Постусловия

При успешном завершении гарантируется:

  • все сделки обработаны;
  • все сделки прошли через TradeStreamConsistencyController;
  • сформирован TradeRecoveryResult.

Побочные эффекты

Единственным допустимым побочным эффектом является публикация новых сделок в существующий Canonical Trade Stream.

Других изменений состояния системы Controller не выполняет.


Публичный класс

TradeRecoveryRequest


Назначение

TradeRecoveryRequest описывает один запрос восстановления.

После создания объект никогда не изменяется.


Обязательные поля

symbol

start_time

end_time

Необязательные поля

limit

Инварианты

Всегда выполняются условия.

start_time < end_time

limit ∈ [1;1000]

если limit указан.


Запрос относится только к одному символу.


Объект является immutable.


Предусловия создания

Все поля проходят базовую проверку корректности.


Постусловия создания

После создания Recovery Request считается валидным и может использоваться RecoveryController.


Публичный класс

TradeRecoveryResult


Назначение

TradeRecoveryResult описывает итог выполнения Recovery.

Он создаётся исключительно после успешного завершения Recovery Pipeline.


Основные поля

Recovered Trades

Количество новых опубликованных сделок.


Skipped Duplicates

Количество корректных дублей.


Инварианты

Количество восстановленных сделок всегда больше либо равно нулю.

Количество пропущенных дублей всегда больше либо равно нулю.

Все значения являются согласованными относительно завершённого Recovery Pipeline.


Предусловия создания

Recovery успешно завершён.


Постусловия

Result полностью описывает завершённую операцию.

После создания объект не изменяется.


Внутренний публичный компонент

TradeRecoveryNormalizer


Назначение

Несмотря на то, что Normalizer используется только Controller, его контракт фиксируется отдельно.

Это позволяет независимо тестировать данный компонент.


Публичный контракт

normalize(
    trades: tuple[Trade, ...],
) -> tuple[Trade, ...]

Входные параметры

Последовательность Canonical Trade.


Возвращаемое значение

Та же последовательность,

но гарантированно приведённая к каноническому порядку.


Возможные исключения

TradeRecoveryNormalizationError

Предусловия

Все элементы коллекции являются корректными объектами Trade.


Постусловия

Количество элементов сохраняется.

Ни один объект Trade не изменяется.

Допускается изменение исключительно порядка следования элементов.


Контракт взаимодействия компонентов

Настоящий раздел определяет допустимые направления вызовов между компонентами Recovery.


Допустимые зависимости

TradeRecoveryController имеет право зависеть от:

TradeRecoveryRequest

DzengiTradesDocumentSource

REST Adapter

TradeRecoveryNormalizer

TradeStreamConsistencyController

TradeRecoveryResult

Других зависимостей Controller иметь не должен.


Недопустимые зависимости

TradeRecoveryController не должен зависеть от:

  • Runtime;
  • Reconnect;
  • Subscription Manager;
  • WebSocket Transport;
  • Event Bus;
  • Scheduler;
  • Timer;
  • Retry Engine.

Появление подобных зависимостей рассматривается как нарушение архитектуры Build 060.19.


Контракт TradeRecoveryNormalizer

Normalizer представляет собой полностью детерминированную функцию.

Он зависит исключительно от:

tuple[Trade]

Никакие другие объекты системы ему не требуются.


Запрещённые зависимости Normalizer

Normalizer никогда не взаимодействует с:

  • REST;
  • Runtime;
  • WebSocket;
  • Controller;
  • Repository;
  • Cache;
  • Configuration;
  • Logger.

Это обеспечивает возможность полностью изолированного тестирования.


Контракт взаимодействия с REST Pipeline

Recovery не обращается к REST API напрямую.

Единственная допустимая точка взаимодействия —

существующий транспортный источник.

Архитектурная схема выглядит следующим образом.

TradeRecoveryController

↓

DzengiTradesDocumentSource

↓

REST API

Recovery не формирует HTTP-запросы самостоятельно.

Recovery не знает формат REST-документа.

Recovery не знает структуру JSON.


Контракт взаимодействия с Parser

Recovery никогда не вызывает Parser напрямую.

Вызов Parser осуществляется исключительно существующим REST Adapter.

Таким образом сохраняется единый Pipeline преобразования транспортных данных.


Контракт взаимодействия с Mapper

Recovery никогда не создаёт объект Trade самостоятельно.

Появление Canonical Trade возможно исключительно после успешного завершения Mapper.

Recovery использует только уже готовые доменные объекты.


Контракт взаимодействия со Stream Consistency

TradeRecoveryController взаимодействует с TradeStreamConsistencyController исключительно через его публичный API.

Recovery запрещается:

  • изменять внутреннее состояние Controller;
  • обращаться к внутренним коллекциям;
  • использовать приватные методы;
  • обходить механизм accept().

Таким образом сохраняется строгая инкапсуляция Consistency Layer.


Контракт взаимодействия с Runtime

В Build 060.19 Runtime не является участником Recovery Pipeline.

Единственная допустимая форма взаимодействия:

Runtime

↓

TradeRecoveryController

↓

TradeRecoveryResult

Runtime рассматривается исключительно как вызывающая сторона.

Recovery не знает о его внутреннем устройстве.


Контракт расширяемости

Recovery проектируется с учётом последующего развития системы.

Следующие компоненты могут быть добавлены без изменения существующего публичного API.

Например:

Retry Policy

Metrics

Tracing

Telemetry

Performance Monitor

Recovery Statistics

Все перечисленные расширения должны использовать композицию.

Изменение публичного API Recovery не допускается.


Контракт потокобезопасности

Build 060.19 не накладывает требований на многопоточную обработку.

Recovery выполняет один запрос восстановления за один вызов.

Параллельное выполнение нескольких Recovery Pipeline не входит в Scope настоящего Build.

Вопрос конкурентного восстановления нескольких символов рассматривается в последующих Build.


Контракт детерминированности

Recovery обязан удовлетворять следующему свойству.

При одинаковых:

  • Recovery Request;
  • ответе REST;
  • состоянии TradeStreamConsistencyController;

результат выполнения обязан быть идентичным.

Данное свойство считается обязательным архитектурным инвариантом публичного API.


Контракт обратной совместимости

После утверждения настоящего документа публичный API Recovery считается стабильным.

Изменение следующих сущностей требует подготовки нового архитектурного решения (ADR):

  • сигнатуры recover();
  • структуры TradeRecoveryRequest;
  • структуры TradeRecoveryResult;
  • публичного контракта TradeRecoveryNormalizer.

Допускается только расширение функциональности без нарушения существующих контрактов.


Матрица ответственности публичных компонентов

Компонент Основная ответственность Не отвечает за
TradeRecoveryController Оркестрация полного процесса Recovery Runtime, Retry, Deduplication, Ordering
TradeRecoveryRequest Описание параметров восстановления Вычисление диапазона восстановления
TradeRecoveryNormalizer Нормализация порядка сделок REST, Validation, Deduplication
TradeRecoveryResult Представление результата Recovery Хранение внутреннего состояния Recovery

Матрица владения бизнес-правилами

Бизнес-правило Владелец
Получение исторических данных DzengiTradesDocumentSource
Разбор REST-документа REST Parser
Проверка корректности значений Value Validation
Построение Canonical Trade REST Mapper
Нормализация порядка TradeRecoveryNormalizer
Проверка порядка сделок TradeStreamConsistencyController
Дедупликация TradeStreamConsistencyController
Формирование результата Recovery TradeRecoveryController

Итоги приложения C

Настоящее приложение фиксирует публичный API Build 060.19 и определяет архитектурные границы взаимодействия Recovery с остальными подсистемами Dzentra.

После утверждения настоящего приложения любые новые компоненты должны интегрироваться с Recovery исключительно через описанные контракты.

Это обеспечивает:

  • минимальную связанность подсистем;
  • стабильность публичного API;
  • возможность безопасного расширения функциональности в последующих Build без нарушения существующей архитектуры.

Приложение D. Сценарии выполнения Recovery


Назначение приложения

Настоящее приложение содержит нормативные сценарии выполнения Build 060.19.

Если приложение B описывает последовательности взаимодействия компонентов, то настоящее приложение описывает ожидаемое поведение Recovery при различных входных данных.

Каждый сценарий фиксирует:

  • исходное состояние системы;
  • последовательность действий;
  • ожидаемый результат;
  • архитектурные инварианты.

Данные сценарии являются эталоном для разработки интеграционных тестов Build 060.19.


Scenario 1

Полное успешное восстановление

Исходное состояние

В системе существует активный Canonical Trade Stream.

Recovery получает корректный диапазон времени.

REST API возвращает четыре сделки.

140

121

105

100

Последовательность выполнения

  1. RecoveryController получает TradeRecoveryRequest.

  2. Выполняется REST-запрос.

  3. REST Adapter строит четыре объекта Trade.

  4. TradeRecoveryNormalizer определяет обратный порядок.

  5. Последовательность нормализуется.

100

105

121

140
  1. Каждая сделка последовательно передаётся в TradeStreamConsistencyController.

  2. Все сделки успешно принимаются.

  3. Формируется TradeRecoveryResult.


Ожидаемый результат

Recovered Trades = 4

Skipped Duplicates = 0

Recovery завершается успешно.


Проверяемые инварианты

  • нормализация выполнена;
  • все сделки прошли через Stream Consistency;
  • ни одна сделка не была изменена;
  • результат сформирован.

Scenario 2

В указанном диапазоне отсутствуют сделки

Исходное состояние

REST возвращает пустую последовательность.

()

Последовательность выполнения

  1. RecoveryController выполняет REST-запрос.

  2. Parser успешно обрабатывает ответ.

  3. Mapper формирует пустую коллекцию.

  4. Normalizer получает пустую последовательность.

  5. Stream Consistency не вызывается.

  6. Формируется TradeRecoveryResult.


Ожидаемый результат

Recovered Trades = 0

Skipped Duplicates = 0

Recovery завершается успешно.


Проверяемые инварианты

Пустой диапазон не считается ошибкой.


Scenario 3

Все сделки являются корректными дубликатами

Исходное состояние

REST возвращает:

100

105

121

Все три сделки уже присутствуют в Canonical Trade Stream.


Последовательность выполнения

Каждая сделка последовательно передаётся Controller.

Controller возвращает:

None

для каждой сделки.

После завершения обработки создаётся TradeRecoveryResult.


Ожидаемый результат

Recovered Trades = 0

Skipped Duplicates = 3

Проверяемые инварианты

Recovery не считает корректные дубликаты ошибкой.


Scenario 4

Частичное восстановление диапазона

Исходное состояние

REST возвращает:

140

121

105

100

Controller определяет:

100 -> duplicate

105 -> accepted

121 -> accepted

140 -> accepted

Последовательность выполнения

Recovery нормализует поток.

Каждая сделка проходит через Controller.

Дубликат исключается.

Три новые сделки публикуются.


Ожидаемый результат

Recovered Trades = 3

Skipped Duplicates = 1

Проверяемые инварианты

Recovery не прекращает выполнение при обнаружении корректного дубликата.


Scenario 5

REST возвращает ASCENDING последовательность

Исходное состояние

REST возвращает:

100

105

121

140

Последовательность выполнения

Normalizer анализирует направление.

Последовательность уже соответствует Canonical Trade Stream.

Изменения не выполняются.


Ожидаемый результат

Все сделки передаются Controller в первоначальном порядке.


Проверяемые инварианты

Normalizer не выполняет лишних преобразований.

Последовательность сохраняется без изменений.


Scenario 6

REST возвращает DESCENDING последовательность

Исходное состояние

REST возвращает:

140

121

105

100

Последовательность выполнения

Normalizer определяет обратное направление.

Выполняется нормализация.

Полученный поток передаётся Controller.


Ожидаемый результат

Controller получает:

100

105

121

140

Проверяемые инварианты

TradeStreamConsistencyController никогда не получает последовательность в обратном порядке.


Scenario 7

REST возвращает смешанный порядок

Исходное состояние

REST возвращает последовательность:

100

150

121

180

Последовательность не является ни возрастающей, ни убывающей.


Последовательность выполнения

  1. RecoveryController получает результат REST Pipeline.

  2. Последовательность передаётся в TradeRecoveryNormalizer.

  3. Normalizer анализирует направление последовательности.

  4. Обнаруживается нарушение архитектурного инварианта.

  5. Генерируется:

TradeRecoveryNormalizationError
  1. Выполнение Recovery прекращается.

Ожидаемый результат

Recovery завершается ошибкой.

TradeStreamConsistencyController не вызывается.

TradeRecoveryResult не создаётся.


Проверяемые инварианты

  • смешанная последовательность не допускается;
  • Recovery не пытается исправить поток;
  • никакие сделки не публикуются.

Scenario 8

Ошибка Parser

Исходное состояние

REST возвращает документ с нарушением транспортного контракта.

Parser обнаруживает ошибку структуры.


Последовательность выполнения

  1. RecoveryController инициирует получение истории.

  2. REST Adapter вызывает Parser.

  3. Parser генерирует исключение.

  4. Recovery прекращает выполнение.


Ожидаемый результат

TradeRecoveryResult отсутствует.

Исключение распространяется вызывающему компоненту.


Проверяемые инварианты

Recovery не вмешивается в транспортную обработку данных.

Parser остаётся единственным владельцем логики разбора REST-документа.


Scenario 9

Ошибка Value Validation

Исходное состояние

Parser успешно завершил обработку.

Во время проверки значений обнаружено нарушение одного из инвариантов Canonical Trade.


Последовательность выполнения

  1. Parser завершает работу.

  2. Запускается Value Validation.

  3. Validation обнаруживает некорректное значение.

  4. Генерируется исключение Validation.

  5. Recovery прекращает выполнение.


Ожидаемый результат

TradeRecoveryResult отсутствует.

Никакие сделки не публикуются.


Проверяемые инварианты

Recovery никогда не работает с объектами, не прошедшими Validation.


Scenario 10

Ошибка Mapper

Исходное состояние

Parser и Validation успешно завершены.

Mapper не способен сформировать объект Canonical Trade.


Последовательность выполнения

  1. Recovery получает транспортные данные.

  2. Mapper генерирует исключение.

  3. Recovery немедленно завершает выполнение.


Ожидаемый результат

TradeRecoveryResult отсутствует.

TradeRecoveryNormalizer не вызывается.


Проверяемые инварианты

Recovery никогда не получает частично сформированные объекты Trade.


Scenario 11

Ошибка TradeStreamConsistencyController

Исходное состояние

REST Pipeline завершился успешно.

Normalizer успешно выполнил нормализацию.

Во время обработки одной из сделок Controller обнаруживает нарушение инвариантов потока.


Последовательность выполнения

  1. Recovery начинает последовательную передачу сделок.

  2. Первые сделки успешно принимаются.

  3. Для очередной сделки Controller генерирует:

TradeOrderingError

или

TradeConsistencyError
  1. Recovery прекращает выполнение.

Ожидаемый результат

TradeRecoveryResult отсутствует.

Исключение распространяется вызывающему компоненту.


Проверяемые инварианты

Recovery не скрывает ошибки Stream Consistency.

Recovery не пытается продолжить обработку.


Scenario 12

Ошибка получения исторических данных

Исходное состояние

Во время обращения к REST API возникает транспортная ошибка.

Например:

  • Timeout;
  • Network Error;
  • HTTP Error.

Последовательность выполнения

  1. RecoveryController вызывает DzengiTradesDocumentSource.

  2. Источник генерирует исключение.

  3. Recovery немедленно завершает выполнение.


Ожидаемый результат

TradeRecoveryResult отсутствует.

Повторный запрос не выполняется.


Проверяемые инварианты

Retry Policy отсутствует.

Recovery выполняет только одну попытку получения истории.


Матрица покрытия сценариев

Сценарий REST Normalizer Stream Consistency Result
Полное восстановление
Пустой диапазон
Полные дубликаты
Частичное восстановление
ASCENDING
DESCENDING
Смешанный порядок
Ошибка Parser
Ошибка Validation
Ошибка Mapper
Ошибка Consistency
Ошибка REST

Использование сценариев

Настоящие сценарии являются нормативной моделью поведения Recovery.

Они используются как основа для:

  • Unit-тестирования;
  • Integration-тестирования;
  • проверки архитектурных инвариантов;
  • регрессионного тестирования последующих Build серии 060.

Каждый новый сценарий, добавляемый в Recovery, должен сопровождаться соответствующим дополнением настоящего приложения.


Итоги приложения D

Настоящее приложение описывает эталонное поведение подсистемы Recovery в наиболее важных эксплуатационных ситуациях.

Любая реализация Build 060.19 должна демонстрировать поведение, полностью соответствующее данным сценариям.

Отклонение от описанных сценариев допускается только после внесения изменений в архитектурную спецификацию и утверждения нового Architecture Decision Record (ADR).


Приложение E. План реализации Build 060.19


Назначение приложения

Настоящее приложение определяет официальный план реализации Build 060.19.

Основной документ фиксирует архитектуру Recovery.

Настоящее приложение определяет последовательность разработки, которая обеспечивает:

  • минимальные изменения существующего кода;
  • отсутствие нарушения архитектуры предыдущих Build;
  • возможность тестирования каждого этапа отдельно;
  • безопасную интеграцию Recovery в существующую систему.

Все этапы должны выполняться строго последовательно.

Переход к следующему этапу допускается только после полного завершения предыдущего.


Общая последовательность реализации

Этап 1

Recovery Domain Models

        │

        ▼

Этап 2

Recovery Normalizer

        │

        ▼

Этап 3

Recovery Controller

        │

        ▼

Этап 4

Unit Tests

        │

        ▼

Этап 5

Integration Tests

        │

        ▼

Build Complete

Этап 1

Recovery Domain Models


Цель

Создать все доменные модели Build 060.19.

На данном этапе не реализуется никакая бизнес-логика.

Создаются исключительно структуры данных.


Новые сущности

Минимальный состав моделей:

TradeRecoveryRequest

TradeRecoveryResult

TradeRecoveryError

TradeRecoveryRequestError

TradeRecoveryNormalizationError

Требования

Все модели должны быть:

  • immutable;
  • полностью типизированными;
  • независимыми от Runtime;
  • независимыми от REST.

Не допускается

На данном этапе запрещается:

  • выполнять Recovery;
  • обращаться к REST;
  • создавать Controller;
  • изменять существующий Pipeline.

Критерии завершения

Этап считается завершённым, если:

  • все модели реализованы;
  • модели проходят статическую проверку типов;
  • модели покрыты Unit-тестами.

Этап 2

TradeRecoveryNormalizer


Цель

Реализовать алгоритм нормализации порядка сделок.


Функциональность

Normalizer обязан:

  • принимать tuple[Trade];
  • определять направление последовательности;
  • возвращать ASCENDING поток;
  • обнаруживать смешанный порядок;
  • генерировать TradeRecoveryNormalizationError.

Не допускается

Normalizer не должен:

  • обращаться к REST;
  • использовать Runtime;
  • использовать Controller;
  • выполнять дедупликацию;
  • изменять Trade.

Минимальный набор тестов

Проверяются сценарии:

  • пустой поток;
  • одна сделка;
  • ASCENDING;
  • DESCENDING;
  • смешанный порядок.

Критерии завершения

Этап считается завершённым после успешного прохождения всех Unit-тестов Normalizer.


Этап 3

TradeRecoveryController


Цель

Реализовать центральный компонент Recovery.


Функциональность

Controller обязан:

  • принять Recovery Request;
  • вызвать существующий REST Pipeline;
  • вызвать Normalizer;
  • передать сделки в TradeStreamConsistencyController;
  • сформировать TradeRecoveryResult.

Используемые компоненты

Controller использует только существующие компоненты системы.

Никаких новых транспортных механизмов не создаётся.


Не допускается

Controller не должен:

  • вычислять диапазон времени;
  • выполнять Retry;
  • работать с Runtime;
  • работать с WebSocket;
  • выполнять дедупликацию.

Критерии завершения

Controller успешно проходит все Unit-тесты.


Этап 4

Unit Testing


Цель

Проверить корректность каждого компонента Recovery изолированно.


Проверяемые компоненты

TradeRecoveryRequest

TradeRecoveryResult

TradeRecoveryNormalizer

TradeRecoveryController

Требования

Unit-тесты не должны обращаться:

  • к REST API;
  • к Runtime;
  • к WebSocket.

Все внешние зависимости заменяются Mock-объектами.


Критерии завершения

Все Unit-тесты успешно проходят.

Покрываются все архитектурные сценарии, определённые приложением D.


Этап 5

Integration Testing


Цель

Проверить корректность работы Recovery как единой подсистемы Acquisition Layer.

На данном этапе тестируются взаимодействия между уже существующими компонентами системы.

Проверяется не отдельная логика компонентов, а корректность всей цепочки обработки исторических сделок.


Интегрируемые компоненты

В интеграционных тестах участвуют:

DzengiTradesDocumentSource

REST Adapter

TradeRecoveryNormalizer

TradeStreamConsistencyController

TradeRecoveryResult

Проверяемые сценарии

Минимальный набор интеграционных тестов включает:

Сценарий 1

Полное успешное восстановление.


Сценарий 2

Пустой диапазон.


Сценарий 3

Полное дублирование.


Сценарий 4

Частичное восстановление.


Сценарий 5

REST возвращает DESCENDING поток.


Сценарий 6

REST возвращает ASCENDING поток.


Сценарий 7

REST возвращает смешанную последовательность.


Сценарий 8

TradeStreamConsistencyController обнаруживает ошибку порядка.


Сценарий 9

REST Pipeline завершается исключением.


Критерии завершения

Этап считается завершённым при выполнении следующих условий.

  • Все интеграционные тесты успешно проходят.
  • Все архитектурные инварианты подтверждены.
  • Ни один существующий Build не требует изменения своей архитектуры.
  • Recovery полностью совместим с существующим Canonical Trade Stream.

Проверка соответствия архитектуре

После завершения реализации выполняется архитектурная проверка Build.

Проверяются следующие требования.


Проверка №1

Recovery использует существующий REST Pipeline.


Проверка №2

Recovery использует существующий TradeStreamConsistencyController.


Проверка №3

Recovery не содержит собственной реализации Deduplication.


Проверка №4

Recovery не содержит собственной реализации Ordering.


Проверка №5

Recovery не зависит от Runtime.


Проверка №6

Recovery не зависит от WebSocket Transport.


Проверка №7

Recovery не изменяет Canonical Trade Model.


Проверка №8

Recovery не изменяет Parser.


Проверка №9

Recovery не изменяет Mapper.


Проверка №10

Recovery не изменяет Value Validation.


Проверка №11

Recovery не нарушает архитектурные решения Build 060.18.


Definition of Done (DoD)

Build 060.19 считается завершённым только при выполнении всех перечисленных условий.


Архитектура

  • Архитектура полностью соответствует настоящему документу.
  • Все ADR соблюдены.
  • Все архитектурные инварианты сохранены.

Код

  • Реализованы все модели Recovery.
  • Реализован TradeRecoveryNormalizer.
  • Реализован TradeRecoveryController.
  • Не изменены существующие публичные контракты предыдущих Build без отдельного ADR.

Качество

  • Код проходит статическую проверку типов.
  • Код соответствует принятому стилю проекта.
  • Новые сущности имеют уникальные имена файлов.
  • Не допущено дублирование существующей функциональности.

Тестирование

  • Все Unit-тесты проходят успешно.
  • Все Integration-тесты проходят успешно.
  • Покрыты все сценарии, описанные в Приложении D.

Документация

Подготовлены и согласованы:

  • build_060_19_architecture.md;
  • build_060_19.md (Engineering Report);
  • комментарии в коде (при необходимости);
  • обновлены внутренние ссылки на связанные Build.

Зависимости от последующих Build

Build 060.19 сознательно ограничивает собственную область ответственности.

Следующие задачи не входят в его Scope и будут реализованы позже.


Build 060.20

Trade Recovery Registry.

Хранение и управление состоянием восстановления для нескольких символов.


Build 060.21

Recovery Acquisition Protocol.

Определение протокола взаимодействия Runtime и Recovery.


Build 060.22

Recovery Acquisition Service.

Высокоуровневый сервис восстановления, объединяющий Registry и Controller.


Build 060.23

Runtime Integration.

Интеграция Recovery с жизненным циклом Runtime.


Build 060.24

Reconnect Integration.

Автоматический запуск Recovery после восстановления WebSocket-соединения.


Build 060.25

Полная интеграция Trades Feed.

Объединение потоковых и исторических сделок в единый производственный Pipeline.


Build 060.26

Финальная документация, регрессионный аудит и подтверждение соответствия всей серии Build 060.


Заключение

Build 060.19 завершает следующий важный этап развития подсистемы Trades Feed.

После его реализации система впервые получает архитектурно корректный механизм восстановления исторических сделок, построенный на уже существующих компонентах без нарушения их ответственности.

Recovery не создаёт альтернативную модель обработки данных и не дублирует ранее реализованную функциональность. Вместо этого он объединяет:

  • существующий REST Pipeline;
  • существующую Canonical Trade Model;
  • существующий TradeStreamConsistencyController;

в единый механизм восстановления истории.

Это решение обеспечивает:

  • единый источник истины для проверки согласованности потока;
  • минимальную связанность подсистем;
  • высокую тестируемость;
  • возможность безопасного масштабирования архитектуры в следующих Build.

Настоящий документ завершает архитектурное проектирование Build 060.19 и является нормативной спецификацией, на основании которой должна выполняться реализация.


Приложение F. Архитектурная совместимость и дальнейшее развитие Build 060.19


Назначение приложения

Настоящее приложение определяет место Build 060.19 в архитектуре Dzentra серии Build 060.

Оно фиксирует:

  • архитектурные зависимости;
  • совместимость с предыдущими Build;
  • влияние на существующие подсистемы;
  • направления дальнейшего развития Recovery;
  • гарантии обратной совместимости.

Данное приложение служит контрольной точкой эволюции архитектуры Trades Feed.


Место Build 060.19 в серии Build 060

Build 060.19 является первым Build серии, реализующим механизм восстановления исторических сделок (Trade Recovery).

Recovery не создаёт новую модель обработки данных.

Recovery использует существующие архитектурные компоненты и объединяет их в единый процесс восстановления истории.

Таким образом Build 060.19 является интеграционным Build, а не Build, изменяющим фундаментальную архитектуру Trades Feed.


Архитектурные зависимости

Build 060.19 непосредственно зависит только от следующих компонентов.

Canonical Trade Model

REST Trade Pipeline

TradeStreamConsistencyController

Других обязательных архитектурных зависимостей Recovery не имеет.


Совместимость с предыдущими Build

Build 060.19 полностью совместим со всеми ранее утверждёнными Build серии 060.

Build Назначение Совместимость
Build 060.1 Canonical Trade Model Полная
Build 060.2060.8 Базовые доменные модели и инфраструктура Полная
Build 060.9 REST Trade Pipeline Полная
Build 060.10060.16 Парсинг, Validation, Mapping Полная
Build 060.17 Stream Consistency Полная
Build 060.18 REST Pipeline Integration Полная

Ни один из перечисленных Build не требует модификации для внедрения Recovery.


Build, которые Recovery не изменяет

Build 060.19 сознательно не изменяет архитектуру следующих компонентов.

  • Canonical Trade;
  • REST Parser;
  • Value Validation;
  • REST Mapper;
  • REST Adapter;
  • TradeStreamConsistencyController;
  • Transport Layer;
  • Exchange Integration;
  • Runtime.

Все перечисленные подсистемы используются без изменения их ответственности.


Build, использующие Recovery

После завершения Build 060.19 механизм Recovery становится частью архитектурного фундамента следующих Build.

Build Использование Recovery
Build 060.20 Registry хранения состояния Recovery
Build 060.21 Acquisition Protocol
Build 060.22 Acquisition Service
Build 060.23 Runtime Integration
Build 060.24 Reconnect Integration
Build 060.25 Полная интеграция Trades Feed
Build 060.26 Финальная документация и аудит

Таким образом Recovery становится базовым компонентом всей последующей серии Build.


Архитектурные гарантии

Настоящий Build гарантирует следующие свойства.

Единственный источник проверки согласованности

Проверка порядка сделок выполняется исключительно через:

TradeStreamConsistencyController

Recovery не реализует собственную проверку согласованности.


Единственная Canonical Model

Во всей системе существует только одна модель сделки:

Trade

Recovery не создаёт альтернативных представлений Trade.


Единственный REST Pipeline

Recovery использует исключительно существующий REST Pipeline.

Создание второго Pipeline запрещается.


Единственный механизм нормализации

Нормализация порядка выполняется только в пределах Recovery и исключительно перед передачей сделок в Stream Consistency.

После прохождения данного этапа все последующие компоненты работают только с Canonical ASCENDING-потоком.


Архитектурные ограничения

Build 060.19 сознательно не решает следующие задачи.

  • автоматический запуск Recovery;
  • вычисление диапазона восстановления;
  • управление несколькими символами;
  • повторные попытки получения истории;
  • планирование Recovery;
  • мониторинг Recovery;
  • сбор метрик;
  • журналирование Recovery;
  • хранение состояния Recovery.

Все перечисленные задачи относятся к следующим Build серии 060.


Гарантии обратной совместимости

После реализации Build 060.19 гарантируется следующее.

  • Все существующие Build продолжают работать без изменений.
  • Существующие публичные API остаются совместимыми.
  • Existing REST Pipeline продолжает использоваться без модификации.
  • Canonical Trade Model остаётся неизменной.
  • TradeStreamConsistencyController остаётся единственным владельцем правил согласованности Trade Stream.

Таким образом внедрение Recovery не нарушает существующую архитектуру проекта.


Правила дальнейшего развития

Любое развитие Recovery должно соответствовать следующим принципам.

Не допускается

  • создание альтернативного Trade Pipeline;
  • создание второго Controller согласованности;
  • создание альтернативной модели Trade;
  • изменение ответственности RecoveryController;
  • дублирование логики TradeStreamConsistencyController.

Допускается

  • добавление Retry Policy;
  • добавление Metrics;
  • добавление Telemetry;
  • добавление Tracing;
  • добавление Performance Monitoring;
  • расширение RecoveryResult;
  • добавление новых диагностических возможностей.

Расширение допускается только при сохранении существующего публичного API.


Матрица архитектурной эволюции

Build Статус Роль
060.1 Завершён Canonical Trade Model
060.2060.8 Завершены Базовая инфраструктура
060.9 Завершён REST Trade Pipeline
060.10060.16 Завершены Parser / Validation / Mapper
060.17 Завершён Stream Consistency
060.18 Завершён REST Pipeline Integration
060.19 🟢 Текущий Build Trade Recovery
060.20 Следующий Recovery Registry
060.21 Планируется Acquisition Protocol
060.22 Планируется Acquisition Service
060.23 Планируется Runtime Integration
060.24 Планируется Reconnect Integration
060.25 Планируется Trades Feed Integration
060.26 Планируется Финальный архитектурный аудит

Архитектурная зрелость Build

После завершения Build 060.19 подсистема Trades Feed получает завершённый фундамент для работы с историческими сделками.

В результате серия Build 060 впервые включает полный жизненный цикл обработки Trade.

REST

↓

Parser

↓

Validation

↓

Mapper

↓

Canonical Trade

↓

Recovery Normalizer

↓

TradeStreamConsistencyController

↓

Canonical Trade Stream

Следующие Build будут расширять данный Pipeline, не изменяя его фундаментальную архитектуру.


Итоги приложения F

Настоящее приложение фиксирует архитектурное положение Build 060.19 в общей системе Dzentra и определяет правила его дальнейшей эволюции.

После утверждения настоящего приложения Build 060.19 рассматривается как стабильный архитектурный фундамент для всех последующих работ по подсистеме Trades Feed.

Любые изменения, затрагивающие описанные в данном приложении архитектурные гарантии, должны сопровождаться новым Architecture Decision Record (ADR) и обновлением архитектурной спецификации.