Files
dzentra_bot/docs/migrations/build_060_17.md

55 KiB
Raw Permalink Blame History

Build 060.17 — Trades Feed Core

Engineering Migration Report


Контроль документа

Свойство Значение
Build 060.17
Название Trades Feed Core
Статус Completed
Проект Dzentra
Подсистема Market Data Acquisition
Компонент Trades Feed
Версия 1.0

Цель Build

После завершения Build 060.16 подсистема Market Data Acquisition получила полностью самостоятельный слой формирования подписок на поток сделок.

К этому моменту архитектура уже обеспечивала:

  • универсальный WebSocket Runtime;
  • транспортную маршрутизацию входящих сообщений;
  • Subscription Layer;
  • полноценный Trade Adapter;
  • преобразование транспортных сообщений в каноническую модель Trade.

Несмотря на это, один важный архитектурный уровень ещё отсутствовал.

Система уже умела:

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

Однако отсутствовал компонент, объединяющий эти части в единый поток получения данных.

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

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

Подобное решение противоречило одному из фундаментальных принципов архитектуры Dzentra.

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

Главной задачей Build 060.17 становится создание специализированного слоя Trades Feed, который объединяет источник транспортных документов и существующий механизм их преобразования в единую точку получения канонических моделей Trade.

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

  • специализированный TradeDocumentSource;
  • специализированный TradeDocumentHandler;
  • специализированный TradesFeed;
  • специализированный TradesFeedRegistry;
  • отдельный тип ошибки регистрации Feed;
  • набор Protocol-интерфейсов для всех перечисленных компонентов.

При этом Build принципиально не затрагивает:

  • WebSocket Runtime;
  • Subscription Layer;
  • Unified Router;
  • Parser;
  • Value Validation;
  • Mapper;
  • Trade Adapter;
  • Runtime Registry;
  • восстановление подписок;
  • обработку истории сделок;
  • дедупликацию;
  • сортировку сделок;
  • интеграцию Feed с Runtime.

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


Предпосылки

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

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

Она включала следующие этапы.

WebSocket Document
        │
        ▼
Schema Validation
        │
        ▼
Parser
        │
        ▼
Value Validation
        │
        ▼
Mapper
        │
        ▼
Trade

Каждый из перечисленных компонентов обладал строго определённой зоной ответственности.

Schema Validation проверяла соответствие транспортного документа контракту WebSocket API.

Parser извлекал необходимые поля.

Value Validation проверяла корректность полученных значений.

Mapper строил внутреннюю модель Trade.

Подобная архитектура уже полностью соответствовала общим принципам Market Data Acquisition.

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

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

Именно эту роль должен был взять на себя новый Feed.


Архитектурное основание

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

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

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

Handler отвечает только за преобразование документа.

Feed отвечает только за организацию потока данных.

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

До начала Build подобное разделение отсутствовало.

Trade Adapter уже существовал, однако он представлял собой исключительно преобразователь транспортной модели.

Он не должен был:

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

Эти обязанности относятся к уровню Feed.

После завершения Build архитектура приобретает следующий вид.

TradeDocumentSource
        │
        ▼
TradeDocumentHandler
        │
        ▼
TradesFeed
        │
        ▼
Trade

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

Такое разделение полностью повторяет ранее реализованную архитектуру других потоков рыночных данных и формирует единый шаблон построения Feed внутри подсистемы Market Data Acquisition.


Результаты архитектурного аудита

Перед началом реализации был выполнен полный аудит существующей структуры подсистемы Market Data Acquisition.

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

В частности, были обнаружены:

feeds/
    quotes_feed.py
    instrument_feed.py

а также соответствующие обработчики

handlers/
    quotes_handler.py
    instrument_handler.py

Дополнительно в структуре проекта уже существовали пустые заготовки файлов

feeds/trades_feed.py

handlers/trades_handler.py

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

Отдельный аудит был проведён для существующего Trade Adapter.

Проверка показала, что функция

adapt_websocket_trade_document()

уже реализует полный внутренний конвейер обработки.

Она выполняет:

  • Parser;
  • Value Validation;
  • Mapper.

При этом предварительная Schema Validation в состав Adapter не входит.

Данное архитектурное решение оказалось принципиально важным.

Это означало, что новый Handler должен выполнять только одну дополнительную обязанность — проверку транспортного документа перед передачей его Adapter.

