1734 lines
47 KiB
Markdown
1734 lines
47 KiB
Markdown
# Build 060.7 — REST Trade Adapter
|
||
|
||
**Engineering Migration Report**
|
||
|
||
---
|
||
|
||
# Контроль документа
|
||
|
||
| Свойство | Значение |
|
||
|---|---|
|
||
| Build | 060.7 |
|
||
| Название | REST Trade Adapter |
|
||
| Статус | Завершён |
|
||
| Проект | Dzentra |
|
||
| Подсистема | Market Data Acquisition |
|
||
| Компонент | Trades Feed / Time & Sales |
|
||
| Версия документа | 1.0 |
|
||
| Дата завершения | 2026-07-19 |
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
После завершения Build 060.6 подсистема получения исторических сделок уже содержала полностью сформированный конвейер преобразования транспортных данных.
|
||
|
||
К этому моменту были реализованы:
|
||
|
||
- транспортная модель агрегированной сделки REST API;
|
||
- проверка структуры REST-документа;
|
||
- parser транспортной модели;
|
||
- проверка корректности значений;
|
||
- mapper транспортной модели в Canonical Trade.
|
||
|
||
После завершения предыдущего этапа полный pipeline имел следующий вид:
|
||
|
||
```text
|
||
REST JSON
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[DzengiRestAggTrade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Несмотря на завершённость всех отдельных компонентов, в архитектуре отсутствовал единый публичный интерфейс, объединяющий их в последовательную операцию обработки документа.
|
||
|
||
Каждый вызывающий компонент должен был самостоятельно помнить:
|
||
|
||
- какую функцию вызвать первой;
|
||
- какую второй;
|
||
- какой тип возвращает каждая стадия;
|
||
- какой порядок обработки является корректным.
|
||
|
||
Подобная схема противоречит одной из основных целей Acquisition Layer — предоставить простой и безопасный интерфейс обработки рыночных данных.
|
||
|
||
Следовательно возникла необходимость в отдельном компоненте, который не реализует новую бизнес-логику, а исключительно объединяет уже существующие стадии обработки.
|
||
|
||
Именно эту задачу решает Build 060.7.
|
||
|
||
В рамках данного Build реализуется исключительно REST Trade Adapter.
|
||
|
||
Build намеренно **не включает**:
|
||
|
||
- HTTP client;
|
||
- REST endpoint;
|
||
- получение документа;
|
||
- REST polling;
|
||
- Feed;
|
||
- Registry;
|
||
- Runtime Integration;
|
||
- Acquisition Service;
|
||
- Time & Sales Feed;
|
||
- объединение REST и WebSocket сделок;
|
||
- хранение истории;
|
||
- дедупликацию;
|
||
- сортировку;
|
||
- агрегацию;
|
||
- production-интеграцию.
|
||
|
||
Все перечисленные задачи относятся к последующим Build.
|
||
|
||
---
|
||
|
||
# Архитектурный контекст
|
||
|
||
Во всей подсистеме Market Data Acquisition используется единый принцип построения pipeline.
|
||
|
||
Каждый слой выполняет только одну строго определённую задачу.
|
||
|
||
```text
|
||
Transport
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Canonical Model
|
||
```
|
||
|
||
Такой подход уже используется для:
|
||
|
||
- Instrument;
|
||
- REST Quote;
|
||
- WebSocket Quote;
|
||
- REST Candle;
|
||
- WebSocket Candle.
|
||
|
||
Однако для REST Trades отсутствовал последний объединяющий уровень.
|
||
|
||
Вызов каждого слоя выполнялся вручную.
|
||
|
||
Build 060.7 вводит единый Adapter Layer, который становится официальной точкой входа в REST Trade Pipeline.
|
||
|
||
После завершения Build архитектура принимает следующий вид:
|
||
|
||
```text
|
||
REST JSON
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Trade Adapter
|
||
|
||
│
|
||
|
||
├──────── Parser
|
||
|
||
├──────── Value Validation
|
||
|
||
└──────── Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Таким образом Adapter становится исключительно координатором уже существующих компонентов.
|
||
|
||
Сам Adapter не содержит собственной бизнес-логики.
|
||
|
||
---
|
||
|
||
# Исходное состояние
|
||
|
||
До начала Build 060.7 проект уже содержал следующие компоненты.
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
|
||
DzengiRestAggTrade
|
||
|
||
Trade
|
||
|
||
validate_rest_agg_trades_schema()
|
||
|
||
parse_rest_agg_trades()
|
||
|
||
validate_rest_agg_trade_values()
|
||
|
||
map_dzengi_rest_agg_trades_to_trades()
|
||
```
|
||
|
||
Все необходимые стадии обработки документа уже существовали.
|
||
|
||
Отсутствовал только компонент, объединяющий их в единую последовательность.
|
||
|
||
При этом ни один из существующих модулей не должен был изменяться.
|
||
|
||
Это позволяло реализовать Build как полностью additive change.
|
||
|
||
---
|
||
|
||
# Предварительный архитектурный аудит
|
||
|
||
Перед началом реализации был повторно проанализирован существующий код проекта.
|
||
|
||
Проверены следующие файлы:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/parser.py
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/mapper.py
|
||
|
||
app/src/market_data/acquisition/validation/schema.py
|
||
|
||
app/src/market_data/acquisition/validation/values.py
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/websocket.py
|
||
|
||
docs/migrations/build_060_1.md
|
||
|
||
docs/migrations/build_060_2.md
|
||
|
||
docs/migrations/build_060_3.md
|
||
|
||
docs/migrations/build_060_4.md
|
||
|
||
docs/migrations/build_060_5.md
|
||
|
||
docs/migrations/build_060_6.md
|
||
```
|
||
|
||
Кроме анализа кода был повторно выполнен аудит всей архитектуры Acquisition Pipeline.
|
||
|
||
В результате были подтверждены следующие выводы.
|
||
|
||
- Schema Validation уже является самостоятельным архитектурным слоем.
|
||
- Parser отвечает исключительно за построение transport-моделей.
|
||
- Value Validation отвечает исключительно за корректность значений.
|
||
- Mapper отвечает исключительно за построение Canonical Trade.
|
||
- Ни один из существующих компонентов не должен изменять собственную ответственность.
|
||
- Новый компонент должен выполнять исключительно композицию существующих этапов.
|
||
|
||
Именно эти выводы легли в основу проектирования Build 060.7.
|
||
|
||
---
|
||
|
||
# Архитектурная задача Build
|
||
|
||
Главная задача Build заключается не в добавлении новой функциональности.
|
||
|
||
Все необходимые операции обработки документа уже существуют.
|
||
|
||
Задача Build состоит в создании единой точки входа в REST Trade Pipeline.
|
||
|
||
После завершения Build вызывающий код должен работать только с одной публичной функцией:
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document(...)
|
||
```
|
||
|
||
Внутри неё последовательно выполняются:
|
||
|
||
```text
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
```
|
||
|
||
При этом вызывающий код полностью освобождается от знания внутренней структуры pipeline.
|
||
|
||
Такое решение уменьшает связанность компонентов и существенно упрощает дальнейшее сопровождение системы.
|
||
|
||
# Рассмотренные архитектурные решения
|
||
|
||
Перед началом реализации Build было рассмотрено несколько вариантов построения REST Trade Adapter.
|
||
|
||
Основная задача заключалась не в написании кода, а в определении его архитектурной ответственности.
|
||
|
||
Именно на этом этапе было принято несколько решений, которые впоследствии стали архитектурными инвариантами всей подсистемы Acquisition.
|
||
|
||
---
|
||
|
||
## Вариант 1
|
||
|
||
Разместить всю последовательность обработки непосредственно внутри будущего REST Source.
|
||
|
||
Например:
|
||
|
||
```text
|
||
HTTP
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
JSON
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Trade
|
||
```
|
||
|
||
На первый взгляд подобная схема выглядит простой.
|
||
|
||
Однако после анализа существующей архитектуры данный вариант был отклонён.
|
||
|
||
Причины:
|
||
|
||
- REST Source начинает заниматься обработкой данных;
|
||
- источник данных становится зависимым от транспортной модели;
|
||
- становится невозможно использовать Adapter повторно;
|
||
- нарушается разделение ответственности между слоями получения и обработки данных.
|
||
|
||
В Dzentra источник данных должен отвечать исключительно за получение документа.
|
||
|
||
Вся обработка документа относится к отдельному компоненту.
|
||
|
||
---
|
||
|
||
## Вариант 2
|
||
|
||
Передавать в Adapter исходный REST JSON.
|
||
|
||
Например:
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document(
|
||
document: object,
|
||
symbol="BTC/USDT",
|
||
)
|
||
```
|
||
|
||
При таком подходе Adapter самостоятельно вызывал бы:
|
||
|
||
```text
|
||
validate_rest_agg_trades_schema()
|
||
|
||
↓
|
||
|
||
parse_rest_agg_trades()
|
||
|
||
↓
|
||
|
||
validate_rest_agg_trade_values()
|
||
|
||
↓
|
||
|
||
map_dzengi_rest_agg_trades_to_trades()
|
||
```
|
||
|
||
Именно такой вариант первоначально рассматривался во время проектирования.
|
||
|
||
После повторного анализа существующей архитектуры он был отклонён.
|
||
|
||
Причины:
|
||
|
||
Schema Validation уже является самостоятельным архитектурным уровнем.
|
||
|
||
Если Adapter начинает самостоятельно выполнять проверку структуры документа,
|
||
|
||
он получает сразу две ответственности:
|
||
|
||
- обработку документа;
|
||
- проверку структуры документа.
|
||
|
||
Это противоречит принятой архитектуре Acquisition Layer.
|
||
|
||
Кроме того, подобный подход нарушал бы единообразие остальных pipeline проекта.
|
||
|
||
Следовательно Schema Validation должна оставаться отдельным независимым этапом.
|
||
|
||
---
|
||
|
||
## Утверждённое решение
|
||
|
||
Adapter получает уже проверенный документ:
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
```
|
||
|
||
После этого выполняются только следующие операции:
|
||
|
||
```text
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
```
|
||
|
||
Таким образом Build полностью сохраняет разделение ответственности между слоями.
|
||
|
||
---
|
||
|
||
# Почему Adapter не выполняет Schema Validation
|
||
|
||
Во время проектирования данный вопрос обсуждался отдельно.
|
||
|
||
На первый взгляд логично было бы сделать Adapter полностью автономным.
|
||
|
||
То есть предоставить ему возможность принимать произвольный REST документ.
|
||
|
||
Однако после анализа существующего проекта было подтверждено,
|
||
|
||
что подобное решение нарушило бы уже сформированную архитектуру.
|
||
|
||
К моменту начала Build в системе уже существовали независимые функции:
|
||
|
||
```python
|
||
validate_quote_schema()
|
||
|
||
validate_candle_schema()
|
||
|
||
validate_rest_agg_trades_schema()
|
||
|
||
validate_websocket_ohlc_schema()
|
||
```
|
||
|
||
Все они образуют самостоятельный слой Schema Validation.
|
||
|
||
Следовательно Adapter не должен дублировать их ответственность.
|
||
|
||
После Build 060.7 последовательность обработки выглядит следующим образом:
|
||
|
||
```text
|
||
REST Document Source
|
||
|
||
↓
|
||
|
||
Schema Validation
|
||
|
||
↓
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
↓
|
||
|
||
REST Trade Adapter
|
||
```
|
||
|
||
Такое разделение полностью соответствует архитектуре остальных компонентов проекта.
|
||
|
||
---
|
||
|
||
# Почему Adapter реализован функцией
|
||
|
||
Во время проектирования первоначально предполагалось реализовать Adapter как отдельный класс.
|
||
|
||
Например:
|
||
|
||
```python
|
||
class DzengiRestTradeAdapter:
|
||
```
|
||
|
||
Такой подход выглядит привычным для многих проектов.
|
||
|
||
Однако после анализа существующего кода Acquisition Layer возник вопрос:
|
||
|
||
имеет ли Adapter собственное состояние?
|
||
|
||
Ответ оказался отрицательным.
|
||
|
||
Adapter:
|
||
|
||
- не хранит состояние;
|
||
- не содержит конфигурацию;
|
||
- не управляет ресурсами;
|
||
- не использует Dependency Injection;
|
||
- не предоставляет расширяемый интерфейс;
|
||
- не содержит полиморфизма.
|
||
|
||
Фактически единственной его задачей является последовательный вызов уже существующих функций.
|
||
|
||
Следовательно класс не предоставляет никаких преимуществ.
|
||
|
||
Использование класса лишь увеличило бы объём кода и усложнило архитектуру.
|
||
|
||
Поэтому было принято решение отказаться от него.
|
||
|
||
---
|
||
|
||
# Новый архитектурный принцип
|
||
|
||
Именно во время реализации Build 060.7 был окончательно сформулирован новый архитектурный принцип проекта.
|
||
|
||
Он выглядит следующим образом.
|
||
|
||
```text
|
||
Stateful
|
||
|
||
↓
|
||
|
||
Class
|
||
```
|
||
|
||
```text
|
||
Stateless
|
||
|
||
↓
|
||
|
||
Function
|
||
```
|
||
|
||
Именно этот принцип теперь применяется при проектировании новых компонентов Market Data Acquisition.
|
||
|
||
---
|
||
|
||
# Почему выбран функциональный API
|
||
|
||
После отказа от класса оставалось определить форму публичного интерфейса.
|
||
|
||
Было принято решение использовать одну функцию:
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document()
|
||
```
|
||
|
||
Причины такого решения.
|
||
|
||
Во-первых,
|
||
|
||
весь Acquisition Layer уже использует функциональный стиль.
|
||
|
||
Например:
|
||
|
||
```python
|
||
parse_rest_agg_trades()
|
||
|
||
validate_rest_agg_trade_values()
|
||
|
||
map_dzengi_rest_agg_trades_to_trades()
|
||
```
|
||
|
||
Adapter становится естественным продолжением этой цепочки.
|
||
|
||
Во-вторых,
|
||
|
||
функция значительно проще тестируется.
|
||
|
||
Она полностью детерминирована.
|
||
|
||
При одинаковых входных данных всегда возвращает одинаковый результат.
|
||
|
||
В-третьих,
|
||
|
||
отсутствует необходимость создавать экземпляры класса,
|
||
|
||
что делает публичный API проще для использования.
|
||
|
||
---
|
||
|
||
# Почему файл называется rest_trade_adapter.py
|
||
|
||
Во время проектирования обсуждалось несколько вариантов имени файла.
|
||
|
||
Например:
|
||
|
||
```text
|
||
trade_adapter.py
|
||
```
|
||
|
||
или
|
||
|
||
```text
|
||
dzengi_rest_trade_adapter.py
|
||
```
|
||
|
||
После анализа структуры проекта было принято решение использовать:
|
||
|
||
```text
|
||
rest_trade_adapter.py
|
||
```
|
||
|
||
Причины.
|
||
|
||
Каталог уже содержит информацию о конкретной бирже:
|
||
|
||
```text
|
||
adapters/dzengi/
|
||
```
|
||
|
||
Следовательно повторять слово:
|
||
|
||
```text
|
||
dzengi
|
||
```
|
||
|
||
в имени файла не требуется.
|
||
|
||
Кроме того,
|
||
|
||
имя:
|
||
|
||
```text
|
||
trade_adapter.py
|
||
```
|
||
|
||
оказалось слишком общим.
|
||
|
||
В будущем в проекте могут появиться:
|
||
|
||
- websocket_trade_adapter.py;
|
||
- replay_trade_adapter.py;
|
||
- simulated_trade_adapter.py.
|
||
|
||
Название:
|
||
|
||
```text
|
||
rest_trade_adapter.py
|
||
```
|
||
|
||
однозначно определяет назначение компонента и соответствует принятому правилу уникальности имён файлов.
|
||
|
||
---
|
||
|
||
# Новый публичный API
|
||
|
||
Build 060.7 вводит единственную публичную функцию.
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document()
|
||
```
|
||
|
||
Назначение функции:
|
||
|
||
- принимает документ, уже прошедший Schema Validation;
|
||
- запускает Parser;
|
||
- выполняет Value Validation;
|
||
- выполняет Mapper;
|
||
- возвращает immutable tuple Canonical Trade.
|
||
|
||
Сигнатура имеет следующий вид:
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Внутренняя реализация полностью скрыта от вызывающего кода.
|
||
|
||
После завершения Build именно эта функция становится официальной точкой входа REST Trade Pipeline.
|
||
|
||
# Семантика REST Trade Adapter
|
||
|
||
После завершения Build 060.7 Adapter становится единственной точкой композиции всех ранее реализованных компонентов обработки REST Trade.
|
||
|
||
При этом сам Adapter намеренно остаётся максимально простым.
|
||
|
||
Его ответственность ограничивается исключительно последовательным вызовом уже существующих функций.
|
||
|
||
Полная последовательность обработки выглядит следующим образом.
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
parse_rest_agg_trades()
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[DzengiRestAggTrade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
validate_rest_agg_trade_values()
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[DzengiRestAggTrade]
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
map_dzengi_rest_agg_trades_to_trades()
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Adapter не выполняет никаких дополнительных преобразований.
|
||
|
||
Он лишь организует последовательность уже существующих этапов.
|
||
|
||
---
|
||
|
||
# Почему Adapter не содержит собственной логики
|
||
|
||
Во время проектирования обсуждался естественный вопрос.
|
||
|
||
Если Adapter является самостоятельным компонентом,
|
||
|
||
не должен ли он выполнять хотя бы часть бизнес-логики?
|
||
|
||
Ответ оказался отрицательным.
|
||
|
||
Все необходимые операции уже реализованы в предыдущих Build.
|
||
|
||
Повторное выполнение каких-либо проверок внутри Adapter привело бы к:
|
||
|
||
- дублированию кода;
|
||
- нарушению принципа Single Responsibility;
|
||
- усложнению сопровождения;
|
||
- появлению двух различных реализаций одной и той же логики.
|
||
|
||
Поэтому Adapter сознательно остаётся максимально "тонким".
|
||
|
||
Его задача — исключительно композиция.
|
||
|
||
---
|
||
|
||
# Почему Adapter не перехватывает исключения
|
||
|
||
Во время реализации рассматривался вариант,
|
||
|
||
при котором Adapter преобразовывал бы ошибки всех нижележащих компонентов в единый тип исключения.
|
||
|
||
Например:
|
||
|
||
```text
|
||
TradeAdapterError
|
||
```
|
||
|
||
После анализа архитектуры данный вариант был отклонён.
|
||
|
||
Причины.
|
||
|
||
Каждый уровень Acquisition Pipeline уже обладает собственным специализированным типом исключений.
|
||
|
||
```text
|
||
TradeSchemaError
|
||
|
||
↓
|
||
|
||
TradeParseError
|
||
|
||
↓
|
||
|
||
TradeValueError
|
||
|
||
↓
|
||
|
||
TradeMappingError
|
||
```
|
||
|
||
Если Adapter начнёт преобразовывать их в единый тип,
|
||
|
||
будет потеряна информация о том,
|
||
|
||
на каком именно этапе возникла ошибка.
|
||
|
||
Это существенно ухудшит диагностику системы.
|
||
|
||
Поэтому Adapter намеренно не перехватывает исключения.
|
||
|
||
Все ошибки пробрасываются вызывающему компоненту без изменений.
|
||
|
||
---
|
||
|
||
# Почему Adapter не изменяет порядок выполнения этапов
|
||
|
||
Последовательность вызова компонентов также обсуждалась отдельно.
|
||
|
||
Теоретически можно было изменить порядок:
|
||
|
||
```text
|
||
Parser
|
||
|
||
↓
|
||
|
||
Mapper
|
||
|
||
↓
|
||
|
||
Validation
|
||
```
|
||
|
||
или
|
||
|
||
```text
|
||
Validation
|
||
|
||
↓
|
||
|
||
Parser
|
||
```
|
||
|
||
Оба варианта были отклонены.
|
||
|
||
Причины очевидны.
|
||
|
||
Parser невозможно выполнить до проверки структуры документа.
|
||
|
||
Mapper невозможно выполнить до проверки корректности значений.
|
||
|
||
Следовательно единственная корректная последовательность выглядит следующим образом.
|
||
|
||
```text
|
||
Schema Validation
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
```
|
||
|
||
Никакой другой порядок не соответствует архитектуре Acquisition Layer.
|
||
|
||
---
|
||
|
||
# Почему Adapter возвращает tuple
|
||
|
||
Все предыдущие Build закрепили использование immutable-коллекций.
|
||
|
||
Поэтому Adapter также возвращает:
|
||
|
||
```python
|
||
tuple[Trade]
|
||
```
|
||
|
||
Использование списка не рассматривалось.
|
||
|
||
Причины.
|
||
|
||
Immutable-коллекция:
|
||
|
||
- безопаснее при передаче между слоями;
|
||
- исключает случайное изменение результата;
|
||
- соответствует остальным компонентам Acquisition;
|
||
- делает контракт функции полностью предсказуемым.
|
||
|
||
Таким образом Build полностью сохраняет существующий архитектурный стиль проекта.
|
||
|
||
---
|
||
|
||
# Что намеренно не делает Adapter
|
||
|
||
Build 060.7 специально не добавляет никакой дополнительной обработки данных.
|
||
|
||
Adapter намеренно:
|
||
|
||
не выполняет HTTP-запросы;
|
||
|
||
не получает REST-документ;
|
||
|
||
не выполняет Schema Validation;
|
||
|
||
не сортирует сделки;
|
||
|
||
не удаляет дубликаты;
|
||
|
||
не объединяет REST и WebSocket данные;
|
||
|
||
не агрегирует сделки;
|
||
|
||
не рассчитывает статистику;
|
||
|
||
не анализирует последовательность сделок;
|
||
|
||
не создаёт свечи;
|
||
|
||
не взаимодействует с Runtime;
|
||
|
||
не взаимодействует с Feed;
|
||
|
||
не взаимодействует с Registry;
|
||
|
||
не сохраняет данные;
|
||
|
||
не ведёт журнал обработки.
|
||
|
||
Все перечисленные задачи относятся к другим архитектурным слоям и намеренно исключены из области ответственности Adapter.
|
||
|
||
---
|
||
|
||
# Изменённые файлы
|
||
|
||
В рамках Build 060.7 был добавлен один новый файл проекта.
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/rest_trade_adapter.py
|
||
```
|
||
|
||
Также был добавлен новый набор unit-тестов.
|
||
|
||
```text
|
||
app/tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py
|
||
```
|
||
|
||
Кроме подготовки инженерной документации Build никакие другие файлы проекта не изменялись.
|
||
|
||
Build полностью соответствует принятому принципу **Local Additive Change**.
|
||
|
||
---
|
||
|
||
# Изменения в rest_trade_adapter.py
|
||
|
||
Новый файл содержит единственную публичную функцию.
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document()
|
||
```
|
||
|
||
Внутри неё отсутствует какая-либо собственная логика обработки.
|
||
|
||
Функция последовательно вызывает:
|
||
|
||
```python
|
||
parse_rest_agg_trades()
|
||
|
||
↓
|
||
|
||
validate_rest_agg_trade_values()
|
||
|
||
↓
|
||
|
||
map_dzengi_rest_agg_trades_to_trades()
|
||
```
|
||
|
||
После завершения последнего этапа результат сразу возвращается вызывающему компоненту.
|
||
|
||
Таким образом Adapter становится исключительно координационным уровнем REST Trade Pipeline.
|
||
|
||
---
|
||
|
||
# Изменения в unit-тестах
|
||
|
||
Для нового Adapter создан отдельный файл тестов.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py
|
||
```
|
||
|
||
Это соответствует принятому соглашению проекта,
|
||
|
||
согласно которому каждый самостоятельный компонент Acquisition Layer имеет собственный независимый набор unit-тестов.
|
||
|
||
Существующие тесты:
|
||
|
||
- Parser;
|
||
- Value Validation;
|
||
- Mapper;
|
||
|
||
не изменялись.
|
||
|
||
Build полностью additive.
|
||
|
||
---
|
||
|
||
# Проверяемые сценарии
|
||
|
||
Новый набор тестов проверяет исключительно ответственность Adapter.
|
||
|
||
Подтверждаются следующие сценарии.
|
||
|
||
- успешная обработка корректного документа;
|
||
- корректная передача symbol в Mapper;
|
||
- корректная обработка пустого документа;
|
||
- корректное пробрасывание исключений нижележащих компонентов.
|
||
|
||
Тем самым подтверждается,
|
||
|
||
что Adapter не изменяет семантику работы уже существующих этапов обработки.
|
||
|
||
При этом логика Parser, Validation и Mapper продолжает проверяться их собственными независимыми тестами.
|
||
|
||
# Что намеренно не изменялось
|
||
|
||
Build 060.7 специально не изменяет существующие компоненты Acquisition Pipeline.
|
||
|
||
Без изменений остаются:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/parser.py
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/mapper.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 намеренно не включает:
|
||
|
||
- REST client;
|
||
- выполнение HTTP-запросов;
|
||
- получение исторических сделок;
|
||
- REST polling;
|
||
- объединение REST и WebSocket Trade;
|
||
- Time & Sales Feed;
|
||
- Runtime Integration;
|
||
- хранение истории сделок;
|
||
- кэширование;
|
||
- дедупликацию;
|
||
- сортировку;
|
||
- агрегацию;
|
||
- вычисление аналитики;
|
||
- построение свечей;
|
||
- взаимодействие с Trading Layer.
|
||
|
||
Все перечисленные задачи реализуются отдельными Build согласно утверждённой дорожной карте.
|
||
|
||
Таким образом Build 060.7 остаётся полностью локальным и не выходит за пределы собственной архитектурной ответственности.
|
||
|
||
---
|
||
|
||
# Проверка компиляции
|
||
|
||
После завершения реализации была выполнена проверка компиляции проекта.
|
||
|
||
Команда выполнялась из каталога:
|
||
|
||
```text
|
||
~/vsprojects/dzentra_bot/app
|
||
```
|
||
|
||
При активированном виртуальном окружении:
|
||
|
||
```bash
|
||
source .venv/bin/activate
|
||
```
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
python -m compileall src
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
успешно
|
||
```
|
||
|
||
Все модули проекта успешно скомпилированы.
|
||
|
||
Ошибок синтаксиса не обнаружено.
|
||
|
||
Build не нарушил корректность структуры проекта.
|
||
|
||
---
|
||
|
||
# Проверка локальных unit-тестов
|
||
|
||
После завершения реализации Adapter были выполнены специализированные unit-тесты.
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest \
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py \
|
||
-q
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
4 passed in 0.02s
|
||
```
|
||
|
||
Проверки подтвердили:
|
||
|
||
- корректную последовательность вызова компонентов;
|
||
- корректную передачу параметра symbol;
|
||
- корректную обработку пустого документа;
|
||
- корректное пробрасывание исключений.
|
||
|
||
Ни одна существующая проверка проекта не была нарушена.
|
||
|
||
---
|
||
|
||
# Полный regression suite
|
||
|
||
После завершения Build выполнен полный набор тестов проекта.
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
1096 passed in 2.59s
|
||
```
|
||
|
||
Регрессий не обнаружено.
|
||
|
||
Все ранее реализованные Build продолжают работать без изменений.
|
||
|
||
Это подтверждает,
|
||
|
||
что Build 060.7 полностью соответствует принципу **Local Additive Change**.
|
||
|
||
---
|
||
|
||
# Проверка форматирования
|
||
|
||
После завершения работы выполнена команда:
|
||
|
||
```bash
|
||
git diff --check
|
||
```
|
||
|
||
Вывод отсутствует.
|
||
|
||
Это подтверждает отсутствие:
|
||
|
||
- trailing whitespace;
|
||
- лишних пробелов;
|
||
- нарушений форматирования;
|
||
- ошибок оформления diff.
|
||
|
||
Build соответствует принятым требованиям оформления исходного кода.
|
||
|
||
---
|
||
|
||
# Контроль размещения новой функциональности
|
||
|
||
После завершения Build вся новая функциональность сосредоточена только в предназначенных для неё компонентах.
|
||
|
||
Adapter расположен исключительно в:
|
||
|
||
```text
|
||
src/market_data/acquisition/adapters/dzengi/rest_trade_adapter.py
|
||
```
|
||
|
||
Unit-тесты расположены исключительно в:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py
|
||
```
|
||
|
||
Другие подсистемы проекта Build не затрагивает.
|
||
|
||
Архитектурная изоляция полностью сохранена.
|
||
|
||
---
|
||
|
||
# Состояние Git
|
||
|
||
После завершения Build выполнена команда:
|
||
|
||
```bash
|
||
git status
|
||
```
|
||
|
||
Для Build 060.7 зафиксированы изменения:
|
||
|
||
```text
|
||
added:
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/rest_trade_adapter.py
|
||
|
||
app/tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py
|
||
|
||
untracked:
|
||
|
||
docs/migrations/build_060_7.md
|
||
```
|
||
|
||
Ветка разработки:
|
||
|
||
```text
|
||
main
|
||
```
|
||
|
||
опережает `origin/main`.
|
||
|
||
Данное состояние соответствует текущему процессу разработки и не связано с архитектурой Build.
|
||
|
||
---
|
||
|
||
# Фактический diff
|
||
|
||
В рамках Build добавлен новый компонент:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/rest_trade_adapter.py
|
||
```
|
||
|
||
Публичный API Build:
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document()
|
||
```
|
||
|
||
Функция объединяет:
|
||
|
||
```text
|
||
parse_rest_agg_trades()
|
||
|
||
↓
|
||
|
||
validate_rest_agg_trade_values()
|
||
|
||
↓
|
||
|
||
map_dzengi_rest_agg_trades_to_trades()
|
||
```
|
||
|
||
Кроме того,
|
||
|
||
добавлен отдельный набор unit-тестов:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_rest_trade_adapter.py
|
||
```
|
||
|
||
Все изменения являются полностью additive.
|
||
|
||
Никакое существующее поведение системы не изменялось.
|
||
|
||
---
|
||
|
||
# Архитектурный результат
|
||
|
||
После завершения Build 060.7 REST Trade Pipeline впервые получает официальную единую точку входа.
|
||
|
||
Полная архитектура теперь выглядит следующим образом.
|
||
|
||
```text
|
||
REST JSON
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Trade Adapter
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Каждый слой обладает собственной зоной ответственности.
|
||
|
||
Ни один слой не выполняет задачи соседнего.
|
||
|
||
Adapter становится исключительно координатором существующих компонентов.
|
||
|
||
Это полностью соответствует принципам:
|
||
|
||
- Single Responsibility;
|
||
- Local Additive Change;
|
||
- Transport Isolation;
|
||
- Canonical Model Separation.
|
||
|
||
Кроме того,
|
||
|
||
Build 060.7 окончательно закрепляет разделение между:
|
||
|
||
- получением документа;
|
||
- обработкой документа;
|
||
- построением предметной модели.
|
||
|
||
Именно это разделение станет основой следующего этапа развития REST Trade Pipeline.
|
||
|
||
# Влияние на последующие Build
|
||
|
||
Build 060.7 завершает формирование логического слоя обработки REST Trade Document.
|
||
|
||
После его окончания все последующие компоненты Acquisition Layer могут использовать единый публичный интерфейс:
|
||
|
||
```python
|
||
adapt_rest_agg_trades_document()
|
||
```
|
||
|
||
При этом они полностью освобождаются от необходимости знать внутреннее устройство Pipeline.
|
||
|
||
Следующие Build будут работать исключительно через данный Adapter.
|
||
|
||
Это позволяет изменять внутреннюю реализацию Parser, Validation или Mapper без изменения внешнего API обработки документа.
|
||
|
||
Таким образом Adapter становится стабильной границей между верхними и нижними слоями Acquisition.
|
||
|
||
---
|
||
|
||
# Критерии завершения
|
||
|
||
Build 060.7 считается полностью завершённым, поскольку:
|
||
|
||
- проведён повторный архитектурный аудит существующего Pipeline;
|
||
- реализован отдельный REST Trade Adapter;
|
||
- определена единственная публичная точка входа обработки документа;
|
||
- сохранено разделение ответственности между Schema Validation, Parser, Value Validation и Mapper;
|
||
- Adapter не содержит собственной бизнес-логики;
|
||
- Adapter не выполняет HTTP-запросы;
|
||
- Adapter не выполняет Schema Validation;
|
||
- Adapter не изменяет порядок обработки данных;
|
||
- Adapter не сортирует сделки;
|
||
- Adapter не удаляет дубликаты;
|
||
- Adapter не агрегирует данные;
|
||
- Adapter не изменяет транспортные модели;
|
||
- Adapter не перехватывает исключения нижележащих компонентов;
|
||
- compile-проверка успешно пройдена;
|
||
- локальные unit-тесты успешно пройдены;
|
||
- полный regression suite успешно пройден;
|
||
- проверка форматирования успешно пройдена;
|
||
- scope Build не расширен.
|
||
|
||
---
|
||
|
||
# Архитектурные инварианты
|
||
|
||
После завершения Build 060.7 следующие свойства REST Trade Pipeline считаются архитектурным контрактом проекта.
|
||
|
||
Изменение любого из перечисленных инвариантов требует отдельного архитектурного решения.
|
||
|
||
---
|
||
|
||
## Инвариант 1
|
||
|
||
Schema Validation остаётся самостоятельным слоем Acquisition Pipeline.
|
||
|
||
Никакие Adapter не должны выполнять проверку структуры документа самостоятельно.
|
||
|
||
Полная последовательность всегда начинается с отдельного этапа Schema Validation.
|
||
|
||
---
|
||
|
||
## Инвариант 2
|
||
|
||
REST Trade Adapter получает только уже проверенный документ.
|
||
|
||
Тип входного параметра:
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
```
|
||
|
||
Использование необработанного REST JSON внутри Adapter не допускается.
|
||
|
||
---
|
||
|
||
## Инвариант 3
|
||
|
||
REST Trade Adapter отвечает исключительно за композицию существующих компонентов.
|
||
|
||
Он не содержит собственной бизнес-логики.
|
||
|
||
---
|
||
|
||
## Инвариант 4
|
||
|
||
REST Trade Adapter не изменяет последовательность обработки.
|
||
|
||
Pipeline всегда имеет следующий вид:
|
||
|
||
```text
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
```
|
||
|
||
Изменение порядка выполнения данных этапов не допускается.
|
||
|
||
---
|
||
|
||
## Инвариант 5
|
||
|
||
REST Trade Adapter не перехватывает исключения нижележащих компонентов.
|
||
|
||
Ошибки каждого уровня должны сохранять собственный специализированный тип.
|
||
|
||
---
|
||
|
||
## Инвариант 6
|
||
|
||
REST Trade Adapter не создаёт предметные объекты самостоятельно.
|
||
|
||
Создание экземпляров:
|
||
|
||
```text
|
||
Trade
|
||
```
|
||
|
||
остаётся исключительной ответственностью Mapper.
|
||
|
||
---
|
||
|
||
## Инвариант 7
|
||
|
||
REST Trade Adapter не зависит от механизма получения документа.
|
||
|
||
Он не знает:
|
||
|
||
- каким способом был получен REST JSON;
|
||
- из какого HTTP-клиента он поступил;
|
||
- выполнялся ли запрос повторно;
|
||
- использовалось ли кэширование.
|
||
|
||
Эти вопросы относятся исключительно к Source Layer.
|
||
|
||
---
|
||
|
||
## Инвариант 8
|
||
|
||
Stateless-компоненты реализуются функциями.
|
||
|
||
Если компонент:
|
||
|
||
- не хранит состояние;
|
||
- не содержит конфигурации;
|
||
- не управляет жизненным циклом;
|
||
- не требует Dependency Injection;
|
||
- не реализует полиморфизм,
|
||
|
||
он должен быть реализован функцией.
|
||
|
||
---
|
||
|
||
## Инвариант 9
|
||
|
||
Stateful-компоненты реализуются классами.
|
||
|
||
Класс используется только в случаях, когда компонент:
|
||
|
||
- хранит состояние;
|
||
- управляет ресурсами;
|
||
- содержит конфигурацию;
|
||
- инкапсулирует зависимости;
|
||
- предоставляет расширяемый интерфейс.
|
||
|
||
Использование класса при отсутствии состояния считается нарушением архитектурного стиля проекта.
|
||
|
||
---
|
||
|
||
## Инвариант 10
|
||
|
||
REST Trade Adapter является единственной официальной точкой запуска полного REST Trade Pipeline.
|
||
|
||
Все последующие компоненты системы должны использовать именно его.
|
||
|
||
Самостоятельный последовательный вызов:
|
||
|
||
```text
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
```
|
||
|
||
за пределами Adapter не допускается.
|
||
|
||
---
|
||
|
||
# Итог
|
||
|
||
**Build 060.7 завершён успешно.**
|
||
|
||
Текущее состояние REST Trade Pipeline:
|
||
|
||
```text
|
||
Transport Model — реализована
|
||
|
||
Schema Validation — реализована
|
||
|
||
Parser — реализован
|
||
|
||
Value Validation — реализована
|
||
|
||
Mapper — реализован
|
||
|
||
REST Trade Adapter — реализован
|
||
|
||
Canonical Trade — используется
|
||
|
||
Compile check — успешно
|
||
|
||
Target tests — 4 passed
|
||
|
||
Full regression suite — 1096 passed
|
||
|
||
Whitespace check — успешно
|
||
|
||
Production Integration — намеренно не выполнялась
|
||
```
|
||
|
||
После завершения Build 060.7 подсистема получения исторических сделок получила единый координационный слой обработки документа.
|
||
|
||
Вся логика преобразования REST-документа в канонические сделки теперь доступна через единый публичный интерфейс, при этом все ранее реализованные компоненты сохранили независимость и собственную архитектурную ответственность.
|
||
|
||
---
|
||
|
||
# Следующий этап
|
||
|
||
Следующим этапом развития серии Build 060 становится:
|
||
|
||
```text
|
||
Build 060.8 — REST Trades Document Source
|
||
```
|
||
|
||
На данном этапе будет реализован компонент, отвечающий исключительно за получение документа из REST API и его структурную проверку.
|
||
|
||
После завершения Build 060.8 полный REST Pipeline примет следующий вид:
|
||
|
||
```text
|
||
HTTP Client
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST API
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
JSON
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Trade Adapter
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Таким образом будут окончательно разделены три независимые архитектурные области:
|
||
|
||
- получение документа;
|
||
- обработка документа;
|
||
- построение канонической модели.
|
||
|
||
После этого REST-подсистема получения сделок будет полностью подготовлена к построению **Trades Feed (Time & Sales)** и последующему объединению исторических REST-сделок с потоком WebSocket в рамках единого конвейера обработки рыночных данных. |