Files
dzentra_bot/docs/migrations/build_060_8.md

53 KiB
Raw Permalink Blame History

Build 060.8 — REST Trades Document Source

Engineering Migration Report


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

Свойство Значение
Build 060.8
Название REST Trades Document Source
Статус Завершён
Проект Dzentra
Подсистема Market Data Acquisition
Компонент Trades Feed / Time & Sales
Версия документа 1.0
Дата завершения 2026-07-19

Цель Build

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

К этому моменту были реализованы:

  • транспортная модель агрегированной сделки;
  • Schema Validation;
  • Parser;
  • Value Validation;
  • Mapper;
  • REST Trade Adapter.

Pipeline обработки документа уже имел следующий вид:

ValidatedRestAggTradesDocument

        │

        ▼

REST Trade Adapter

        │

        ▼

Parser

        │

        ▼

Value Validation

        │

        ▼

Mapper

        │

        ▼

tuple[Trade]

Однако данный pipeline начинался уже после получения документа.

В архитектуре отсутствовал компонент, отвечающий исключительно за взаимодействие с REST API биржи.

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

  • знать REST endpoint;
  • формировать параметры HTTP-запроса;
  • работать с ExchangeRestClient;
  • создавать HTTP-клиент;
  • обрабатывать транспортные ошибки.

Таким образом отсутствовал отдельный Source Layer для получения документа.

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

Следовательно возникла необходимость реализовать специализированный REST Source, который будет отвечать исключительно за получение документа агрегированных сделок.

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

Build намеренно не включает:

  • Schema Validation;
  • Parser;
  • Value Validation;
  • Mapper;
  • REST Trade Adapter;
  • REST polling;
  • Trades Feed;
  • Runtime Integration;
  • Registry;
  • Acquisition Service;
  • объединение REST и WebSocket сделок;
  • дедупликацию;
  • сортировку;
  • хранение истории;
  • агрегацию;
  • аналитическую обработку;
  • production-интеграцию.

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


Архитектурный контекст

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

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

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

Exchange

        │

        ▼

REST Client

        │

        ▼

Document Source

        │

        ▼

Raw Document

        │

        ▼

Schema Validation

        │

        ▼

Validated Document

        │

        ▼

Adapter

        │

        ▼

Canonical Model

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

Например:

  • Instrument Document Source;
  • Quote Document Source;
  • Candles Document Source.

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

Ни один из них:

  • не выполняет Schema Validation;
  • не создаёт предметные модели;
  • не анализирует содержимое документа;
  • не выполняет бизнес-логику.

REST Trades должны полностью следовать этому архитектурному принципу.

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

Exchange

        │

        ▼

ExchangeRestClient

        │

        ▼

DzengiTradesDocumentSource

        │

        ▼

Raw REST Document

        │

        ▼

Schema Validation

        │

        ▼

ValidatedRestAggTradesDocument

        │

        ▼

REST Trade Adapter

        │

        ▼

Parser

        │

        ▼

Value Validation

        │

        ▼

Mapper

        │

        ▼

tuple[Trade]

Таким образом Build завершает формирование Source Layer для REST Trade Pipeline.


Исходное состояние

До начала Build 060.8 проект уже содержал следующие REST Source.

DzengiInstrumentDocumentSource

DzengiQuoteDocumentSource

DzengiCandlesDocumentSource

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

Каждый Source:

  • инкапсулировал ExchangeRestClient;
  • выполнял HTTP GET;
  • формировал параметры запроса;
  • преобразовывал транспортные исключения;
  • возвращал необработанный REST-документ.

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

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

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

Кроме того, отсутствовал специализированный тип транспортной ошибки для REST Trade.

В системе уже существовали:

InstrumentReferenceTransportError

QuoteTransportError

CandleTransportError

Но отсутствовал:

TradeTransportError

Таким образом Build 060.8 должен был устранить оба архитектурных пробела:

  • добавить REST Source;
  • добавить специализированное транспортное исключение.

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

Build должен был остаться полностью Local Additive Change.


Предварительный архитектурный аудит

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

Были проанализированы следующие файлы.

app/src/market_data/acquisition/adapters/dzengi/rest.py

