13 KiB
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.
Получается следующая цепочка.
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.
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.
Например.
Session
получает
ConnectCommand
↓
вызывает
Transport.connect()
Таким образом команда остаётся полностью независимой от реализации транспорта.
Обновлённая архитектура Runtime
После завершения Build Runtime выглядит следующим образом.
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 имеет три независимых уровня:
Protocols
│
Transport Messages
│
Runtime Commands
Каждый уровень отвечает только за собственную область ответственности.
Это создаёт устойчивый фундамент для следующих Build, в которых будут реализованы Runtime Events, Subscription Manager, Session и механизмы обеспечения надёжности соединения.
Главный принцип миграции Dzentra остаётся неизменным:
- архитектура развивается небольшими независимыми шагами;
- каждый Build вводит только один новый уровень ответственности;
- существующий торговый бот продолжает работать без необходимости полной одномоментной миграции на новую Runtime-подсистему.