Таким образом существующий Adapter не потребовал никаких изменений.

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


Архитектурное решение

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

Parser, Value Validation и Mapper уже были реализованы, протестированы и использовались Trade Adapter.

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

Новая архитектура получила следующий вид.

TradeDocumentSource
        │
        ▼
TradeDocumentHandler
        │
        ▼
TradesFeed
        │
        ▼
Trade

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

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

Handler знает только правила преобразования документа.

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

Подобное разделение полностью соответствует принятой архитектуре Market Data Acquisition.


Новые Protocol-интерфейсы

Одной из целей Build являлось формирование полноценного контрактного уровня для нового Feed.

До начала Build соответствующие интерфейсы отсутствовали.

В рамках реализации были добавлены три новых Protocol.

TradeDocumentSource

TradeDocumentHandler

TradesFeedProtocol

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

Во-первых, новый Feed перестаёт зависеть от конкретной реализации источника данных.

Во-вторых, Feed перестаёт зависеть от конкретной реализации Handler.

В-третьих, появляется возможность независимого тестирования каждого компонента посредством Stub-реализаций.

Вся дальнейшая работа Trade Feed строится исключительно через данные контракты.

Это полностью соответствует принципу Dependency Inversion, принятому в архитектуре Dzentra.


TradeDocumentSource

Protocol

TradeDocumentSource

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

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

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

symbol
        │
        ▼
fetch_trade_document()
        │
        ▼
raw document

Источник не знает:

  • структуру модели Trade;
  • Parser;
  • Mapper;
  • Feed;
  • Runtime.

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


TradeDocumentHandler

Вторым новым контрактом стал

TradeDocumentHandler

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

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

raw document
        │
        ▼
handle_trade_document()
        │
        ▼
Trade

Принципиальным архитектурным решением стало использование типа

object

в качестве входного параметра Protocol.

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

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

Подобное решение предотвращает проникновение знаний о транспортной модели Dzengi в общий слой Protocol.


TradesFeedProtocol

Последним новым интерфейсом стал

TradesFeedProtocol

Он определяет публичный контракт самого Feed.

Feed предоставляет единственную операцию.

load_trade(symbol)
        │
        ▼
Trade

Никаких дополнительных обязанностей Protocol не содержит.

Он не определяет:

  • очереди;
  • кеш;
  • подписки;
  • историю;
  • Runtime.

Все перечисленные возможности будут добавляться в следующих Build без изменения публичного интерфейса Feed.

Именно поэтому уже сейчас выбран максимально простой и устойчивый контракт.


Новый TradeFeedRegistry

Следующим отсутствующим архитектурным уровнем являлся Registry.

До начала Build инфраструктура уже содержала аналогичные Registry для других потоков рыночных данных.

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

Новый компонент получил название

TradesFeedRegistry

Его обязанностью является регистрация и получение экземпляров Feed по имени источника.

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

Source name
        │
        ▼
TradesFeedRegistry
        │
        ▼
TradesFeed

Registry не создаёт Feed.

Не управляет жизненным циклом.

Не выполняет ленивую инициализацию.

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

Он лишь хранит зарегистрированные реализации.


Нормализация имени источника

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

Удаляются внешние пробелы.

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

Например

"dzengi"

и

"   dzengi   "

рассматриваются как один и тот же источник.

Подобное решение полностью повторяет поведение остальных Registry подсистемы.


Проверка соответствия Protocol

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

isinstance(..., TradesFeedProtocol)

Таким образом Registry гарантирует хранение исключительно корректных реализаций Feed.

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


Новый тип ошибки

Для Trade Feed введён собственный тип исключения.

TradeFeedRegistryError

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

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


Новый TradeDocumentHandler

Одним из ключевых компонентов Build становится

DzengiTradeDocumentHandler

Во время проектирования рассматривалось несколько вариантов распределения обязанностей между Handler и Adapter.

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

Однако проведённый архитектурный аудит показал, что подобный подход приводит к утечке транспортной модели Dzengi в общий слой Protocol.

Поэтому было принято другое решение.

Handler принимает обычный объект.

object

После чего самостоятельно выполняет Schema Validation.

Лишь затем документ передаётся существующему Adapter.

Итоговая последовательность выглядит следующим образом.

raw document
        │
        ▼
Schema Validation
        │
        ▼
Trade Adapter
        │
        ▼
Trade