app/src/integrations/exchange/rest_client.py

app/src/market_data/acquisition/exceptions.py

docs/migrations/build_060_7.md

Кроме анализа исходного кода был повторно проверен полный REST Pipeline Acquisition.

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

  • ExchangeRestClient уже является универсальным HTTP-транспортом.
  • REST Source не должен знать структуру документа.
  • REST Source не должен выполнять Schema Validation.
  • REST Source не должен выполнять Parser.
  • REST Source не должен выполнять Value Validation.
  • REST Source не должен выполнять Mapper.
  • REST Source отвечает исключительно за получение документа и преобразование транспортных ошибок.

Именно эти выводы стали основой проектирования Build 060.8.

Архитектурная задача Build

Главная задача Build 060.8 заключается не в добавлении новой логики обработки сделок.

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

Задача Build состоит в создании официального Source Layer для REST Trade Pipeline.

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

DzengiTradesDocumentSource

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

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

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

Schema Validation

↓

REST Trade Adapter

↓

Parser

↓

Value Validation

↓

Mapper

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


Рассмотренные архитектурные решения

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

Основной вопрос заключался не в выборе способа выполнения HTTP-запроса.

Главной задачей было определить,

какая именно архитектурная ответственность должна принадлежать Source Layer.

Именно на этом этапе были рассмотрены несколько вариантов реализации.


Вариант 1

Получать REST-документ напрямую через ExchangeRestClient.

Например:

client = ExchangeRestClient()

document = client.get_payload(...)

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

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

Причины.

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

  • знать endpoint;
  • знать параметры запроса;
  • создавать HTTP-клиент;
  • обрабатывать транспортные ошибки.

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

Кроме того,

подобный подход полностью противоречит архитектуре остальных REST Source проекта.

Следовательно данный вариант был отклонён.


Вариант 2

Разместить получение документа внутри REST Trade Adapter.

Например:

HTTP

↓

JSON

↓

REST Trade Adapter

↓

Parser

↓

Validation

↓

Mapper

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

Однако после повторного анализа архитектуры было подтверждено,

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

Получение документа относится к Source Layer.

Если Adapter начнёт самостоятельно выполнять HTTP-запрос,

он одновременно получит две независимые ответственности:

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

Это нарушает принцип Single Responsibility.

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


Вариант 3

Создать специализированный REST Source.

Именно этот вариант оказался полностью совместимым с архитектурой проекта.

Новый компонент отвечает исключительно за:

  • получение REST-документа;
  • подготовку параметров запроса;
  • работу с ExchangeRestClient;
  • преобразование транспортных ошибок.

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

Именно этот вариант был утверждён для реализации Build 060.8.


Почему выбран отдельный REST Source

Во время проектирования обсуждался вопрос,

нужно ли вообще создавать отдельный компонент,

если ExchangeRestClient уже умеет выполнять HTTP GET.

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

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

Он ничего не знает:

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

Напротив,

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

Именно Source определяет:

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

Следовательно REST Source является не транспортом,

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

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


Почему Trades Source реализован классом

Во время реализации отдельно обсуждался вопрос,

следует ли использовать функцию,

как это было сделано в Build 060.7,

или класс.

На первый взгляд мог появиться следующий API.

fetch_trades_document(...)

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

Причина заключается в том,

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

Он содержит экземпляр:

ExchangeRestClient

или получает его через Dependency Injection.

Таким образом объект Source обладает состоянием.

Даже если это состояние состоит только из одной зависимости,

оно всё равно является частью жизненного цикла компонента.

Следовательно данный компонент относится к категории Stateful.

В соответствии с утверждённым архитектурным принципом проекта:

Stateful

↓

Class

Source должен быть реализован именно классом.


Почему используется Dependency Injection

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

Например:

DzengiQuoteDocumentSource(
    client=...
)

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

Если клиент передан извне,

используется именно он.

Если клиент отсутствует,

Source самостоятельно создаёт:

ExchangeRestClient()

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

Во-первых,

упрощается unit-тестирование.

Во-вторых,

Source не зависит от конкретной реализации клиента.

В-третьих,

