423 lines
11 KiB
Markdown
423 lines
11 KiB
Markdown
# Build 059.10 — Runtime Transport Message Models
|
||
|
||
**Migration Build**
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
После завершения Build 059.9 в проекте появился самостоятельный Runtime слой, содержащий транспортные контракты:
|
||
|
||
- WebSocketTransportProtocol
|
||
- WebSocketSessionProtocol
|
||
- WebSocketSubscriptionManagerProtocol
|
||
|
||
Следующим логическим шагом является создание моделей данных, которыми будут обмениваться компоненты Runtime.
|
||
|
||
Данный Build вводит первый уровень моделей Runtime — **Transport Message Models**.
|
||
|
||
---
|
||
|
||
# Причина появления Transport Message Models
|
||
|
||
До настоящего момента Runtime содержал только интерфейсы взаимодействия.
|
||
|
||
Например:
|
||
|
||
```
|
||
Transport
|
||
│
|
||
connect()
|
||
disconnect()
|
||
send()
|
||
receive()
|
||
```
|
||
|
||
Однако отсутствовало описание того, **какие именно объекты транспорт должен принимать и возвращать**.
|
||
|
||
Использование непосредственно типов
|
||
|
||
```python
|
||
str
|
||
bytes
|
||
```
|
||
|
||
делает код менее выразительным.
|
||
|
||
Невозможно определить:
|
||
|
||
- транспортное сообщение это;
|
||
- сериализованный JSON;
|
||
- бинарный protobuf;
|
||
- внутренний объект Runtime.
|
||
|
||
Поэтому вводится отдельный уровень моделей транспортных сообщений.
|
||
|
||
---
|
||
|
||
# Архитектурная идея
|
||
|
||
Главная задача данного Build — отделить транспортные данные от всех остальных сущностей системы.
|
||
|
||
Получается следующая иерархия.
|
||
|
||
```text
|
||
Transport
|
||
│
|
||
Transport Message
|
||
│
|
||
Parser
|
||
│
|
||
Validation
|
||
│
|
||
Mapper
|
||
│
|
||
Canonical Models
|
||
```
|
||
|
||
Transport знает только одно:
|
||
|
||
> существует некоторый payload.
|
||
|
||
Он совершенно не знает:
|
||
|
||
- что находится внутри payload;
|
||
- как этот payload будет интерпретирован;
|
||
- является ли он JSON;
|
||
- относится ли он к конкретной бирже;
|
||
- какие модели будут построены дальше.
|
||
|
||
Это полностью соответствует принципу Single Responsibility.
|
||
|
||
---
|
||
|
||
# Почему Runtime не знает про WebSocket
|
||
|
||
Несмотря на то, что текущим транспортом является WebSocket, Runtime проектируется максимально универсальным.
|
||
|
||
Поэтому модели транспортных сообщений намеренно не содержат слов:
|
||
|
||
- WebSocket
|
||
- Dzengi
|
||
- Exchange
|
||
- Candle
|
||
- Quote
|
||
|
||
Runtime оперирует исключительно транспортными сообщениями.
|
||
|
||
Это позволяет в будущем использовать один и тот же Runtime для различных транспортов:
|
||
|
||
- WebSocket;
|
||
- HTTP Streaming;
|
||
- FIX;
|
||
- gRPC Streaming;
|
||
- внутренние очереди сообщений.
|
||
|
||
---
|
||
|
||
# Новые модели
|
||
|
||
Создан новый файл
|
||
|
||
```
|
||
src/market_data/acquisition/runtime/transport_messages.py
|
||
```
|
||
|
||
В Build добавлены две immutable модели.
|
||
|
||
---
|
||
|
||
## TransportTextMessage
|
||
|
||
Представляет текстовое транспортное сообщение.
|
||
|
||
Содержит единственное поле:
|
||
|
||
```
|
||
payload: str
|
||
```
|
||
|
||
Никакой дополнительной информации модель не содержит.
|
||
|
||
Она не знает:
|
||
|
||
- что внутри находится JSON;
|
||
- является ли сообщение командой;
|
||
- является ли сообщение событием;
|
||
- содержит ли сообщение свечу.
|
||
|
||
Она является исключительно контейнером транспортного текста.
|
||
|
||
---
|
||
|
||
## TransportBinaryMessage
|
||
|
||
Представляет бинарное транспортное сообщение.
|
||
|
||
Содержит единственное поле
|
||
|
||
```
|
||
payload: bytes
|
||
```
|
||
|
||
Как и текстовая модель, не содержит никакой информации о содержимом.
|
||
|
||
Runtime рассматривает бинарные данные как непрозрачный набор байтов.
|
||
|
||
---
|
||
|
||
# Почему модели содержат только payload
|
||
|
||
Во время проектирования рассматривались варианты добавить дополнительные поля.
|
||
|
||
Например:
|
||
|
||
```
|
||
message_type
|
||
encoding
|
||
channel
|
||
metadata
|
||
timestamp
|
||
```
|
||
|
||
От данной идеи было принято решение отказаться.
|
||
|
||
Причины следующие.
|
||
|
||
## Причина №1
|
||
|
||
Транспорт не должен ничего знать о содержимом сообщения.
|
||
|
||
Если транспорт начинает анализировать содержимое сообщения, происходит смешение ответственности между Runtime и Parser.
|
||
|
||
---
|
||
|
||
## Причина №2
|
||
|
||
Любые дополнительные поля являются предположениями относительно будущего транспорта.
|
||
|
||
На данном этапе архитектуры неизвестно:
|
||
|
||
- понадобится ли message_type;
|
||
- понадобится ли encoding;
|
||
- понадобится ли channel;
|
||
- понадобится ли metadata.
|
||
|
||
Следовательно, преждевременно добавлять подобные поля.
|
||
|
||
---
|
||
|
||
## Причина №3
|
||
|
||
Минимальные immutable модели проще поддерживать.
|
||
|
||
При необходимости они могут быть расширены отдельным Build без нарушения обратной совместимости.
|
||
|
||
---
|
||
|
||
# Почему здесь отсутствуют ConnectRequest
|
||
|
||
Во время проектирования рассматривалась идея добавить модели:
|
||
|
||
```
|
||
ConnectRequest
|
||
DisconnectRequest
|
||
SubscribeRequest
|
||
UnsubscribeRequest
|
||
```
|
||
|
||
Однако было принято решение отказаться от неё.
|
||
|
||
Причина заключается в разделении уровней ответственности.
|
||
|
||
ConnectRequest не является транспортным сообщением.
|
||
|
||
Это команда Runtime.
|
||
|
||
---
|
||
|
||
# Почему отсутствуют Ping/Pong
|
||
|
||
По аналогичной причине.
|
||
|
||
Ping и Pong являются частью логики Runtime.
|
||
|
||
Транспорт получает некоторый payload.
|
||
|
||
Что именно находится внутри этого payload, транспорт не анализирует.
|
||
|
||
Следовательно:
|
||
|
||
Ping/Pong должны появиться позднее вместе с Runtime Commands.
|
||
|
||
---
|
||
|
||
# Новая архитектура Runtime
|
||
|
||
После завершения Build Runtime начинает разделяться на несколько независимых уровней.
|
||
|
||
```text
|
||
Runtime
|
||
|
||
Transport Protocol
|
||
│
|
||
Transport Messages
|
||
│
|
||
Runtime Commands
|
||
│
|
||
Runtime Events
|
||
│
|
||
Runtime Services
|
||
```
|
||
|
||
Подобное разделение является значительно более устойчивым, чем смешение всех сущностей в одном модуле.
|
||
|
||
---
|
||
|
||
# Обновлённый план Runtime
|
||
|
||
После выполнения Build 059.10 дальнейшее развитие Runtime выглядит следующим образом.
|
||
|
||
## Build 059.11
|
||
|
||
Runtime Commands
|
||
|
||
Будут введены команды:
|
||
|
||
- ConnectCommand
|
||
- DisconnectCommand
|
||
- SubscribeCommand
|
||
- UnsubscribeCommand
|
||
- SendTextCommand
|
||
- SendBinaryCommand
|
||
|
||
---
|
||
|
||
## Build 059.12
|
||
|
||
Runtime Events
|
||
|
||
Появятся события Runtime:
|
||
|
||
- ConnectedEvent
|
||
- DisconnectedEvent
|
||
- SubscriptionRestoredEvent
|
||
- HeartbeatTimeoutEvent
|
||
- ReconnectStartedEvent
|
||
- ReconnectCompletedEvent
|
||
|
||
---
|
||
|
||
## Build 059.13
|
||
|
||
Subscription Manager
|
||
|
||
Полноценное управление активными подписками Runtime.
|
||
|
||
---
|
||
|
||
## Build 059.14
|
||
|
||
WebSocket Session
|
||
|
||
Жизненный цикл Runtime Session.
|
||
|
||
---
|
||
|
||
## Build 059.15
|
||
|
||
Transport Reliability
|
||
|
||
Инфраструктура Runtime:
|
||
|
||
- Heartbeat
|
||
- Reconnect
|
||
- Ping/Pong
|
||
- Scheduler
|
||
- Recovery
|
||
|
||
---
|
||
|
||
## Build 059.16
|
||
|
||
Runtime Integration
|
||
|
||
Интеграция нового Runtime с существующей цепочкой Acquisition.
|
||
|
||
---
|
||
|
||
## Build 059.17
|
||
|
||
Runtime Validation & Documentation
|
||
|
||
Полная регрессия новой архитектуры и финальная документация.
|
||
|
||
---
|
||
|
||
# Проверка Build
|
||
|
||
Выполнены проверки.
|
||
|
||
## Компиляция
|
||
|
||
```
|
||
python -m compileall
|
||
```
|
||
|
||
Успешно.
|
||
|
||
---
|
||
|
||
## Unit Tests
|
||
|
||
```
|
||
test_transport_messages.py
|
||
```
|
||
|
||
Все тесты успешно пройдены.
|
||
|
||
Проверяется:
|
||
|
||
- создание моделей;
|
||
- корректность хранения payload;
|
||
- immutable поведение dataclass.
|
||
|
||
---
|
||
|
||
## Runtime Regression
|
||
|
||
Проверена совместимость новой модели сообщений с ранее созданными Runtime Protocol.
|
||
|
||
Все проверки успешно завершены.
|
||
|
||
---
|
||
|
||
## Проверка репозитория
|
||
|
||
Выполнен
|
||
|
||
```
|
||
git diff --check
|
||
```
|
||
|
||
Ошибок форматирования не обнаружено.
|
||
|
||
---
|
||
|
||
# Итог
|
||
|
||
Build 059.10 завершает создание базового транспортного уровня Runtime.
|
||
|
||
После него Runtime уже содержит:
|
||
|
||
- транспортные контракты;
|
||
- транспортные модели сообщений.
|
||
|
||
Следующие Build будут постепенно добавлять поведение системы (Commands, Events, Session, Subscription Manager, Reconnect), не изменяя уже построенный фундамент.
|
||
|
||
Таким образом продолжается основной принцип миграции Dzentra:
|
||
|
||
- каждый Build решает одну архитектурную задачу;
|
||
- изменения являются минимальными и независимыми;
|
||
- существующая цепочка Acquisition остаётся неизменной;
|
||
- работающий торговый бот продолжает функционировать без необходимости одновременной полной миграции. |