# Build 059.12 — Runtime Event Models **Migration Build** --- # Цель Build После завершения Build 059.11 Runtime уже содержит три независимых архитектурных уровня: - Runtime Protocols; - Runtime Transport Messages; - Runtime Commands. Следующим шагом является создание слоя **Runtime Events**. Данный Build вводит immutable-модели событий Runtime. События описывают **уже произошедшие факты** и полностью отделены от команд Runtime. --- # Причина появления Runtime Events До настоящего момента Runtime умел описывать: - транспортные интерфейсы; - транспортные сообщения; - намерения системы. Однако отсутствовал механизм описания результатов выполнения этих намерений. Например. После выполнения ``` ConnectCommand ``` может произойти: ``` ConnectedEvent ``` или ``` ConnectFailedEvent ``` Это два разных факта. Команда выражает желание выполнить действие. Событие фиксирует результат выполнения этого действия. --- # Архитектурная идея Build 059.12 завершает построение базовой event-driven модели Runtime. Получается следующая архитектура. ```text Runtime Commands │ ▼ Session │ ▼ Transport │ ▼ Events ``` Commands направляются вниз. Events распространяются вверх. Именно такое разделение используется в большинстве современных event-driven систем. --- # Новый модуль Создан файл ``` src/market_data/acquisition/runtime/runtime_events.py ``` В модуле определены immutable-модели Runtime Events. --- # Использование Transport Messages Транспортные события используют модели Build 059.10. ``` TransportTextMessage TransportBinaryMessage ``` Runtime Event не знает содержимого сообщения. Он лишь фиксирует факт передачи или получения транспортного payload. --- # TransportMessage Для удобства типизации используется локальный alias. ```python TransportMessage = TransportTextMessage | TransportBinaryMessage ``` Он используется только внутри Runtime Events. Никакой новой модели данных не создаётся. --- # Новые события Build вводит девять Runtime Events. --- # ConnectedEvent Фиксирует успешное открытие транспортного соединения. Событие является маркерным. Не содержит дополнительных данных. Причина. На текущем этапе Runtime отсутствует модель идентификатора соединения. Добавлять подобные поля преждевременно. --- # DisconnectedEvent Фиксирует завершение транспортного соединения. Событие также является маркерным. Причина отключения намеренно отсутствует. Определение причин разрыва соединения относится к будущему уровню Reliability. --- # ConnectFailedEvent Фиксирует неудачную попытку подключения. Содержит: ``` reason ``` Причина хранится в виде строки. --- ## Почему используется строка Во время проектирования рассматривались варианты хранения: - Exception; - traceback; - transport-specific error; - websocket exception. От данных вариантов было принято решение отказаться. Runtime Event не должен зависеть от конкретной реализации транспорта. Строковое описание полностью соответствует принципу transport-agnostic Runtime. --- # MessageReceivedEvent Фиксирует получение транспортного сообщения. Содержит: ``` TransportMessage ``` Runtime Event не анализирует payload. Его обработкой занимаются последующие уровни Acquisition. --- # MessageSentEvent Фиксирует успешную передачу транспортного сообщения. Содержит: ``` TransportMessage ``` Это позволяет журналировать транспортный обмен, не анализируя содержимое сообщений. --- # ReconnectStartedEvent Фиксирует начало новой попытки восстановления соединения. Содержит: ``` attempt ``` --- ## Почему хранится номер попытки Номер попытки является устойчивым Runtime-фактом. Он потребуется: - Supervisor; - журналированию; - диагностике; - мониторингу Runtime. --- # ReconnectCompletedEvent Фиксирует успешное восстановление соединения. Также содержит: ``` attempt ``` что позволяет определить, на какой попытке произошло восстановление. --- # ReconnectFailedEvent Фиксирует завершение очередной попытки восстановления соединения с ошибкой. Содержит: ``` attempt reason ``` Таким образом Runtime может описывать процесс восстановления без зависимости от конкретной реализации транспорта. --- # HeartbeatTimeoutEvent Фиксирует превышение допустимого интервала heartbeat. Содержит: ``` timeout_seconds ``` Хранится только настроенный порог ожидания. --- ## Почему отсутствуют timestamp Во время проектирования обсуждалось хранение: ``` timestamp occurred_at created_at ``` Было принято решение отказаться. Причины. --- ### Причина №1 В Runtime ещё отсутствует единая модель времени. --- ### Причина №2 В разных окружениях время может поступать из различных источников. Например: - системные часы; - монотонные часы; - серверное время биржи. До появления общей модели времени вводить timestamp преждевременно. --- # Почему отсутствует RuntimeEvent Также обсуждалось создание общего базового класса. Например. ``` RuntimeEvent ``` или ``` BaseEvent ``` От идеи отказались. Причины. --- ## Причина №1 Все события являются immutable dataclass. Общего поведения между ними нет. --- ## Причина №2 Преждевременная иерархия только усложняет архитектуру. --- ## Причина №3 Общий базовый тип можно добавить позднее без нарушения обратной совместимости. --- # Почему отсутствует RuntimeEventType Рассматривалось использование enum. Например. ``` CONNECTED DISCONNECTED MESSAGE_RECEIVED ``` Было принято решение отказаться. Тип события полностью определяется его классом. Дополнительный enum создавал бы дублирование информации. --- # Почему отсутствуют Subscription Events Изначально предполагалось добавить: - SubscriptionRegisteredEvent; - SubscriptionRemovedEvent; - SubscriptionRestoredEvent. После анализа архитектуры было принято решение перенести их в следующий Build. Причина. В Build 059.12 Subscription Manager ещё отсутствует. Следовательно, пока отсутствует компонент, способный генерировать подобные события. События должны появляться одновременно с соответствующим уровнем архитектуры. Поэтому Subscription Events перенесены в Build 059.13. --- # Что НЕ входит в Build Сознательно не реализованы: - Event Bus; - Dispatcher; - Observer; - callbacks; - asyncio; - Publisher; - Subscriber; - Session; - Subscription Manager; - обработчики событий; - очередь событий. Build содержит исключительно immutable-модели Runtime Events. --- # Обновлённая архитектура Runtime После завершения Build Runtime приобретает следующий вид. ```text Runtime Protocols │ Transport Messages │ Runtime Commands │ Runtime Events │ Transport │ Network ``` Каждый уровень отвечает только за собственную область ответственности. --- # Следующие Build ## Build 059.13 Subscription Manager. Появятся: - управление подписками; - хранение Runtime-состояния; - восстановление подписок; - SubscriptionRegisteredEvent; - SubscriptionRemovedEvent; - SubscriptionRestoredEvent. --- ## Build 059.14 WebSocket Session. Появится координация: - Commands; - Events; - Transport; - Subscription Manager. --- ## Build 059.15 Runtime Reliability. Будут реализованы: - Heartbeat; - Reconnect; - Supervisor; - Scheduler; - Ping/Pong. --- # Проверка Build Выполнена полная проверка. --- ## Компиляция ``` python -m compileall ``` Успешно. --- ## Unit Tests Созданы тесты: ``` test_runtime_events.py ``` Проверяется: - создание каждого события; - корректность хранения данных; - поддержка TransportMessage; - immutable-поведение dataclass. Все тесты успешно пройдены. --- ## Runtime Regression Совместно проверены: - Runtime Protocols; - Runtime Transport Messages; - Runtime Commands; - Runtime Events. Все Runtime-тесты успешно завершены. --- ## Проверка репозитория Выполнен ``` git diff --check ``` Ошибок форматирования не обнаружено. --- # Итог Build 059.12 завершает формирование слоя Runtime Events. Теперь Runtime имеет четыре полностью независимых уровня. ```text Protocols │ Transport Messages │ Runtime Commands │ Runtime Events ``` Каждый уровень описывает собственную область ответственности. Commands выражают намерения системы. Events фиксируют уже произошедшие факты. Подобное разделение делает архитектуру Runtime предсказуемой, расширяемой и соответствует общепринятым принципам построения event-driven систем. Следующий Build посвящён созданию Subscription Manager, который станет первым Runtime-компонентом, использующим одновременно Commands и Events, сохраняя при этом независимость транспортного уровня от бизнес-логики Acquisition.