архитектура остаётся полностью совместимой с уже существующими Source Layer.


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

Во время реализации обсуждалось,

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

Например:

self._client = ExchangeRestClient()

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

Причины.

Во-первых,

если клиент передаётся через Dependency Injection,

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

Во-вторых,

ленивое создание позволяет не создавать HTTP-клиент,

если Source был создан,

но фактически ещё ни разу не использовался.

Именно поэтому применяется следующая схема.

client отсутствует

↓

первый вызов fetch_trades_document()

↓

создание ExchangeRestClient

Такой подход полностью соответствует реализации остальных REST Source проекта.


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

Во время проектирования отдельно обсуждался вопрос,

следует ли REST Source сразу возвращать:

ValidatedRestAggTradesDocument

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

Причины.

Schema Validation уже является самостоятельным архитектурным уровнем.

В системе существуют независимые функции:

validate_quote_schema()

validate_candle_schema()

validate_rest_agg_trades_schema()

Если Source начнёт самостоятельно выполнять проверку структуры,

он одновременно получит две независимые ответственности:

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

Это нарушает уже сформированную архитектуру Acquisition Layer.

Следовательно REST Source возвращает исключительно необработанный REST-документ.

Следующий слой самостоятельно выполняет Schema Validation.

Полный Pipeline после завершения Build принимает следующий вид.

Exchange

↓

ExchangeRestClient

↓

DzengiTradesDocumentSource

↓

Raw REST Document

↓

Schema Validation

↓

ValidatedRestAggTradesDocument

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

Почему Source не выполняет Parser

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

при котором REST Source сразу возвращал бы транспортные модели.

Например:

REST JSON

↓

Parser

↓

tuple[DzengiRestAggTrade]

На первый взгляд подобный подход выглядит привлекательным,

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

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

Причины.

Parser представляет собой самостоятельный уровень Acquisition Pipeline.

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

Если Parser переносится внутрь Source,

Source начинает одновременно выполнять две независимые задачи:

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

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

Кроме того,

остальные Source проекта не содержат Parser.

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


Почему Source не выполняет Value Validation

После отказа от Parser обсуждался ещё один вариант.

Можно было бы возвращать транспортные модели,

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

Например:

HTTP

↓

REST Source

↓

Parser

↓

Value Validation

↓

tuple[DzengiRestAggTrade]

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

Причины.

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

Его задача — проверка содержимого транспортной модели.

Source не должен знать,

какие именно ограничения существуют для:

  • цены;
  • количества;
  • времени;
  • идентификаторов сделок.

Все подобные проверки относятся исключительно к Validation Layer.

Следовательно Source обязан оставаться полностью независимым от предметной модели.


Почему Source не выполняет Mapper

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

при которой REST Source сразу возвращал бы:

tuple[Trade]

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

Exchange

↓

REST Source

↓

Canonical Trade

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

Причины очевидны.

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

Создание канонической модели относится к Mapper Layer.

Если Source начинает создавать экземпляры:

Trade

он становится зависимым:

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

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

Следовательно создание Canonical Trade остаётся исключительной ответственностью Mapper.


Почему введён отдельный TradeTransportError

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

Например:

InstrumentReferenceTransportError

QuoteTransportError

CandleTransportError

Однако для REST Trade использовался общий тип исключений,

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

Во время проектирования обсуждалось,

следует ли использовать существующий общий тип.

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

Причины.

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

Это позволяет вызывающему коду:

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

Поэтому был введён новый тип:

TradeTransportError

который становится официальным транспортным исключением REST Trade Pipeline.


Новый публичный API

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

DzengiTradesDocumentSource

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

fetch_trades_document(...)

Назначение метода.

  • принимает параметры REST-запроса;
  • формирует параметры HTTP GET;
  • обращается к ExchangeRestClient;
  • возвращает необработанный REST-документ;
  • преобразует транспортные исключения в TradeTransportError.

На этом ответственность Source полностью заканчивается.


Семантика REST Trades Source

После завершения Build новый компонент становится официальной точкой получения агрегированных сделок из REST API.

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

ExchangeRestClient

        │

        ▼

HTTP GET

        │

        ▼

