580 lines
13 KiB
Markdown
580 lines
13 KiB
Markdown
# 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-подсистему. |