Таким образом удалось сохранить полную независимость общего Protocol от конкретной реализации транспортной модели.


Почему Schema Validation выполняет Handler

Данное решение было принято осознанно.

Schema Validation относится к уровню обработки транспортного документа.

Feed не должен знать устройство WebSocket-сообщения.

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

Он получает уже корректную транспортную модель.

Следовательно именно Handler становится естественным местом выполнения Schema Validation.

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


Новый TradesFeed

Центральным компонентом настоящего Build становится

TradesFeed

Именно он завершает формирование базовой инфраструктуры получения сделок внутри подсистемы Market Data Acquisition.

До начала Build существовали все необходимые строительные блоки.

Система уже умела:

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

Однако отсутствовал компонент, который объединял бы эти операции в единый сценарий получения данных.

После завершения Build данную роль выполняет TradesFeed.


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

Конструкция Feed намеренно сделана максимально простой.

Она состоит только из двух зависимостей.

TradeDocumentSource

TradeDocumentHandler

При создании Feed оба компонента передаются через конструктор.

TradesFeed
    │
    ├── TradeDocumentSource
    │
    └── TradeDocumentHandler

Подобная схема полностью соответствует принципу Dependency Injection.

Feed ничего не знает о конкретной реализации источника данных.

Он также не знает, каким образом реализован Handler.

Ему известны исключительно Protocol-контракты.

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


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

Работа Feed состоит всего из двух операций.

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

Затем этот документ передаётся Handler.

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

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

symbol
    │
    ▼
TradeDocumentSource
    │
    ▼
raw document
    │
    ▼
TradeDocumentHandler
    │
    ▼
Trade

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

Он не изменяет данные.

Не валидирует значения.

Не преобразует транспортную модель.

Не выполняет кеширование.

Не хранит историю.

Не осуществляет повторные запросы.

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


Почему TradesFeed является Orchestration Layer

Во время проектирования отдельно рассматривался вопрос о распределении логики между Feed и Handler.

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

Первый вариант предполагал перенос части логики обработки непосредственно в Feed.

В этом случае Feed должен был бы:

  • выполнять Schema Validation;
  • вызывать Parser;
  • вызывать Mapper;
  • контролировать отдельные этапы обработки.

После анализа архитектуры данный подход был отклонён.

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

Это нарушало бы принцип единственной ответственности.

Поэтому был выбран второй вариант.

Feed не содержит бизнес-логики.

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

Именно поэтому архитектурно он относится к категории Orchestration Layer.


Преимущества выбранного решения

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

Во-первых, Feed остаётся исключительно лёгким координатором.

Во-вторых, обработка документа полностью сосредоточена внутри Handler.

В-третьих, изменение внутреннего устройства Adapter не требует изменения Feed.

В-четвёртых, в дальнейшем Handler может быть заменён другой реализацией без изменения Feed.

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


Почему Adapter не изменялся

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

На первый взгляд могло показаться логичным добавить в него Schema Validation.

После детального анализа было принято решение этого не делать.

Trade Adapter уже обладает строго определённой зоной ответственности.

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

В его состав входят:

Parser

Value Validation

Mapper

Schema Validation относится к предыдущему уровню обработки.

Она проверяет корректность транспортного документа ещё до появления транспортной модели.

Следовательно включение Schema Validation внутрь Adapter привело бы к смешению двух различных архитектурных уровней.

Поэтому существующий Adapter был оставлен без изменений.

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


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

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

TradeDocumentSource
        │
        ▼
DzengiTradeDocumentHandler
        │
        ▼
Schema Validation
        │
        ▼
Trade Adapter
        │
        ▼
Parser
        │
        ▼
Value Validation
        │
        ▼
Mapper
        │
        ▼
Trade

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

Источник отвечает за получение данных.

Handler отвечает за подготовку документа.

Adapter отвечает за преобразование транспортной модели.

Feed отвечает за организацию взаимодействия между компонентами.

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


Использование существующей инфраструктуры

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

В ходе реализации не создавались новые Parser.

Не создавались новые Mapper.

Не создавались новые модели данных.

Не создавались новые транспортные структуры.

Вместо этого новый Feed полностью использует уже существующую инфраструктуру.

Повторно используются:

  • Schema Validation;
  • Trade Adapter;
  • Parser;
  • Value Validation;
  • Mapper;
  • каноническая модель Trade.

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