REST JSON

        │

        ▼

DzengiTradesDocumentSource

        │

        ▼

Raw Document

Source намеренно не анализирует содержимое ответа.

Он не проверяет:

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

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

Таким образом Source остаётся максимально простым и полностью соответствует принципу Single Responsibility.


Что намеренно не делает Source

Build 060.8 специально не расширяет область ответственности нового компонента.

REST Source намеренно:

не выполняет Schema Validation;

не выполняет Parser;

не выполняет Value Validation;

не выполняет Mapper;

не создаёт Canonical Trade;

не агрегирует сделки;

не сортирует данные;

не удаляет дубликаты;

не вычисляет статистику;

не создаёт свечи;

не объединяет REST и WebSocket сделки;

не взаимодействует с Runtime;

не взаимодействует с Feed;

не взаимодействует с Registry;

не сохраняет историю;

не выполняет повторные HTTP-запросы;

не реализует polling.

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

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

В рамках Build 060.8 были изменены два файла проекта.

Добавлен новый REST Source.

app/src/market_data/acquisition/adapters/dzengi/rest.py

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

app/src/market_data/acquisition/exceptions.py

Кроме того, был добавлен новый набор unit-тестов.

tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py

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

Build полностью соответствует принципу Local Additive Change.


Изменения в rest.py

В существующий модуль REST Source добавлен новый компонент.

DzengiTradesDocumentSource

Он полностью повторяет архитектурный стиль уже существующих Source проекта.

Новый компонент содержит:

  • поддержку Dependency Injection;
  • ленивое создание ExchangeRestClient;
  • специализированный REST endpoint;
  • подготовку параметров запроса;
  • преобразование транспортных исключений.

Также в модуле определён новый endpoint.

/api/v1/aggTrades

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

Никакие существующие REST Source при этом не изменялись.


Изменения в exceptions.py

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

TradeTransportError

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

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

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

Instrument

Quote

Candle

Trade

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


Изменения в unit-тестах

Для нового REST Source создан самостоятельный файл тестов.

tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py

Это соответствует принятому соглашению проекта,

согласно которому каждый самостоятельный компонент Acquisition Layer имеет собственный независимый набор unit-тестов.

Существующие тесты:

  • Instrument REST Source;
  • Quote REST Source;
  • Candle REST Source;

не изменялись.

Build полностью additive.


Проверяемые сценарии

Новый набор тестов проверяет исключительно ответственность REST Source.

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

  • успешное получение документа;
  • корректное использование endpoint;
  • корректная передача обязательного параметра symbol;
  • корректная передача startTime;
  • корректная передача endTime;
  • корректная передача limit;
  • отсутствие параметров со значением None;
  • преобразование всех параметров в строковый формат;
  • отсутствие лишних HTTP-заголовков;
  • корректное использование переданного ExchangeRestClient;
  • ленивое создание собственного клиента;
  • преобразование транспортных ошибок в TradeTransportError;
  • возврат необработанного REST-документа без изменений.

Тем самым подтверждается,

что новый Source полностью соответствует архитектурной ответственности Source Layer.


Что намеренно не изменялось

Build 060.8 специально не изменяет существующие компоненты Acquisition Pipeline.

Без изменений остаются:

app/src/market_data/acquisition/adapters/dzengi/parser.py

app/src/market_data/acquisition/adapters/dzengi/mapper.py

app/src/market_data/acquisition/adapters/dzengi/rest_trade_adapter.py

app/src/market_data/acquisition/validation/schema.py

app/src/market_data/acquisition/validation/values.py

app/src/market_data/acquisition/models/trade.py

app/src/market_data/acquisition/runtime/

app/src/market_data/acquisition/feeds/

app/src/market_data/acquisition/service.py

app/src/market_data/acquisition/registry.py

Также Build намеренно не включает:

  • Schema Validation;
  • Parser;
  • Value Validation;
  • Mapper;
  • REST polling;
  • Trades Feed;
  • Runtime Integration;
  • объединение REST и WebSocket сделок;
  • дедупликацию;
  • хранение истории;
  • аналитическую обработку.

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

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


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

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

Команда выполнялась из каталога:

