525 lines
15 KiB
Markdown
525 lines
15 KiB
Markdown
# Build 059.8 — Unified WebSocket Adapter
|
||
|
||
**Проект:** Dzentra
|
||
**Подсистема:** Market Data Acquisition
|
||
**Этап:** 059.8
|
||
**Статус:** Completed
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
Завершить формирование слоя WebSocket-адаптеров, добавив единую точку входа для обработки входящих сообщений Dzengi WebSocket без изменения существующих специализированных адаптеров.
|
||
|
||
Build 059.8 объединяет ранее реализованные адаптеры Quote и OHLC в единый маршрутизатор сообщений, который определяет тип входящего документа и передаёт его соответствующему обработчику.
|
||
|
||
В результате последующие уровни системы больше не должны знать о конкретных форматах сообщений Dzengi WebSocket.
|
||
|
||
---
|
||
|
||
# Причина изменения
|
||
|
||
После завершения Build 059.7 архитектура содержала два полностью независимых специализированных адаптера:
|
||
|
||
- DzengiWebSocketQuoteAdapter
|
||
- DzengiWebSocketOhlcAdapter
|
||
|
||
Каждый адаптер имел собственный pipeline обработки данных:
|
||
|
||
```text
|
||
Schema Validation
|
||
↓
|
||
Parser
|
||
↓
|
||
Value Validation
|
||
↓
|
||
Mapper
|
||
↓
|
||
Internal Model
|
||
```
|
||
|
||
Оба адаптера обладали одинаковым публичным контрактом:
|
||
|
||
```python
|
||
map_message(document, received_at=None)
|
||
```
|
||
|
||
Однако внешний код должен был самостоятельно определять тип входящего сообщения и выбирать необходимый адаптер.
|
||
|
||
Подобная логика выбора типа сообщения не относится к ответственности Runtime или транспортного уровня.
|
||
|
||
Она должна существовать непосредственно в слое адаптеров.
|
||
|
||
Build 059.8 устраняет данную проблему.
|
||
|
||
---
|
||
|
||
# Реализованные изменения
|
||
|
||
Изменены файлы:
|
||
|
||
```text
|
||
src/market_data/acquisition/adapters/dzengi/websocket.py
|
||
src/market_data/acquisition/exceptions.py
|
||
```
|
||
|
||
Добавлен новый файл:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_unified_adapter.py
|
||
```
|
||
|
||
---
|
||
|
||
# Новый компонент
|
||
|
||
Добавлен новый класс:
|
||
|
||
```text
|
||
DzengiUnifiedWebSocketAdapter
|
||
```
|
||
|
||
Назначение класса:
|
||
|
||
- получение произвольного декодированного сообщения Dzengi WebSocket;
|
||
- определение типа сообщения;
|
||
- выбор специализированного адаптера;
|
||
- возврат внутренней модели системы.
|
||
|
||
Unified Adapter не содержит собственной логики обработки рыночных данных.
|
||
|
||
Он выполняет исключительно маршрутизацию сообщений.
|
||
|
||
---
|
||
|
||
# Архитектура после Build 059.8
|
||
|
||
После завершения Build структура слоя WebSocket имеет следующий вид:
|
||
|
||
```text
|
||
Raw WebSocket Message
|
||
│
|
||
▼
|
||
DzengiUnifiedWebSocketAdapter
|
||
│
|
||
┌───────────────┴───────────────┐
|
||
│ │
|
||
▼ ▼
|
||
DzengiWebSocketQuoteAdapter DzengiWebSocketOhlcAdapter
|
||
│ │
|
||
▼ ▼
|
||
Quote CandleCloseEvent
|
||
```
|
||
|
||
Каждый специализированный адаптер полностью сохраняет собственный pipeline.
|
||
|
||
Unified Adapter лишь выбирает необходимый маршрут обработки.
|
||
|
||
---
|
||
|
||
# Маршрутизация сообщений
|
||
|
||
Unified Adapter определяет тип сообщения по структуре входящего документа.
|
||
|
||
## Quote
|
||
|
||
Сообщение котировки определяется по наличию поля:
|
||
|
||
```text
|
||
Payload
|
||
```
|
||
|
||
После определения типа сообщение передаётся в:
|
||
|
||
```text
|
||
DzengiWebSocketQuoteAdapter
|
||
```
|
||
|
||
Дальнейшая обработка полностью выполняется существующим Quote pipeline.
|
||
|
||
---
|
||
|
||
## OHLC
|
||
|
||
Сообщение закрытия свечи определяется по значению:
|
||
|
||
```text
|
||
destination == "ohlc.event"
|
||
```
|
||
|
||
После определения типа сообщение передаётся в:
|
||
|
||
```text
|
||
DzengiWebSocketOhlcAdapter
|
||
```
|
||
|
||
Дальнейшая обработка полностью выполняется существующим OHLC pipeline.
|
||
|
||
---
|
||
|
||
# Отсутствие дублирования логики
|
||
|
||
Unified Adapter не выполняет:
|
||
|
||
- Schema Validation;
|
||
- Parser;
|
||
- Value Validation;
|
||
- Mapping.
|
||
|
||
Вся существующая логика полностью переиспользуется.
|
||
|
||
Build 059.8 не изменяет существующие алгоритмы обработки сообщений.
|
||
|
||
Добавляется исключительно уровень маршрутизации.
|
||
|
||
---
|
||
|
||
# Контракт Unified Adapter
|
||
|
||
Публичный метод:
|
||
|
||
```python
|
||
map_message(
|
||
document,
|
||
*,
|
||
received_at=None,
|
||
)
|
||
```
|
||
|
||
Возвращаемое значение:
|
||
|
||
```text
|
||
Quote
|
||
или
|
||
CandleCloseEvent
|
||
```
|
||
|
||
В зависимости от типа входящего сообщения.
|
||
|
||
Таким образом внешний код получает уже внутреннюю модель системы и не зависит от структуры сообщений Dzengi.
|
||
|
||
---
|
||
|
||
# Передача received_at
|
||
|
||
Оба специализированных адаптера уже поддерживали передачу времени получения сообщения через параметр:
|
||
|
||
```python
|
||
received_at
|
||
```
|
||
|
||
Unified Adapter полностью сохраняет данный контракт.
|
||
|
||
При вызове:
|
||
|
||
```python
|
||
map_message(
|
||
document,
|
||
received_at=received_at,
|
||
)
|
||
```
|
||
|
||
полученное значение без изменений передаётся выбранному специализированному адаптеру.
|
||
|
||
Таким образом единая точка входа не изменяет временные метки и не создаёт новые значения времени самостоятельно.
|
||
|
||
Если параметр не передан, соответствующий специализированный адаптер продолжает использовать собственную существующую логику формирования `received_at`.
|
||
|
||
Build 059.8 не изменяет поведение предыдущих Build.
|
||
|
||
---
|
||
|
||
# Dependency Injection
|
||
|
||
Unified Adapter поддерживает передачу специализированных адаптеров через конструктор.
|
||
|
||
Используются параметры:
|
||
|
||
```python
|
||
quote_adapter
|
||
ohlc_adapter
|
||
```
|
||
|
||
Если адаптеры не переданы, создаются экземпляры по умолчанию:
|
||
|
||
```text
|
||
DzengiWebSocketQuoteAdapter
|
||
DzengiWebSocketOhlcAdapter
|
||
```
|
||
|
||
Подобное решение обеспечивает:
|
||
|
||
- изоляцию Unit Test;
|
||
- возможность последующего расширения;
|
||
- отсутствие жёсткой зависимости от конкретных реализаций.
|
||
|
||
При этом публичный контракт класса остаётся неизменным.
|
||
|
||
---
|
||
|
||
# Новая ошибка маршрутизации
|
||
|
||
Build 059.8 вводит новый тип ошибки:
|
||
|
||
```text
|
||
WebSocketMessageRoutingError
|
||
```
|
||
|
||
Назначение ошибки:
|
||
|
||
определить ситуацию, когда входящее сообщение невозможно отнести ни к одному поддерживаемому типу.
|
||
|
||
Ошибка возникает исключительно на этапе выбора специализированного адаптера.
|
||
|
||
Она не относится к:
|
||
|
||
- транспортному уровню;
|
||
- проверке схемы;
|
||
- parser;
|
||
- validator;
|
||
- mapper.
|
||
|
||
Каждый специализированный адаптер продолжает использовать собственные типы ошибок.
|
||
|
||
Таким образом уровни ответственности остаются полностью разделёнными.
|
||
|
||
---
|
||
|
||
# Контракт обработки ошибок
|
||
|
||
При успешном определении типа сообщения Unified Adapter передаёт управление соответствующему адаптеру.
|
||
|
||
Все исключения специализированного pipeline проходят наружу без изменений.
|
||
|
||
Например:
|
||
|
||
```text
|
||
QuoteValueError
|
||
|
||
QuoteSchemaError
|
||
|
||
QuoteMappingError
|
||
|
||
CandleWebSocketSchemaError
|
||
|
||
CandleWebSocketValueError
|
||
|
||
CandleWebSocketMappingError
|
||
```
|
||
|
||
не перехватываются и не преобразуются.
|
||
|
||
Unified Adapter не изменяет существующую модель обработки ошибок.
|
||
|
||
---
|
||
|
||
# Границы ответственности
|
||
|
||
Unified Adapter отвечает исключительно за:
|
||
|
||
- определение типа сообщения;
|
||
- выбор специализированного адаптера;
|
||
- передачу параметров;
|
||
- возврат внутренней модели.
|
||
|
||
Unified Adapter сознательно не выполняет:
|
||
|
||
- проверку структуры документа;
|
||
- преобразование данных;
|
||
- проверку бизнес-ограничений;
|
||
- преобразование во внутренние модели;
|
||
- взаимодействие с WebSocket-соединением.
|
||
|
||
Все перечисленные обязанности остаются в существующих специализированных компонентах.
|
||
|
||
Подобное разделение соответствует принципу Single Responsibility.
|
||
|
||
---
|
||
|
||
# Поддерживаемые типы сообщений
|
||
|
||
После завершения Build 059.8 поддерживаются:
|
||
|
||
```text
|
||
Quote
|
||
|
||
OHLC Close Event
|
||
```
|
||
|
||
Архитектура допускает дальнейшее расширение без изменения существующего кода Unified Adapter.
|
||
|
||
В последующих Build возможно подключение новых специализированных адаптеров:
|
||
|
||
```text
|
||
Order Book
|
||
|
||
Trades
|
||
|
||
Ticker
|
||
|
||
Orders
|
||
|
||
Positions
|
||
|
||
Account Events
|
||
```
|
||
|
||
при сохранении единой точки входа.
|
||
|
||
---
|
||
|
||
# Unit Test
|
||
|
||
Добавлен новый набор тестов:
|
||
|
||
```text
|
||
test_websocket_unified_adapter.py
|
||
```
|
||
|
||
Проверяются следующие сценарии:
|
||
|
||
- маршрутизация Quote;
|
||
- маршрутизация OHLC;
|
||
- передача received_at в Quote Adapter;
|
||
- передача received_at в OHLC Adapter;
|
||
- обработка неизвестных сообщений.
|
||
|
||
Для проверки неизвестного сообщения используется параметризованный тест, охватывающий несколько вариантов входных данных.
|
||
|
||
---
|
||
|
||
# Проверка корректности
|
||
|
||
Перед завершением Build выполнены проверки.
|
||
|
||
## Компиляция
|
||
|
||
```text
|
||
python -m compileall
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
OK
|
||
```
|
||
|
||
---
|
||
|
||
## Unit Test Unified Adapter
|
||
|
||
```text
|
||
9 passed
|
||
```
|
||
|
||
---
|
||
|
||
## Совместная регрессия адаптеров
|
||
|
||
```text
|
||
15 passed
|
||
```
|
||
|
||
Проверены:
|
||
|
||
- Quote Adapter;
|
||
- OHLC Adapter;
|
||
- Unified Adapter.
|
||
|
||
---
|
||
|
||
## Полная регрессия Market Data Acquisition
|
||
|
||
```text
|
||
634 passed
|
||
```
|
||
|
||
Подтверждено отсутствие регрессий во всей подсистеме получения рыночных данных.
|
||
|
||
---
|
||
|
||
## Проверка diff
|
||
|
||
Выполнена команда:
|
||
|
||
```text
|
||
git diff --check
|
||
```
|
||
|
||
Замечаний не обнаружено.
|
||
|
||
---
|
||
|
||
# Изменённые файлы
|
||
|
||
```text
|
||
src/market_data/acquisition/adapters/dzengi/websocket.py
|
||
|
||
src/market_data/acquisition/exceptions.py
|
||
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_unified_adapter.py
|
||
```
|
||
|
||
---
|
||
|
||
# Совместимость
|
||
|
||
Build 059.8 полностью обратно совместим.
|
||
|
||
Все существующие специализированные адаптеры продолжают работать без изменений.
|
||
|
||
Публичие контракты:
|
||
|
||
- Quote Adapter;
|
||
- OHLC Adapter;
|
||
|
||
остаются неизменными.
|
||
|
||
Unified Adapter представляет собой дополнительный уровень маршрутизации и не нарушает существующую архитектуру.
|
||
|
||
---
|
||
|
||
# Архитектурное значение Build
|
||
|
||
Build 059.8 завершает построение слоя адаптеров Dzengi WebSocket.
|
||
|
||
После завершения этапа система получает единую точку входа для обработки входящих сообщений при сохранении независимости специализированных pipeline.
|
||
|
||
Это позволяет последующим Build работать уже не с отдельными типами сообщений биржи, а с единым контрактом обработки WebSocket-событий.
|
||
|
||
---
|
||
|
||
# Ограничения Build
|
||
|
||
Build 059.8 не реализует:
|
||
|
||
- управление WebSocket-соединением;
|
||
- подписку на каналы;
|
||
- повторные подписки;
|
||
- восстановление соединения;
|
||
- обработку heartbeat;
|
||
- синхронизацию состояния.
|
||
|
||
Указанные возможности будут реализованы на следующих этапах миграции.
|
||
|
||
---
|
||
|
||
# Итог
|
||
|
||
Build 059.8 завершил построение слоя маршрутизации сообщений Dzengi WebSocket.
|
||
|
||
Архитектура получила единую точку входа без изменения существующих специализированных адаптеров.
|
||
|
||
Все проверки успешно пройдены.
|
||
|
||
Регрессии отсутствуют.
|
||
|
||
---
|
||
|
||
# Следующий этап
|
||
|
||
```text
|
||
Build 059.9 — Subscription Layer
|
||
```
|
||
|
||
Следующий Build посвящён реализации уровня подписок на WebSocket-каналы и подготовке инфраструктуры для интеграции Unified Adapter с постоянным потоком биржевых сообщений. |