build 059.10: add runtime transport message models
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# app/src/market_data/acquisition/runtime/transport_messages.py
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class TransportTextMessage:
|
||||
"""
|
||||
Транспортное текстовое сообщение.
|
||||
|
||||
Представляет уже полученный или подготовленный для отправки
|
||||
текстовый payload без какой-либо интерпретации содержимого.
|
||||
"""
|
||||
|
||||
payload: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class TransportBinaryMessage:
|
||||
"""
|
||||
Транспортное бинарное сообщение.
|
||||
|
||||
Представляет уже полученный или подготовленный для отправки
|
||||
бинарный payload без какой-либо интерпретации содержимого.
|
||||
"""
|
||||
|
||||
payload: bytes
|
||||
@@ -0,0 +1,36 @@
|
||||
# app/tests/unit/market_data/acquisition/runtime/test_transport_messages.py
|
||||
|
||||
from dataclasses import FrozenInstanceError
|
||||
|
||||
import pytest
|
||||
|
||||
from src.market_data.acquisition.runtime.transport_messages import (
|
||||
TransportBinaryMessage,
|
||||
TransportTextMessage,
|
||||
)
|
||||
|
||||
|
||||
def test_transport_text_message() -> None:
|
||||
message = TransportTextMessage(payload='{"ping":1}')
|
||||
|
||||
assert message.payload == '{"ping":1}'
|
||||
|
||||
|
||||
def test_transport_binary_message() -> None:
|
||||
message = TransportBinaryMessage(payload=b"\x01\x02")
|
||||
|
||||
assert message.payload == b"\x01\x02"
|
||||
|
||||
|
||||
def test_text_message_is_immutable() -> None:
|
||||
message = TransportTextMessage(payload="abc")
|
||||
|
||||
with pytest.raises(FrozenInstanceError):
|
||||
setattr(message, "payload", "def")
|
||||
|
||||
|
||||
def test_binary_message_is_immutable() -> None:
|
||||
message = TransportBinaryMessage(payload=b"\x00")
|
||||
|
||||
with pytest.raises(FrozenInstanceError):
|
||||
setattr(message, "payload", b"\x01")
|
||||
423
docs/migrations/build_059_10.md
Normal file
423
docs/migrations/build_059_10.md
Normal file
@@ -0,0 +1,423 @@
|
||||
# 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 остаётся неизменной;
|
||||
- работающий торговый бот продолжает функционировать без необходимости одновременной полной миграции.
|
||||
Reference in New Issue
Block a user