~/vsprojects/dzentra_bot/app

При активированном виртуальном окружении:

source .venv/bin/activate

Выполнена команда:

python -m compileall src

Результат:

успешно

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

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

Build не нарушил корректность структуры проекта.


Проверка локальных unit-тестов

После завершения реализации нового REST Source были выполнены специализированные unit-тесты.

Команда:

python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py \
-v

Результат:

13 passed in 0.04s

Проверки подтвердили:

  • корректную работу REST Source;
  • корректное формирование HTTP-запроса;
  • корректную обработку параметров;
  • корректную работу Dependency Injection;
  • корректное создание клиента;
  • корректное преобразование транспортных ошибок.

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


Проверка подсистемы REST Adapter

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

Команда:

python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/

Результат:

243 passed in 0.10s

Регрессий не обнаружено.

Все существующие REST- и WebSocket-компоненты продолжают работать без изменений.


Проверка подсистемы Acquisition

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

Команда:

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

Результат:

788 passed in 0.27s

Проверка подтвердила,

что новый REST Source полностью совместим с существующей архитектурой и не вызвал регрессий в других компонентах Acquisition Layer.

Проверка форматирования

После завершения работы выполнена команда:

git diff --check

Вывод отсутствует.

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

  • trailing whitespace;
  • лишних пробелов;
  • нарушений форматирования;
  • ошибок оформления diff.

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


Контроль размещения новой функциональности

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

REST Source расположен в существующем модуле:

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

Новый тип транспортного исключения расположен исключительно в:

src/market_data/acquisition/exceptions.py

Unit-тесты расположены исключительно в:

tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py

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

Архитектурная изоляция полностью сохранена.


Состояние Git

После завершения Build выполнена команда:

git status

Для Build 060.8 зафиксированы изменения:

modified:

app/src/market_data/acquisition/adapters/dzengi/rest.py

app/src/market_data/acquisition/exceptions.py

added:

app/tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py

untracked:

docs/migrations/build_060_8.md

Ветка разработки:

main

опережает origin/main.

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


Фактический diff

В рамках Build реализованы следующие изменения.

В модуле REST Source добавлен новый компонент:

DzengiTradesDocumentSource

Добавлен новый REST endpoint:

/api/v1/aggTrades

Добавлен новый публичный метод:

fetch_trades_document()

В модуле исключений добавлен новый тип:

TradeTransportError

Также добавлен отдельный набор unit-тестов:

tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py

Все изменения являются полностью additive.

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


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

После завершения Build 060.8 REST Trade Pipeline впервые получает полноценный Source Layer.

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

Exchange

        │

        ▼

ExchangeRestClient

        │

        ▼

DzengiTradesDocumentSource

        │

        ▼

Raw REST Document

        │

        ▼

Schema Validation

        │

        ▼

ValidatedRestAggTradesDocument

        │

        ▼

REST Trade Adapter

        │

        ▼

Parser

        │

        ▼

Value Validation

        │

        ▼

Mapper

        │

        ▼

tuple[Trade]

Каждый слой обладает собственной зоной ответственности.

Ни один слой не выполняет задачи соседнего.

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

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

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

Таким образом окончательно разделены три независимые архитектурные области:

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

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


Влияние на последующие Build

Build 060.8 завершает формирование инфраструктуры получения исторических сделок через REST API.

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

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

что любые изменения:

  • REST endpoint;
  • параметров HTTP-запроса;
  • механизма авторизации;
  • реализации HTTP-клиента;
  • политики обработки транспортных ошибок;

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

Все остальные компоненты Acquisition останутся неизменными.

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


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

Build 060.8 считается полностью завершённым, поскольку:

  • проведён повторный архитектурный аудит существующего REST Layer;
  • реализован специализированный REST Trades Source;
  • введён новый публичный API получения документа;
  • реализована поддержка Dependency Injection;
  • реализовано ленивое создание ExchangeRestClient;
  • реализовано формирование параметров REST-запроса;
  • реализовано преобразование транспортных ошибок в TradeTransportError;
  • сохранено разделение ответственности между Source Layer и Pipeline обработки;
  • Source не выполняет Schema Validation;
  • Source не выполняет Parser;
  • Source не выполняет Value Validation;
  • Source не выполняет Mapper;
  • Source не создаёт Canonical Trade;
  • compile-проверка успешно пройдена;
  • локальные unit-тесты успешно пройдены;
  • тесты всех адаптеров успешно пройдены;
  • тесты всей подсистемы Acquisition успешно пройдены;
  • проверка форматирования успешно пройдена;
  • scope Build не расширен.

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

