Files
dzentra_bot/docs/migrations/build_059_11.md

580 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build 059.11 — Runtime Commands
**Migration Build**
---
# Цель Build
После завершения Build 059.10 Runtime уже содержит два фундаментальных уровня:
- Runtime Protocols
- Transport Message Models
Следующим шагом является появление объектов, описывающих **намерения Runtime**.
Данный Build вводит отдельный слой **Runtime Commands**.
Команды описывают **что необходимо сделать**, но не содержат логики выполнения.
---
# Почему понадобился отдельный слой Commands
До настоящего момента Runtime умел описывать:
- транспортные интерфейсы;
- транспортные сообщения.
Однако отсутствовало понятие действия.
Например:
- открыть соединение;
- закрыть соединение;
- зарегистрировать подписку;
- отправить сообщение.
Все эти действия являются командами Runtime.
Они не являются:
- транспортными сообщениями;
- событиями Runtime;
- сетевыми операциями.
Поэтому они выделяются в самостоятельный уровень архитектуры.
---
# Архитектурная идея
Runtime начинает строиться по принципу Command-driven Architecture.
Получается следующая цепочка.
```text
Runtime Service
Runtime Command
Transport
Transport Message
Network
```
Таким образом команды описывают исключительно намерение.
Исполнение намерения остаётся обязанностью Session и Transport.
---
# Новый модуль
Создан файл
```
src/market_data/acquisition/runtime/runtime_commands.py
```
В нём определены immutable-модели Runtime-команд.
---
# Использование Transport Messages
Все команды, связанные с передачей данных, используют модели Build 059.10.
То есть Runtime продолжает работать только с двумя видами транспортных сообщений:
```
TransportTextMessage
TransportBinaryMessage
```
Никакие команды не работают непосредственно со строками или массивами байтов.
Это обеспечивает единый транспортный контракт Runtime.
---
# TransportMessage
В модуле введён локальный type alias.
```python
TransportMessage =
TransportTextMessage
| TransportBinaryMessage
```
Он используется исключительно для типизации.
Никакой новой Runtime-модели не создаётся.
Данный alias позволяет избежать дублирования кода в командах подписки.
---
# Новые команды
В Build добавлены шесть Runtime-команд.
---
## ConnectCommand
Представляет намерение открыть транспортное соединение.
Команда:
- не содержит URL;
- не содержит параметры подключения;
- не выполняет подключение самостоятельно.
Она лишь сообщает Runtime:
> необходимо открыть соединение.
---
## DisconnectCommand
Представляет намерение завершить соединение.
Команда не управляет транспортом.
Она только описывает действие Runtime.
---
## Почему ConnectCommand не содержит URL
Во время проектирования рассматривались варианты добавить:
```
url
headers
timeout
ssl
authentication
```
От данной идеи было принято решение отказаться.
Причины следующие.
---
### Причина №1
URL является частью конфигурации транспорта.
Он не должен передаваться вместе с каждой командой.
---
### Причина №2
Команда должна описывать действие.
Не настройки транспорта.
---
### Причина №3
Подобная архитектура позволяет легко заменить транспорт без изменения Runtime-команд.
---
# SubscribeCommand
Представляет команду регистрации новой подписки Runtime.
Содержит два поля.
```
subscription_key
message
```
---
## subscription_key
Строковый идентификатор подписки.
На текущем этапе Runtime он нигде не интерпретируется.
Однако именно он станет основным ключом будущего Subscription Manager.
Например:
```
quotes:BTCUSDT
```
или
```
candles:ETHUSDT:1m
```
Формат идентификатора пока намеренно не стандартизируется.
Это будет сделано позднее после проектирования Subscription Manager.
---
## message
Содержит транспортное сообщение.
Тип поля:
```
TransportMessage
```
То есть подписка может использовать:
- текстовое сообщение;
- бинарное сообщение.
Runtime не анализирует содержимое payload.
---
# Почему SubscribeCommand хранит готовое сообщение
Рассматривались два варианта.
---
## Вариант 1
Хранить параметры подписки.
Например
```
symbol
interval
channel
```
---
## Вариант 2
Хранить уже подготовленное транспортное сообщение.
Был выбран второй вариант.
Причины.
---
### Runtime не знает протокол биржи
Построением JSON занимается слой выше.
Runtime не должен знать:
- Binance;
- Bybit;
- Dzengi;
- Kraken;
- OKX.
---
### Runtime отвечает только за доставку
Transport получает уже готовое сообщение.
Как оно было сформировано — его не касается.
---
# UnsubscribeCommand
Представляет команду отмены существующей подписки.
Содержит:
```
subscription_key
message
```
По архитектуре полностью симметрична SubscribeCommand.
Это позволит Subscription Manager использовать единый механизм регистрации и удаления подписок.
---
# SendTextCommand
Представляет команду отправки текстового транспортного сообщения.
Содержит
```
TransportTextMessage
```
Никаких дополнительных параметров команда не имеет.
---
# SendBinaryCommand
Представляет команду отправки бинарного транспортного сообщения.
Содержит
```
TransportBinaryMessage
```
Runtime рассматривает его исключительно как транспортный payload.
---
# Почему отсутствует базовый класс Command
Во время проектирования обсуждались варианты:
```
BaseCommand
```
или
```
RuntimeCommand
```
Было принято решение отказаться.
Причины.
---
## Причина №1
Все команды являются immutable dataclass.
Общего поведения между ними нет.
---
## Причина №2
Преждевременная иерархия усложняет архитектуру.
Добавлять её без появления реальной общей логики нецелесообразно.
---
## Причина №3
При необходимости общий базовый тип может быть введён отдельным Build без нарушения обратной совместимости.
---
# Почему отсутствует CommandType
Также обсуждалось использование enum.
Например.
```
CONNECT
DISCONNECT
SUBSCRIBE
```
От идеи отказались.
Тип команды уже определяется её классом.
Дополнительный enum создавал бы дублирование информации.
---
# Почему команды ничего не делают
Очень важный архитектурный принцип Runtime.
Команды являются моделями данных.
Они:
- ничего не выполняют;
- не взаимодействуют с сетью;
- не содержат asyncio;
- не вызывают Transport;
- не знают Session.
Это соответствует принципу Command Pattern.
---
# Что появится позже
Исполнением команд будут заниматься Runtime Services.
Например.
```text
Session
получает
ConnectCommand
вызывает
Transport.connect()
```
Таким образом команда остаётся полностью независимой от реализации транспорта.
---
# Обновлённая архитектура Runtime
После завершения Build Runtime выглядит следующим образом.
```text
Runtime
Protocols
Transport Messages
Runtime Commands
Transport
Network
```
Следующим Build между Commands и Transport появятся Runtime Events.
---
# Что НЕ входит в Build
Сознательно не реализованы:
- Session;
- Command Dispatcher;
- очередь команд;
- asyncio;
- обработчики команд;
- сериализация;
- JSON;
- Subscription Manager;
- Reconnect;
- Heartbeat;
- Ping/Pong.
Все эти элементы будут появляться постепенно отдельными Build.
---
# Проверка Build
Выполнена полная проверка.
---
## Компиляция
```
python -m compileall
```
Успешно.
---
## Unit Tests
Созданы тесты:
```
test_runtime_commands.py
```
Проверяется:
- создание каждой команды;
- корректность хранения TransportMessage;
- поддержка текстовых сообщений;
- поддержка бинарных сообщений;
- immutable-поведение dataclass.
Все тесты успешно пройдены.
---
## Runtime Regression
Проверены совместно:
- Runtime Protocols;
- Transport Messages;
- Runtime Commands.
Все Runtime-тесты успешно завершены.
---
## Проверка репозитория
Выполнен
```
git diff --check
```
Ошибок форматирования не обнаружено.
---
# Итог
Build 059.11 завершает формирование слоя Runtime Commands.
Теперь Runtime имеет три независимых уровня:
```text
Protocols
Transport Messages
Runtime Commands
```
Каждый уровень отвечает только за собственную область ответственности.
Это создаёт устойчивый фундамент для следующих Build, в которых будут реализованы Runtime Events, Subscription Manager, Session и механизмы обеспечения надёжности соединения.
Главный принцип миграции Dzentra остаётся неизменным:
- архитектура развивается небольшими независимыми шагами;
- каждый Build вводит только один новый уровень ответственности;
- существующий торговый бот продолжает работать без необходимости полной одномоментной миграции на новую Runtime-подсистему.