Почему Feed не взаимодействует с Runtime

Во время проектирования отдельно рассматривалась возможность непосредственного подключения Feed к WebSocket Runtime.

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

Однако после анализа архитектуры данный вариант был отклонён.

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

Feed отвечает исключительно за получение канонических моделей.

Эти два уровня не должны знать друг о друге.

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

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

Он может работать:

  • с WebSocket;
  • с REST;
  • с тестовыми источниками;
  • с Mock-реализациями.

Без каких-либо изменений собственного кода.


Подготовка к дальнейшему развитию

Несмотря на минимальный объём собственной логики, настоящий Build закладывает фундамент для последующих этапов развития Trade Pipeline.

Именно поверх нового Feed будут реализованы:

  • непрерывный поток сделок;
  • интеграция с WebSocket Runtime;
  • восстановление подписок после reconnect;
  • дедупликация сообщений;
  • упорядочивание сделок;
  • обработка пропусков;
  • REST Backfill;
  • синхронизация истории.

Таким образом Build 060.17 завершает создание базовой архитектуры Trades Feed и формирует стабильную основу для последующего развития подсистемы.


Изменённые файлы

В рамках Build были добавлены и расширены несколько компонентов подсистемы Market Data Acquisition.

Все изменения являются локальными и полностью соответствуют согласованному scope настоящего Build.

Существующая архитектура Parser, Mapper, Runtime и Subscription Layer изменена не была.


Protocol

src/market_data/acquisition/protocol.py

Добавлены три новых контракта.

TradeDocumentSource

TradeDocumentHandler

TradesFeedProtocol

Новые Protocol завершают контрактный уровень Trade Feed и позволяют всем последующим компонентам взаимодействовать исключительно через абстракции.


Exceptions

src/market_data/acquisition/exceptions.py

Добавлен новый специализированный тип ошибки.

TradeFeedRegistryError

Он используется исключительно инфраструктурой регистрации Feed.

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


Registry

src/market_data/acquisition/registry.py

Добавлен новый компонент

TradesFeedRegistry

Реализованы:

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

Архитектура полностью повторяет существующий шаблон остальных Registry проекта.


Handler

src/market_data/acquisition/handlers/trades_handler.py

Реализован

DzengiTradeDocumentHandler

В его обязанности входят:

  • Schema Validation;
  • вызов существующего Trade Adapter;
  • возврат канонической модели Trade.

Handler не содержит собственной бизнес-логики и не изменяет существующий Adapter.


Feed

src/market_data/acquisition/feeds/trades_feed.py

Реализован новый

TradesFeed

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

  • TradeDocumentSource;
  • TradeDocumentHandler.

Feed не содержит логики обработки данных.


Adapter Export

src/market_data/acquisition/adapters/dzengi/__init__.py

Добавлен экспорт

adapt_websocket_trade_document

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


Добавленные unit-тесты

Настоящий Build сопровождается расширением существующего набора unit-тестов.

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

Кроме того, были расширены тесты существующей инфраструктуры Protocol и Registry.


Handler

Добавлен новый файл.

tests/unit/market_data/acquisition/handlers/test_trades_handler.py

Проверяются следующие сценарии.


Корректная обработка документа

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


Вызов Schema Validation

Подтверждается, что Handler выполняет предварительную проверку документа перед передачей его Adapter.


Использование Adapter

Подтверждается, что после успешной проверки документ передаётся существующему Trade Adapter.


Возврат канонической модели

Подтверждается, что результатом работы Handler всегда является объект Trade.


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

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


Feed

Добавлен новый файл.

tests/unit/market_data/acquisition/feeds/test_trades_feed.py

Проверяются следующие сценарии.


Получение документа

Подтверждается вызов метода

fetch_trade_document()

у источника данных.


Передача документа Handler

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


Возврат модели Trade

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


Корректная последовательность вызовов

Проверяется, что сначала вызывается Source, а затем Handler.


Protocol

Расширен существующий файл.

tests/unit/market_data/acquisition/test_protocol.py

Добавлены проверки новых контрактов.

Подтверждается:

  • соответствие TradeDocumentSource;
  • соответствие TradeDocumentHandler;
  • соответствие TradesFeedProtocol;
  • корректность Structural Typing.

Registry

Расширен существующий файл.

tests/unit/market_data/acquisition/test_registry.py

Добавлены проверки нового