После завершения Build 060.8 следующие свойства REST Trade Source считаются архитектурным контрактом проекта.

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


Инвариант 1

REST Source отвечает исключительно за получение документа.

Никакая обработка содержимого документа внутри Source не допускается.


Инвариант 2

REST Source всегда возвращает необработанный транспортный документ.

Schema Validation остаётся отдельным архитектурным слоем.


Инвариант 3

REST Source не зависит от Parser, Validation и Mapper.

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


Инвариант 4

Все транспортные ошибки получения исторических сделок преобразуются в:

TradeTransportError

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


Инвариант 5

REST Source реализуется классом.

Причина —

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


Инвариант 6

ExchangeRestClient создаётся лениво.

Если клиент передан через Dependency Injection,

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


Инвариант 7

REST Source является единственной официальной точкой получения исторических агрегированных сделок.

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

Прямое обращение к ExchangeRestClient за пределами Source Layer не допускается.


Инвариант 8

Source Layer не зависит от предметной модели.

Он ничего не знает о существовании:

Trade

или других канонических объектов системы.

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


Итог

Build 060.8 завершён успешно.

Текущее состояние REST Trade Pipeline:

Transport Source — реализован

Transport Exception — реализована

Schema Validation — реализована

REST Trade Adapter — реализован

Parser — реализован

Value Validation — реализована

Mapper — реализован

Canonical Trade — используется

Compile check — успешно

Target tests — 13 passed

Dzengi adapters — 243 passed

Acquisition subsystem — 788 passed

Whitespace check — успешно

Production Integration — намеренно не выполнялась

После завершения Build 060.8 подсистема получения исторических сделок получила полноценный специализированный Source Layer, полностью соответствующий архитектуре остальных компонентов Market Data Acquisition.

Получение REST-документа теперь полностью изолировано от его последующей обработки, что завершает формирование транспортного уровня REST Trade Pipeline.


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

Следующим этапом развития серии Build 060 становится:

Build 060.9 — WebSocket Trade Transport Model

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

Как и в случае с REST Pipeline, данный Build ограничивается исключительно транспортным уровнем и не включает:

  • Schema Validation;
  • Parser;
  • Value Validation;
  • Mapper;
  • Adapter;
  • маршрутизацию WebSocket-сообщений;
  • механизм подписки;
  • Trades Feed;
  • Runtime Integration;
  • объединение REST и WebSocket данных.

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

После завершения Build 060.9 будет сформирована транспортная основа WebSocket Trade Pipeline, полностью симметричная ранее реализованной REST-модели.

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

WebSocket Trade Transport Model

        │

        ▼

WebSocket Trade Schema Validation

        │

        ▼

WebSocket Trade Parser

        │

        ▼

WebSocket Trade Value Validation

        │

        ▼

WebSocket Trade Mapper

        │

        ▼

WebSocket Trade Adapter

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

Следующие Build последовательно реализуют:

Unified WebSocket Routing

        │

        ▼

Trade Subscription Layer

        │

        ▼

Trades Feed Core

        │

        ▼

Trade Ordering & Deduplication

        │

        ▼

REST Backfill & Gap Recovery

        │

        ▼

Trades Feed Registry

        │

        ▼

Acquisition Protocol Integration

        │

        ▼

Acquisition Service Integration

        │

        ▼

Runtime Integration

        │

        ▼

Reconnect & Recovery

        │

        ▼

Integration & Regression

        │

        ▼

Final Documentation

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

Сначала полностью формируется REST Pipeline, затем — полностью симметричный WebSocket Pipeline, после чего оба источника объединяются в единый Trades Feed (Time & Sales) без необходимости пересмотра или переработки ранее реализованных компонентов.