TradesFeedRegistry

Проверяются:

  • успешная регистрация Feed;
  • получение зарегистрированного Feed;
  • проверка Protocol;
  • защита от повторной регистрации;
  • нормализация имени источника;
  • обработка неизвестного источника;
  • генерация TradeFeedRegistryError;
  • наследование нового исключения от общей иерархии ошибок Market Data Acquisition.

Результаты тестирования

После завершения реализации был выполнен запуск полного набора unit-тестов подсистемы Market Data Acquisition.

Использовалась команда.

python -m pytest tests/unit/market_data/acquisition -q

Результат выполнения.

950 passed

Все существующие тесты проекта успешно пройдены.

Новые компоненты не вызвали регрессий ранее реализованной функциональности.


Регрессионное тестирование

Дополнительно были выполнены целевые проверки новых компонентов.

На ранних этапах разработки тестирование проводилось отдельно для:

  • Trade Handler;
  • Trades Feed.

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

Результат подтвердил полную совместимость нового Feed с существующей архитектурой.

Ни один ранее реализованный компонент не потребовал изменений.


Проверка компиляции

После завершения реализации выполнена проверка корректности компиляции новых компонентов.

Ошибок синтаксиса обнаружено не было.

Все новые файлы успешно проходят статическую проверку интерпретатора Python.


Проверка Git diff

После завершения Build рекомендуется выполнить стандартную инженерную проверку.

git diff --check

Ожидаемый результат.

без замечаний

Проверка подтверждает отсутствие:

  • trailing whitespace;
  • конфликтов merge;
  • нарушений форматирования;
  • ошибок окончания строк.

Scope Build 060.17

Настоящий Build ограничен исключительно реализацией инфраструктуры Trades Feed Core.

В область реализации входят:

  • Protocol;
  • Registry;
  • Handler;
  • Feed;
  • специализированное исключение;
  • unit-тесты новой инфраструктуры.

Build не включает:

  • интеграцию с Runtime;
  • непрерывное получение Trade;
  • подписки WebSocket;
  • дедупликацию;
  • сортировку Trade;
  • восстановление истории;
  • REST Backfill;
  • агрегирование сделок;
  • хранение состояния Feed.

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


Архитектурный результат

После завершения Build 060.17 подсистема Market Data Acquisition получила полностью сформированный базовый уровень Trades Feed.

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

TradeDocumentSource
        │
        ▼
TradeDocumentHandler
        │
        ▼
Schema Validation
        │
        ▼
Trade Adapter
        │
        ▼
Parser
        │
        ▼
Value Validation
        │
        ▼
Mapper
        │
        ▼
Trade

Над данной цепочкой располагается компонент

TradesFeed

который организует взаимодействие между Source и Handler.

При этом Feed не вмешивается в процесс преобразования данных и не содержит собственной бизнес-логики.

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

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


Соблюдение архитектурных принципов

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


Локальность изменений

Все изменения ограничены исключительно инфраструктурой Trade Feed.

Не изменялись:

  • Runtime;
  • Subscription Layer;
  • Parser;
  • Mapper;
  • Value Validation;
  • транспортные модели;
  • каноническая модель Trade.

Это позволило свести риск возникновения регрессий к минимуму.


Повторное использование существующей инфраструктуры

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

Новый Feed не создаёт собственных механизмов обработки.

Он использует уже существующие:

  • Schema Validation;
  • Trade Adapter;
  • Parser;
  • Value Validation;
  • Mapper.

Подобное решение обеспечивает единый конвейер обработки Trade независимо от точки входа данных.


Разделение ответственности

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

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

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

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

TradesFeedRegistry отвечает исключительно за регистрацию реализаций Feed.

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

Это полностью соответствует принципу Single Responsibility.


Использование Protocol

Все зависимости нового Feed построены через Protocol.

Ни один компонент не зависит от конкретной реализации другого.

Подобный подход обеспечивает:

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

Отсутствие состояния

Новый Feed не хранит собственного состояния.

Он не содержит:

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

Каждый вызов метода

load_trade()

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

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

Stateful-механизмы будут реализованы отдельными Build.


Подготовка к масштабированию

Несмотря на минимальный объём собственной логики, настоящий Build закладывает архитектурную основу для дальнейшего развития Trade Pipeline.

Поверх текущего Feed могут быть реализованы:

  • непрерывный WebSocket Feed;
  • буферизация сделок;
  • сортировка по времени;
  • дедупликация сообщений;
  • REST Backfill;
  • синхронизация после reconnect;
  • агрегирование Trade.

При этом публичный контракт Feed останется неизменным.


Архитектурные решения Build (ADR)

ADR-060.17-001

TradesFeed является исключительно Orchestration Layer.

Feed не содержит бизнес-логики.

Его единственная обязанность заключается в организации взаимодействия между Source и Handler.

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


ADR-060.17-002

Schema Validation выполняется внутри Handler.

Проверка структуры транспортного документа относится к уровню обработки документа.

Feed не должен знать устройство WebSocket-сообщения.

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

Поэтому ответственность за Schema Validation закреплена за Handler.


ADR-060.17-003

Trade Adapter остаётся неизменным.

Существующий Adapter уже реализует:

  • Parser;
  • Value Validation;
  • Mapper.

Расширение его обязанностей привело бы к нарушению принципа единственной ответственности.

В рамках Build Adapter используется повторно без каких-либо изменений.


ADR-060.17-004

Все зависимости Feed определяются через Protocol.

Feed взаимодействует исключительно с контрактами:

TradeDocumentSource

TradeDocumentHandler

Конкретные реализации остаются полностью взаимозаменяемыми.

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


ADR-060.17-005

TradesFeedRegistry повторяет существующий архитектурный шаблон Registry.

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

Использован уже принятый архитектурный шаблон проекта.

Благодаря этому все Registry подсистемы Market Data Acquisition обладают единым поведением и единым жизненным циклом.


ADR-060.17-006

Новый Feed не зависит от Runtime.

Несмотря на то что в дальнейшем Trade будет поступать через WebSocket Runtime, настоящий Build сознательно не связывает эти компоненты.

Feed остаётся независимым от источника транспортных документов.

Это позволяет использовать его:

  • с WebSocket;
  • с REST;
  • с Mock-реализациями;
  • в unit-тестах.

Без изменения собственного кода.


Критерии завершения Build

Build 060.17 считается завершённым, поскольку выполнены все поставленные задачи.

  • ✔ добавлен TradeDocumentSource;
  • ✔ добавлен TradeDocumentHandler;
  • ✔ добавлен TradesFeedProtocol;
  • ✔ реализован TradesFeedRegistry;
  • ✔ реализован TradeFeedRegistryError;
  • ✔ реализован DzengiTradeDocumentHandler;
  • ✔ реализован TradesFeed;
  • ✔ выполнен экспорт adapt_websocket_trade_document;
  • ✔ реализованы unit-тесты Handler;
  • ✔ реализованы unit-тесты Feed;
  • ✔ расширены тесты Protocol;
  • ✔ расширены тесты Registry;
  • ✔ успешно пройден полный набор тестов (950 passed);
  • ✔ существующая архитектура не нарушена;
  • ✔ изменения полностью соответствуют согласованному scope Build.

Следующий этап

Следующим этапом дорожной карты является

Build 060.18 — Trades Feed Runtime Integration

Основной целью следующего Build станет интеграция нового Trades Feed с инфраструктурой WebSocket Runtime.

На данном этапе предстоит реализовать:

  • подключение Feed к Runtime;
  • использование Subscription Layer;
  • получение непрерывного потока сделок;
  • взаимодействие с Runtime Commands;
  • подготовку инфраструктуры для обработки непрерывного потока Trade.

Build 060.17 создаёт необходимый фундамент для данной интеграции.


Итог

Build 060.17 завершил формирование базовой инфраструктуры Trades Feed Core внутри подсистемы Market Data Acquisition.

В рамках Build был реализован полный набор контрактов, необходимых для организации потока сделок:

  • TradeDocumentSource;
  • TradeDocumentHandler;
  • TradesFeedProtocol;
  • TradesFeedRegistry;
  • TradeFeedRegistryError;
  • DzengiTradeDocumentHandler;
  • TradesFeed.

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

Особое внимание было уделено сохранению архитектурных принципов проекта.

Feed реализован как лёгкий Orchestration Layer.

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

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

В результате Build сформировал завершённый базовый слой получения сделок, который полностью соответствует архитектуре Market Data Acquisition, успешно прошёл полное регрессионное тестирование (950 passed) и стал фундаментом для последующей интеграции с WebSocket Runtime.