From 433ac5d375c267eb72903e1b5f8425f17ec37f90 Mon Sep 17 00:00:00 2001 From: Sergey Date: Sun, 26 Jul 2026 09:20:24 +0300 Subject: [PATCH] Build 060.21: integrate acquisition runtime protocols --- .../acquisition/runtime/websocket_protocol.py | 88 +- .../runtime/test_websocket_protocol.py | 56 +- docs/migrations/build_060_21.md | 841 ++++++++++++++++ docs/migrations/build_060_21_architecture.md | 931 ++++++++++++++++++ 4 files changed, 1914 insertions(+), 2 deletions(-) create mode 100644 docs/migrations/build_060_21.md create mode 100644 docs/migrations/build_060_21_architecture.md diff --git a/app/src/market_data/acquisition/runtime/websocket_protocol.py b/app/src/market_data/acquisition/runtime/websocket_protocol.py index cb2c08f..415f058 100644 --- a/app/src/market_data/acquisition/runtime/websocket_protocol.py +++ b/app/src/market_data/acquisition/runtime/websocket_protocol.py @@ -4,6 +4,48 @@ from __future__ import annotations from typing import Protocol, runtime_checkable +from src.market_data.acquisition.runtime.runtime_commands import ( + ConnectCommand, + DisconnectCommand, + SendBinaryCommand, + SendTextCommand, + SubscribeCommand, + UnsubscribeCommand, +) +from src.market_data.acquisition.runtime.runtime_events import ( + ConnectedEvent, + ConnectFailedEvent, + DisconnectedEvent, + HeartbeatTimeoutEvent, + MessageReceivedEvent, + MessageSentEvent, + ReconnectCompletedEvent, + ReconnectFailedEvent, + ReconnectStartedEvent, +) + + +AcquisitionRuntimeCommand = ( + ConnectCommand + | DisconnectCommand + | SubscribeCommand + | UnsubscribeCommand + | SendTextCommand + | SendBinaryCommand +) + +AcquisitionRuntimeEvent = ( + ConnectedEvent + | DisconnectedEvent + | ConnectFailedEvent + | MessageReceivedEvent + | MessageSentEvent + | ReconnectStartedEvent + | ReconnectCompletedEvent + | ReconnectFailedEvent + | HeartbeatTimeoutEvent +) + @runtime_checkable class WebSocketTransportProtocol(Protocol): @@ -76,4 +118,48 @@ class WebSocketSubscriptionManagerProtocol(Protocol): async def clear_subscriptions(self) -> None: """Очистить runtime-состояние активных подписок.""" - ... \ No newline at end of file + ... + + +@runtime_checkable +class AcquisitionRuntimeCommandDispatcherProtocol(Protocol): + """ + Контракт передачи инфраструктурных команд в Runtime. + + Dispatcher принимает только типизированные Runtime-команды + и не содержит знаний о Trade, Feed, Consistency или Recovery. + + Конкретная маршрутизация и выполнение команд будут реализованы + в последующих Build. + """ + + async def dispatch( + self, + command: AcquisitionRuntimeCommand, + ) -> None: + """ + Передать одну инфраструктурную команду в Runtime. + """ + ... + + +@runtime_checkable +class AcquisitionRuntimeEventPublisherProtocol(Protocol): + """ + Контракт публикации инфраструктурных событий Runtime. + + Publisher передаёт уже произошедшие инфраструктурные факты + заинтересованным потребителям и не определяет их реакцию. + + Конкретный механизм доставки событий будет реализован + в последующих Build. + """ + + async def publish( + self, + event: AcquisitionRuntimeEvent, + ) -> None: + """ + Опубликовать одно инфраструктурное событие Runtime. + """ + ... diff --git a/app/tests/unit/market_data/acquisition/runtime/test_websocket_protocol.py b/app/tests/unit/market_data/acquisition/runtime/test_websocket_protocol.py index 82335b2..0a50c38 100644 --- a/app/tests/unit/market_data/acquisition/runtime/test_websocket_protocol.py +++ b/app/tests/unit/market_data/acquisition/runtime/test_websocket_protocol.py @@ -3,6 +3,10 @@ from __future__ import annotations from src.market_data.acquisition.runtime.websocket_protocol import ( + AcquisitionRuntimeCommand, + AcquisitionRuntimeCommandDispatcherProtocol, + AcquisitionRuntimeEvent, + AcquisitionRuntimeEventPublisherProtocol, WebSocketSessionProtocol, WebSocketSubscriptionManagerProtocol, WebSocketTransportProtocol, @@ -43,6 +47,22 @@ class FakeWebSocketSubscriptionManager: return None +class FakeRuntimeCommandDispatcher: + async def dispatch( + self, + command: AcquisitionRuntimeCommand, + ) -> None: + return None + + +class FakeRuntimeEventPublisher: + async def publish( + self, + event: AcquisitionRuntimeEvent, + ) -> None: + return None + + def test_transport_implementation_satisfies_protocol() -> None: assert isinstance(FakeWebSocketTransport(), WebSocketTransportProtocol) @@ -58,9 +78,43 @@ def test_subscription_manager_implementation_satisfies_protocol() -> None: ) +def test_command_dispatcher_implementation_satisfies_protocol() -> None: + assert isinstance( + FakeRuntimeCommandDispatcher(), + AcquisitionRuntimeCommandDispatcherProtocol, + ) + + +def test_event_publisher_implementation_satisfies_protocol() -> None: + assert isinstance( + FakeRuntimeEventPublisher(), + AcquisitionRuntimeEventPublisherProtocol, + ) + + def test_incomplete_transport_does_not_satisfy_protocol() -> None: class IncompleteTransport: async def connect(self) -> None: return None - assert not isinstance(IncompleteTransport(), WebSocketTransportProtocol) \ No newline at end of file + assert not isinstance(IncompleteTransport(), WebSocketTransportProtocol) + + +def test_incomplete_command_dispatcher_does_not_satisfy_protocol() -> None: + class IncompleteCommandDispatcher: + pass + + assert not isinstance( + IncompleteCommandDispatcher(), + AcquisitionRuntimeCommandDispatcherProtocol, + ) + + +def test_incomplete_event_publisher_does_not_satisfy_protocol() -> None: + class IncompleteEventPublisher: + pass + + assert not isinstance( + IncompleteEventPublisher(), + AcquisitionRuntimeEventPublisherProtocol, + ) diff --git a/docs/migrations/build_060_21.md b/docs/migrations/build_060_21.md new file mode 100644 index 0000000..ab79dfd --- /dev/null +++ b/docs/migrations/build_060_21.md @@ -0,0 +1,841 @@ +# Build 060.21 — Runtime Protocol Integration + +**Engineering Migration Report** + +--- + +# Контроль документа + +| Свойство | Значение | +|----------|----------| +| Build | 060.21 | +| Название | Runtime Protocol Integration | +| Статус | Completed | +| Проект | Dzentra | +| Подсистема | Market Data Acquisition | +| Компонент | Acquisition Runtime Protocol Layer | +| Версия | 1.0 | + +--- + +# Связанные документы + +- `build_060_21_architecture.md` — архитектурная спецификация Build. +- `build_060_20_1.md` — Engineering Migration Report предыдущего корректирующего Build. +- `build_060_20_1_architecture.md` — спецификация переноса владельца состояния Trade Stream. + +--- + +# Цель Build + +Build 060.20.1 завершил архитектурную корректировку подсистемы **Trades Feed (Time & Sales)** и окончательно закрепил владельца состояния проверки согласованности потока сделок. + +После предыдущего этапа архитектура сопровождения состояния приняла следующий вид. + +```text +TradeStreamConsistencyController + │ + ▼ +TradeStreamStateStore + │ + ▼ +TradeStreamState +``` + +Runtime перестал владеть состоянием предметной области и сохранил только инфраструктурную ответственность. + +К началу Build 060.21 в проекте уже существовали: + +- транспортные WebSocket-протоколы; +- типизированные Runtime-команды; +- типизированные Runtime-события; +- модели транспортных сообщений; +- независимые подсистемы Trade Stream Consistency и Trade Recovery. + +Однако отсутствовали публичные контракты, связывающие типизированные команды и события с будущей реализацией Runtime Service. + +Главной задачей Build 060.21 стало расширение существующего Runtime Protocol Layer двумя узкими инфраструктурными контрактами: + +```text +AcquisitionRuntimeCommandDispatcherProtocol +AcquisitionRuntimeEventPublisherProtocol +``` + +Одновременно были введены точные типовые объединения: + +```text +AcquisitionRuntimeCommand +AcquisitionRuntimeEvent +``` + +Build сознательно не создаёт Runtime Service и не меняет поведение существующих компонентов. + +--- + +# Предпосылки + +К началу настоящего Build Runtime Layer уже содержал базовые контракты: + +```text +WebSocketTransportProtocol +WebSocketSessionProtocol +WebSocketSubscriptionManagerProtocol +``` + +Они описывали: + +- открытие и закрытие WebSocket-соединения; +- отправку и получение транспортных сообщений; +- жизненный цикл WebSocket-сессии; +- восстановление и очистку подписок. + +Также существовали отдельные модели команд: + +```text +ConnectCommand +DisconnectCommand +SubscribeCommand +UnsubscribeCommand +SendTextCommand +SendBinaryCommand +``` + +и модели событий: + +```text +ConnectedEvent +DisconnectedEvent +ConnectFailedEvent +MessageReceivedEvent +MessageSentEvent +ReconnectStartedEvent +ReconnectCompletedEvent +ReconnectFailedEvent +HeartbeatTimeoutEvent +``` + +Несмотря на наличие всех перечисленных сущностей, в системе отсутствовал формальный контракт их передачи. + +Не было определено: + +- каким образом команда поступает в Runtime; +- каким образом Runtime публикует инфраструктурное событие; +- какие типы команд допустимы; +- какие типы событий допустимы; +- как сохранить независимость Runtime от бизнес-логики Acquisition. + +Именно эту контрактную границу формализует Build 060.21. + +--- + +# Результаты архитектурного аудита + +Перед началом реализации был выполнен аудит следующих компонентов: + +```text +src/market_data/acquisition/runtime/websocket_protocol.py +src/market_data/acquisition/runtime/runtime_commands.py +src/market_data/acquisition/runtime/runtime_events.py +src/market_data/acquisition/runtime/transport_messages.py +``` + +Дополнительно были проанализированы: + +- Runtime unit-тесты; +- Trades Feed; +- WebSocket Trade Adapter; +- Trade Stream Consistency; +- Trade Recovery; +- Acquisition Service и Registry; +- repository-wide зависимости Runtime Protocol Layer. + +Аудит подтвердил, что текущие команды и события уже достаточны для настоящего этапа. + +Следовательно: + +- новые команды не требуются; +- новые события не требуются; +- существующие WebSocket Protocol не должны изменять ответственность; +- Runtime Service не должен создаваться преждевременно; +- Consistency и Recovery не должны получать Runtime-зависимости. + +--- + +# Отказ от общего RuntimeServiceProtocol в Build 060.21 + +На раннем этапе проектирования рассматривалось создание единого контракта: + +```text +RuntimeServiceProtocol +``` + +Предполагалось включить в него операции жизненного цикла, подписок, отправки сообщений и состояния соединения. + +После аудита данный шаг был признан преждевременным. + +В утверждённой дорожной карте следующий этап определён отдельно: + +```text +060.22 Runtime Service Integration +``` + +Создание полноценного сервисного контракта в Build 060.21 фактически перенесло бы часть ответственности Build 060.22 в текущий этап. + +Поэтому Build 060.21 ограничен только недостающими инфраструктурными контрактами: + +- dispatcher команд; +- publisher событий. + +Такое решение сохраняет строгую границу между этапами. + +--- + +# Архитектурное решение + +После завершения Build Runtime Protocol Layer имеет следующий вид. + +```text +Acquisition Runtime Commands + │ + ▼ +AcquisitionRuntimeCommandDispatcherProtocol + │ + ▼ +Future Runtime Service + │ + ▼ +AcquisitionRuntimeEventPublisherProtocol + │ + ▼ +Acquisition Runtime Events +``` + +Новые Protocol не реализуют поведение. + +Они только формализуют допустимый интерфейс будущих компонентов. + +--- + +# AcquisitionRuntimeCommand + +В `websocket_protocol.py` введён типовой alias: + +```text +AcquisitionRuntimeCommand +``` + +Он объединяет все допустимые инфраструктурные команды Acquisition Runtime: + +- `ConnectCommand`; +- `DisconnectCommand`; +- `SubscribeCommand`; +- `UnsubscribeCommand`; +- `SendTextCommand`; +- `SendBinaryCommand`. + +Таким образом будущая реализация dispatcher получает строго ограниченный тип входных данных. + +Она не может принимать: + +- `Trade`; +- `Quote`; +- `Candle`; +- Recovery Request; +- произвольный объект приложения. + +Это закрепляет независимость Runtime от предметной области. + +--- + +# AcquisitionRuntimeEvent + +Также введён типовой alias: + +```text +AcquisitionRuntimeEvent +``` + +Он объединяет все допустимые инфраструктурные события Acquisition Runtime: + +- `ConnectedEvent`; +- `DisconnectedEvent`; +- `ConnectFailedEvent`; +- `MessageReceivedEvent`; +- `MessageSentEvent`; +- `ReconnectStartedEvent`; +- `ReconnectCompletedEvent`; +- `ReconnectFailedEvent`; +- `HeartbeatTimeoutEvent`. + +Alias сознательно получил префикс `Acquisition`. + +В проекте уже существует отдельная глобальная система событий: + +```text +src/runtime_events/ +``` + +с собственной моделью `RuntimeEvent`. + +Использование общего имени внутри Acquisition создало бы неоднозначность и риск ошибочных импортов. + +Поэтому итоговые имена были уточнены: + +```text +AcquisitionRuntimeCommand +AcquisitionRuntimeEvent +``` + +Это решение полностью устраняет терминологический конфликт. + +--- + +# AcquisitionRuntimeCommandDispatcherProtocol + +Добавлен новый публичный контракт: + +```text +AcquisitionRuntimeCommandDispatcherProtocol +``` + +Protocol определяет одну операцию: + +```text +dispatch(command: AcquisitionRuntimeCommand) -> None +``` + +Метод является асинхронным. + +Dispatcher отвечает только за передачу одной типизированной инфраструктурной команды в Runtime. + +Он не определяет: + +- внутреннюю маршрутизацию; +- порядок исполнения; +- повторные попытки; +- обработку ошибок; +- жизненный цикл сессии; +- реакцию бизнес-компонентов. + +Все перечисленные обязанности относятся к будущей реализации Runtime Service. + +--- + +# AcquisitionRuntimeEventPublisherProtocol + +Добавлен второй публичный контракт: + +```text +AcquisitionRuntimeEventPublisherProtocol +``` + +Protocol определяет одну операцию: + +```text +publish(event: AcquisitionRuntimeEvent) -> None +``` + +Метод также является асинхронным. + +Publisher отвечает исключительно за публикацию уже произошедшего инфраструктурного факта. + +Он не определяет: + +- список подписчиков; +- механизм доставки; +- очередь событий; +- обработку ошибок потребителей; +- реакцию Trades Feed; +- запуск Recovery; +- правила reconnect. + +Эти вопросы относятся к последующим Build. + +--- + +# Почему Protocol расположены в websocket_protocol.py + +В рамках настоящего Build новые контракты добавлены в существующий файл: + +```text +src/market_data/acquisition/runtime/websocket_protocol.py +``` + +Причины данного решения: + +- файл уже является центральной точкой Runtime Protocol Layer; +- все текущие Protocol относятся к WebSocket Runtime; +- Build ограничен двумя небольшими контрактами; +- создание нового каталога или дополнительной иерархии файлов было бы преждевременным; +- существующая структура проекта сохраняется без реорганизации. + +При дальнейшем расширении Runtime Protocol Layer отдельный файл может быть введён отдельным согласованным Build, если объём контрактов действительно потребует этого. + +Настоящий Build не выполняет структурную реорганизацию каталогов. + +--- + +# Разделение ответственности + +После завершения Build окончательно закреплены следующие границы. + +## Runtime Commands + +Команды являются неизменяемыми инфраструктурными намерениями. + +Они описывают, что необходимо выполнить. + +Они не выполняют операцию самостоятельно. + +--- + +## Command Dispatcher + +Dispatcher принимает команду и передаёт её будущей реализации Runtime. + +Он не содержит бизнес-логики. + +--- + +## Runtime Events + +События являются неизменяемыми инфраструктурными фактами. + +Они описывают уже произошедшее состояние транспорта. + +--- + +## Event Publisher + +Publisher передаёт событие заинтересованным потребителям. + +Он не определяет реакцию потребителя. + +--- + +## WebSocket Transport + +Transport продолжает отвечать только за низкоуровневое соединение и транспортные сообщения. + +--- + +## WebSocket Session + +Session продолжает отвечать за жизненный цикл WebSocket-сессии. + +--- + +## Subscription Manager + +Subscription Manager продолжает отвечать за восстановление и очистку активных подписок. + +--- + +# Изменённые файлы + +В рамках Build изменены только два файла. + +## Runtime Protocol Layer + +```text +src/market_data/acquisition/runtime/websocket_protocol.py +``` + +Добавлены: + +- `AcquisitionRuntimeCommand`; +- `AcquisitionRuntimeEvent`; +- `AcquisitionRuntimeCommandDispatcherProtocol`; +- `AcquisitionRuntimeEventPublisherProtocol`. + +Существующие Protocol сохранены без изменения поведения: + +- `WebSocketTransportProtocol`; +- `WebSocketSessionProtocol`; +- `WebSocketSubscriptionManagerProtocol`. + +--- + +## Unit-тесты Runtime Protocol + +```text +tests/unit/market_data/acquisition/runtime/test_websocket_protocol.py +``` + +Добавлены тестовые реализации: + +- `FakeRuntimeCommandDispatcher`; +- `FakeRuntimeEventPublisher`. + +Добавлены проверки: + +- полная реализация dispatcher соответствует Protocol; +- полная реализация publisher соответствует Protocol; +- неполная реализация dispatcher не соответствует Protocol; +- неполная реализация publisher не соответствует Protocol. + +Все существующие тесты WebSocket Protocol сохранены. + +--- + +# Файлы, которые не изменялись + +Аудит подтвердил отсутствие необходимости менять: + +```text +src/market_data/acquisition/runtime/runtime_commands.py +src/market_data/acquisition/runtime/runtime_events.py +src/market_data/acquisition/runtime/transport_messages.py +``` + +Их существующее поведение полностью соответствует новому Protocol Layer. + +Также не изменялись: + +- Runtime Supervisor; +- Reconnect; +- Scheduler; +- Heartbeat; +- Trades Feed; +- WebSocket Adapters; +- Trade Stream Consistency; +- Trade Recovery; +- Acquisition Service; +- Acquisition Registry. + +--- + +# Unit-тестирование + +Новый Runtime Protocol Layer был покрыт структурными unit-тестами. + +Проверялись следующие сценарии: + +- реализация WebSocket Transport удовлетворяет Protocol; +- реализация WebSocket Session удовлетворяет Protocol; +- реализация Subscription Manager удовлетворяет Protocol; +- реализация Command Dispatcher удовлетворяет Protocol; +- реализация Event Publisher удовлетворяет Protocol; +- неполный Transport отклоняется; +- неполный Command Dispatcher отклоняется; +- неполный Event Publisher отклоняется. + +Результат локального теста: + +```text +8 passed +``` + +--- + +# Регрессионное тестирование Runtime Layer + +После добавления новых контрактов выполнен полный прогон тестов каталога Runtime. + +Проверены: + +- Runtime Commands; +- Runtime Events; +- Transport Messages; +- WebSocket Protocols; +- новые dispatcher и publisher Protocol. + +Результат: + +```text +34 passed +``` + +Это подтверждает, что расширение Protocol Layer не изменило существующее поведение Runtime. + +--- + +# Расширенное регрессионное тестирование + +Дополнительно выполнен совместный прогон трёх связанных подсистем: + +```text +Runtime +Consistency +Recovery +``` + +Результат: + +```text +136 passed +``` + +Тем самым подтверждено: + +- Runtime Protocol Layer работает корректно; +- Trade Stream Consistency не затронута; +- Trade Stream State Store не затронут; +- Trade Recovery не затронута; +- новые контракты не создали циклических или скрытых зависимостей. + +--- + +# Repository-wide аудит имён + +После первоначальной реализации был выполнен поиск по репозиторию. + +Аудит выявил существующую глобальную модель: + +```text +src.runtime_events.models.RuntimeEvent +``` + +Поэтому первоначальные общие имена: + +```text +RuntimeCommand +RuntimeEvent +RuntimeCommandDispatcherProtocol +RuntimeEventPublisherProtocol +``` + +были уточнены. + +Итоговые имена: + +```text +AcquisitionRuntimeCommand +AcquisitionRuntimeEvent +AcquisitionRuntimeCommandDispatcherProtocol +AcquisitionRuntimeEventPublisherProtocol +``` + +Это разделяет: + +- Acquisition Runtime transport events; +- глобальные application runtime events. + +Терминологическая неоднозначность полностью устранена. + +--- + +# Обратная совместимость + +Build сохраняет полную обратную совместимость. + +Не изменились: + +- сигнатуры существующих WebSocket Protocol; +- модели Runtime Commands; +- модели Runtime Events; +- модели Transport Messages; +- алгоритмы Stream Consistency; +- State Store; +- Recovery Pipeline; +- Trades Feed; +- каноническая модель `Trade`. + +Новые контракты являются исключительно расширением публичного Protocol Layer. + +Существующие реализации не обязаны реализовывать новые Protocol до момента их подключения в последующих Build. + +--- + +# Производительность + +Build не добавляет runtime-операций и не влияет на производительность системы. + +Типовые alias существуют только на уровне статической типизации. + +Protocol также не создают дополнительного runtime-поведения, за исключением стандартной structural runtime-проверки через `@runtime_checkable`, уже применявшейся в существующем коде. + +Следовательно Build не влияет на: + +- задержку обработки сообщений; +- пропускную способность WebSocket; +- использование памяти; +- обработку Trade Stream; +- Recovery Pipeline. + +--- + +# Подтверждённые архитектурные инварианты + +## Runtime не знает предметную область + +Новые контракты принимают только Acquisition Runtime Commands и Events. + +Они не используют модели Trade, Quote, Candle или Order Book. + +--- + +## Consistency не зависит от Runtime + +`TradeStreamConsistencyController` продолжает зависеть только от собственного State Store. + +--- + +## Recovery не зависит от Runtime + +`TradeRecoveryController` продолжает использовать только `TradeStreamConsistencyProtocol`. + +--- + +## Команды отделены от исполнения + +Command-модели описывают намерение. + +Dispatcher предоставляет контракт передачи команды. + +Реализация выполнения отсутствует в настоящем Build. + +--- + +## События отделены от реакции + +Event-модели описывают инфраструктурный факт. + +Publisher предоставляет контракт публикации. + +Реакция потребителей отсутствует в настоящем Build. + +--- + +## Runtime Service не входит в Scope + +Настоящий Build не создаёт сервисную реализацию и не определяет полный сервисный API. + +Эта ответственность закреплена за Build 060.22. + +--- + +# Что не входит в Scope Build + +Настоящий Build сознательно ограничен расширением Runtime Protocol Layer. + +В него не входят: + +- реализация Command Dispatcher; +- реализация Event Publisher; +- Runtime Service; +- Runtime Supervisor; +- запуск и остановка Runtime; +- обработка команд; +- доставка событий подписчикам; +- очередь событий; +- Reconnect orchestration; +- Heartbeat orchestration; +- Scheduler; +- интеграция Trades Feed; +- интеграция Acquisition Service; +- автоматический запуск Recovery; +- восстановление подписок после reconnect; +- Composition Root. + +Отсутствие перечисленных компонентов является осознанной границей Build. + +--- + +# Архитектурное значение Build + +Несмотря на небольшой объём изменения production-кода, Build 060.21 является важным контрактным этапом. + +До него Runtime Layer обладал моделями команд и событий, но не имел формального интерфейса их передачи. + +После завершения Build появляются две стабильные точки расширения: + +```text +AcquisitionRuntimeCommandDispatcherProtocol +AcquisitionRuntimeEventPublisherProtocol +``` + +Именно через них последующие реализации смогут: + +- принимать инфраструктурные команды; +- публиковать инфраструктурные события; +- сохранять независимость от конкретного транспорта; +- оставаться изолированными от предметной области Market Data Acquisition. + +Это создаёт основу для Build 060.22 без преждевременного внедрения сервисной реализации. + +--- + +# Связь с последующими Build + +## Build 060.22 — Runtime Service Integration + +Будущая реализация Runtime Service должна использовать новые Protocol как публичные границы передачи команд и событий. + +--- + +## Build 060.23 — Acquisition Integration + +Acquisition Layer сможет использовать Runtime Service через утверждённые контракты без прямой зависимости от внутренней реализации Runtime. + +--- + +## Build 060.24 — Reconnect & Runtime Recovery + +Reconnect, resubscribe и запуск Recovery смогут строиться на типизированных командах и событиях без изменения существующих контрактов Consistency и Recovery. + +--- + +## Build 060.25 — Integration & Regression + +Будет выполнена полная проверка взаимодействия Runtime, Acquisition, Consistency и Recovery. + +--- + +# Заключение + +Build 060.21 завершает формирование базового Runtime Protocol Layer внутри подсистемы **Market Data Acquisition**. + +В рамках этапа не создавалась новая реализация и не менялось функциональное поведение системы. + +Вместо этого были введены две узкие инфраструктурные границы: + +```text +AcquisitionRuntimeCommandDispatcherProtocol +AcquisitionRuntimeEventPublisherProtocol +``` + +Они дополняют уже существующие: + +```text +WebSocketTransportProtocol +WebSocketSessionProtocol +WebSocketSubscriptionManagerProtocol +``` + +и создают полный контрактный фундамент для будущего Runtime Service. + +Build сохранил независимость: + +- Runtime от бизнес-моделей; +- Consistency от Runtime; +- Recovery от Runtime; +- Feed от внутренней реализации транспорта. + +Все изменения подтверждены unit- и regression-тестами. + +--- + +# Итог Build + +После завершения Build 060.21 система обладает следующими возможностями. + +✓ Определён полный типовой набор Acquisition Runtime Commands. + +✓ Определён полный типовой набор Acquisition Runtime Events. + +✓ Введён публичный Protocol передачи Runtime-команд. + +✓ Введён публичный Protocol публикации Runtime-событий. + +✓ Устранён терминологический конфликт с глобальной подсистемой `src/runtime_events`. + +✓ Существующие Runtime Commands и Events сохранены без изменений. + +✓ Consistency и Recovery сохранены без изменений. + +✓ Runtime regression завершён результатом `34 passed`. + +✓ Расширенный regression завершён результатом `136 passed`. + +Build **060.21 — Runtime Protocol Integration** считается полностью завершённым и готовым к фиксации в Git. diff --git a/docs/migrations/build_060_21_architecture.md b/docs/migrations/build_060_21_architecture.md new file mode 100644 index 0000000..404fefa --- /dev/null +++ b/docs/migrations/build_060_21_architecture.md @@ -0,0 +1,931 @@ +# Build 060.21 — Runtime Integration Contracts + +## Архитектурное обоснование + +--- + +# Назначение документа + +Данный документ фиксирует архитектурные решения, принимаемые перед началом реализации **Build 060.21 — Runtime Protocol Integration**. + +Build 060.20.1 завершил важнейший этап архитектурной декомпозиции подсистемы **Trades Feed**. + +В результате архитектурного аудита было принято решение перенести владение инфраструктурным состоянием проверки согласованности потока сделок (**Trade Stream Consistency**) из общего Runtime Registry в специализированное хранилище состояний: + +```text +TradeStreamStateStore +``` + +Тем самым Runtime окончательно перестал владеть состоянием предметной области. + +После завершения данного этапа стало возможным провести повторный аудит уже всей подсистемы получения сделок в контексте полной архитектуры Dzentra. + +Основной целью Build 060.21 является определение архитектурных контрактов взаимодействия между уже реализованными подсистемами без изменения их ответственности. + +Именно этот Build завершает проектирование слоя Runtime и подготавливает основу для его прикладной реализации в последующих этапах. + +--- + +# Предпосылки Build 060.21 + +К началу данного Build в проекте уже реализованы практически все фундаментальные компоненты подсистемы получения сделок. + +Независимо друг от друга существуют: + +```text +WebSocket Runtime + +WebSocket Adapter + +Trades Feed + +Trade Stream Consistency + +Trade Recovery +``` + +Каждая из перечисленных подсистем имеет собственную область ответственности, собственный публичный API и полностью покрыта модульными тестами. + +При этом отсутствует единый слой интеграции между транспортной инфраструктурой и предметной областью получения рыночных данных. + +Именно отсутствие такого слоя не позволяет перейти к реализации полноценного жизненного цикла получения Trade Stream. + +--- + +# Цель Build 060.21 + +Build 060.21 не создаёт нового поведения системы. + +Он не добавляет: + +- новые алгоритмы; +- новые модели; +- новые механизмы обработки данных. + +Его задача значительно более фундаментальна. + +Build формализует публичные контракты взаимодействия между уже существующими подсистемами. + +После завершения этапа каждая подсистема будет взаимодействовать исключительно через утверждённые Protocol. + +Это позволит: + +- полностью устранить риск циклических зависимостей; +- изолировать Runtime от бизнес-логики получения данных; +- обеспечить независимое тестирование компонентов; +- подготовить систему к дальнейшей реализации Runtime Service. + +--- + +# Место Build 060.21 в общей дорожной карте + +Развитие подсистемы Trades Feed выполняется последовательно. + +```text +060.18 + +Trade Stream Consistency +``` + +↓ + +```text +060.19 + +Trade Recovery +``` + +↓ + +```text +060.20 + +Trade Runtime (первая архитектура) +``` + +↓ + +```text +060.20.1 + +Перенос владельца состояния +``` + +↓ + +```text +060.21 + +Runtime Integration Contracts +``` + +↓ + +```text +060.22 + +Trade Runtime Service +``` + +↓ + +```text +060.23 + +Acquisition Integration +``` + +↓ + +```text +060.24 + +Reconnect & Runtime Recovery +``` + +↓ + +```text +060.25 + +Integration & Regression +``` + +↓ + +```text +060.26 + +Final Documentation +``` + +Каждый этап вводит только один новый архитектурный уровень. + +Именно это позволяет сохранять стабильность системы на протяжении всей миграции. + +--- + +# Архитектурный результат Build 060.20.1 + +После завершения предыдущего этапа архитектура приобрела следующий вид. + +```text +Trade + │ + ▼ +TradeStreamConsistencyController + │ + ▼ +TradeStreamStateStore + │ + ▼ +TradeStreamState +``` + +При этом Runtime полностью перестал владеть инфраструктурным состоянием. + +Это решение соответствует общей архитектуре Dzentra. + +Каждый модуль отвечает исключительно за собственную область ответственности. + +Никакие инфраструктурные компоненты не владеют состоянием предметной области. + +--- + +# Что остаётся нерешённым + +Несмотря на завершённую декомпозицию подсистем Consistency и Recovery, между Runtime и Trades Feed по-прежнему отсутствует архитектурный слой взаимодействия. + +На сегодняшний день Runtime существует как полностью самостоятельная транспортная подсистема. + +Trades Feed также существует как самостоятельная подсистема получения рыночных данных. + +Однако отсутствует формализованный контракт, определяющий: + +- каким образом Feed использует Runtime; +- каким образом Runtime предоставляет свои возможности; +- где проходит граница ответственности между транспортной инфраструктурой и бизнес-логикой получения сделок. + +Именно устранение этой неопределённости является предметом Build 060.21. + +--- + +# Основной архитектурный принцип + +В ходе архитектурного аудита было принято ключевое решение, которое определяет всю дальнейшую разработку Runtime. + +**Runtime остаётся полностью инфраструктурным слоем.** + +Это означает: + +Runtime предоставляет инфраструктурные возможности. + +Runtime не управляет предметной областью. + +Runtime ничего не знает о сделках. + +Runtime ничего не знает о свечах. + +Runtime ничего не знает о котировках. + +Runtime ничего не знает о механизмах проверки последовательности сообщений. + +Runtime ничего не знает о восстановлении пропусков. + +Все знания о предметной области остаются внутри подсистем **Market Data Acquisition**. + +Именно это решение становится фундаментом всех последующих Build. + +--- + +# Почему Runtime не должен зависеть от Acquisition + +На первый взгляд может показаться естественным сделать Runtime частью Trades Feed. + +Однако подобное решение приводит к нарушению слоистой архитектуры. + +Рассмотрим зависимости. + +Правильное направление выглядит следующим образом: + +```text +Trades Feed + │ + ▼ +Runtime Protocol + │ + ▼ +Runtime Implementation +``` + +То есть предметная область использует инфраструктуру. + +Но никогда наоборот. + +Если же Runtime начнёт зависеть от Feed, получится следующая схема: + +```text +Runtime + │ + ▼ +Trades Feed +``` + +В этом случае инфраструктура начинает знать о прикладной области. + +Позже Runtime неизбежно начнёт содержать: + +- обработку Trade; +- обработку Quote; +- обработку Candles; +- обработку Recovery; +- обработку Consistency. + +Фактически Runtime превратится во второй Acquisition Layer. + +Подобное решение противоречит принципу Single Responsibility и разрушает возможность повторного использования Runtime другими потоками данных. + +--- + +# Архитектурная роль Runtime + +После завершения Build 060.21 Runtime окончательно получает следующую ответственность. + +```text +Runtime отвечает исключительно за: + +• жизненный цикл WebSocket; + +• транспортные подключения; + +• отправку команд; + +• получение транспортных сообщений; + +• переподключение; + +• heartbeat; + +• генерацию инфраструктурных событий. +``` + +Runtime не принимает решений относительно содержимого сообщений. + +Runtime не знает, что именно передаётся через WebSocket. + +Для него существует лишь поток транспортных данных. + +--- + +# Архитектурная роль Trades Feed + +В отличие от Runtime, Trades Feed отвечает исключительно за предметную область получения сделок. + +Именно здесь сосредоточена логика: + +- подписки на поток сделок; +- обработки транспортных сообщений; +- преобразования сообщений в каноническую модель; +- проверки последовательности; +- восстановления пропусков; +- передачи данных в следующие уровни системы. + +Таким образом Runtime предоставляет инфраструктуру, а Feed определяет, каким образом эта инфраструктура используется. + +--- + +# Новый слой интеграции + +Для устранения прямых зависимостей вводится отдельный слой интеграционных контрактов. + +После Build 060.21 взаимодействие будет выглядеть следующим образом. + +```text + Runtime + + Runtime Protocol Layer + ▲ + │ + │ + Trades Runtime Adapter + ▲ + │ + │ + Trades Feed +``` + +Feed зависит исключительно от публичных Protocol. + +Runtime также реализует только Protocol. + +Ни одна сторона не знает о внутреннем устройстве другой. + +--- + +# Какие Protocol должны появиться + +После архитектурного анализа становится очевидно, что существующих Protocol недостаточно. + +В настоящий момент имеются лишь транспортные контракты. + +```text +WebSocketTransportProtocol + +WebSocketSessionProtocol + +WebSocketSubscriptionManagerProtocol +``` + +Они описывают низкоуровневые механизмы работы WebSocket. + +Но они ничего не говорят о жизненном цикле Runtime как сервиса. + +Следовательно требуется следующий уровень абстракции. + +--- + +# Runtime Service Protocol + +Главным новым контрактом становится Runtime Service Protocol. + +Именно он описывает полный жизненный цикл Runtime. + +Примерный набор операций выглядит следующим образом. + +```text +connect() + +disconnect() + +subscribe() + +unsubscribe() + +send() + +is_connected() + +connection_state() +``` + +Обращает на себя внимание важная особенность. + +Все перечисленные методы относятся исключительно к транспортной инфраструктуре. + +Ни один из них не содержит понятий: + +- Trade; +- Quote; +- Candle; +- Recovery; +- Consistency. + +Тем самым сохраняется полная независимость Runtime. + +--- + +# Runtime Event Protocol + +Следующим уровнем становятся инфраструктурные события. + +На сегодняшний день существуют отдельные Event-модели. + +Однако отсутствует единый контракт их использования. + +После Build 060.21 Runtime публикует только инфраструктурные события. + +Например: + +```text +Connected + +Disconnected + +ReconnectStarted + +ReconnectFinished + +HeartbeatTimeout + +TransportError +``` + +Получатель сам принимает решение, что делать с этими событиями. + +Runtime ничего не знает о реакции системы. + +--- + +# Runtime Command Protocol + +Аналогичным образом формализуется слой команд. + +Вместо непосредственного вызова внутренних механизмов Runtime вводится единый командный контракт. + +Например: + +```text +ConnectCommand + +DisconnectCommand + +SubscribeCommand + +UnsubscribeCommand + +ShutdownCommand +``` + +Все команды являются транспортными. + +Они не содержат бизнес-логики. + +--- + +# Runtime Message Flow + +После завершения Build поток взаимодействия становится полностью линейным. + +```text +Trades Feed + + │ + + ▼ + +Runtime Service Protocol + + │ + + ▼ + +Runtime + + │ + + ▼ + +WebSocket + + │ + + ▼ + +Exchange +``` + +Обратный поток строится аналогичным образом. + +```text +Exchange + + │ + + ▼ + +Runtime + + │ + + ▼ + +Runtime Events + + │ + + ▼ + +Trades Feed + + │ + + ▼ + +Consistency + + │ + + ▼ + +Recovery +``` + +Каждый уровень отвечает только за собственную часть обработки. + +Никакие слои не пересекают границы ответственности. + +--- + +# Build Scope + +В рамках Build 060.21 производится исключительно интеграция Runtime Protocol Layer. + +Изменения не затрагивают: + +- Recovery; +- Consistency; +- Validation; +- Runtime Supervisor; +- Reconnect; +- Scheduler; +- Heartbeat. + +Все перечисленные компоненты продолжают существовать в прежнем виде. + +Изменяется исключительно способ их последующего подключения к Runtime. + +--- + +# План реализации Build 060.21 + +Работы выполняются небольшими независимыми этапами. + +## Этап 060.21.1 + +Аудит существующих Runtime Protocol. + +Проверка: + +- websocket_protocol.py; +- runtime_commands.py; +- runtime_events.py. + +--- + +## Этап 060.21.2 + +Расширение Runtime Protocol Layer. + +Добавляются отсутствующие инфраструктурные контракты. + +--- + +## Этап 060.21.3 + +Создание Runtime Service Protocol. + +Появляется единый контракт взаимодействия с Runtime. + +--- + +## Этап 060.21.4 + +Интеграция существующих компонентов Runtime через новые Protocol. + +Без изменения поведения. + +--- + +## Этап 060.21.5 + +Полный прогон Runtime Unit Tests. + +Подтверждение отсутствия регрессии. + +--- + +# Ожидаемый результат + +После завершения Build 060.21 архитектура Runtime приобретает окончательный вид. + +```text + Acquisition Layer + + Trades Feed / Quotes Feed / OHLC Feed + │ + │ + ▼ + Runtime Service Protocol + │ + ▼ + ┌────────────────────────────┐ + │ Runtime Layer │ + │ │ + │ Commands │ + │ Events │ + │ Session │ + │ Transport │ + │ Subscription Manager │ + └────────────────────────────┘ + │ + ▼ + Exchange Transport +``` + +Все последующие Build будут использовать именно этот публичный слой. + +Никаких дополнительных прямых зависимостей от Runtime больше вводиться не будет. + +--- + +# Архитектурные гарантии после Build 060.21 + +После завершения данного этапа система получает следующие гарантии. + +## 1. Инверсия зависимостей полностью соблюдается + +Ни один инфраструктурный компонент не знает о предметной области. + +Runtime не импортирует: + +- Trades Feed; +- Quotes Feed; +- Candles Feed; +- Recovery; +- Consistency. + +--- + +## 2. Runtime становится полностью переиспользуемым + +Любой поток данных сможет использовать Runtime без модификации его кода. + +Например: + +```text +Trades Feed + +Quotes Feed + +OHLC Feed + +Order Book Feed + +Funding Feed + +Index Feed +``` + +Все они будут работать через одинаковый Runtime Service Protocol. + +--- + +## 3. Runtime становится тестируемым независимо + +Все Runtime Unit Tests могут запускаться без: + +- Feed; +- Acquisition; +- Exchange Adapter; +- Recovery. + +Тестируется исключительно инфраструктурное поведение. + +--- + +## 4. Feed становится независимым от реализации Runtime + +Trades Feed знает только публичный контракт Runtime. + +Следовательно Runtime можно заменить другой реализацией без изменения Feed. + +Например: + +```text +Current WebSocket Runtime + +↓ + +Future FIX Runtime + +↓ + +Future gRPC Runtime + +↓ + +Future Simulator Runtime +``` + +Feed останется неизменным. + +--- + +## 5. Build 060.22 становится локальным + +Следующий Build будет посвящён реализации Runtime Service. + +Поскольку интерфейсы уже определены в Build 060.21, изменения затронут только внутреннюю реализацию Runtime. + +Публичные контракты останутся неизменными. + +Это существенно снижает риск регрессии. + +--- + +# Итог + +Build 060.21 завершает формирование архитектурного слоя Runtime Protocol Integration. + +В результате: + +- Runtime окончательно отделяется от предметной области Market Data Acquisition; +- взаимодействие осуществляется исключительно через публичные Protocol; +- все транспортные зависимости становятся инвертированными; +- Runtime превращается в независимый инфраструктурный сервис; +- создаётся стабильная контрактная база для последующих Build 060.22–060.25 без необходимости дальнейшего изменения архитектуры Protocol Layer. + +--- + +# Приложение A. Эволюция архитектуры Runtime + +## До Build 060.21 + +```text + Trades Feed + │ + ▼ + WebSocketTransportProtocol + │ + ▼ + Runtime Components + + Commands + Events + Session + Transport +``` + +Недостатки данной схемы: + +- отсутствует единая точка входа в Runtime; +- Feed вынужден знать набор отдельных инфраструктурных компонентов; +- невозможно заменить Runtime целиком; +- отсутствует единый сервисный контракт. + +--- + +## После Build 060.21 + +```text + Trades Feed + │ + ▼ + RuntimeServiceProtocol + │ + ┌────────────────┴────────────────┐ + │ │ + ▼ ▼ + Runtime Commands Runtime Events + │ │ + └────────────────┬────────────────┘ + ▼ + Runtime Implementation + │ + ▼ + WebSocketTransportProtocol + │ + ▼ + WebSocket + │ + ▼ + Exchange +``` + +В новой архитектуре весь Runtime скрывается за единым публичным сервисным контрактом. + +Trades Feed больше не зависит от внутренних компонентов Runtime. + +--- + +# Приложение B. Dependency Graph + +После Build 060.21 зависимости приобретают следующий вид. + +```text +Trades Feed + │ + ▼ +RuntimeServiceProtocol + │ + ▼ +Runtime + │ + ├──────────────► Runtime Commands + │ + ├──────────────► Runtime Events + │ + ├──────────────► Session Protocol + │ + ├──────────────► Subscription Protocol + │ + └──────────────► Transport Protocol + │ + ▼ + Exchange Adapter +``` + +Ни одна зависимость не направлена обратно к Feed. + +--- + +# Приложение C. Build Boundary + +В Build 060.21 разрешается изменять только следующие компоненты. + +```text +runtime/ + websocket_protocol.py + runtime_commands.py + runtime_events.py +``` + +При необходимости допускается добавление новых файлов Protocol Layer. + +Не допускается изменение: + +```text +consistency/ +recovery/ +validation/ +processing/ +analytics/ +storage/ +``` + +Также не изменяются: + +```text +Trades Feed + +Recovery Controller + +Consistency Controller + +Exchange Adapter +``` + +Их интеграция будет выполняться на следующих этапах Roadmap. + +--- + +# Приложение D. Следующие этапы + +После завершения Build 060.21 дальнейшая последовательность работ выглядит следующим образом. + +```text +060.22 +Runtime Service Integration + +↓ + +060.23 +Acquisition Integration + +↓ + +060.24 +Reconnect & Runtime Recovery + +↓ + +060.25 +Integration & Regression + +↓ + +060.26 +Final Documentation +``` + +Таким образом Build 060.21 завершает исключительно проектирование и интеграцию контрактов Runtime Protocol Layer, создавая стабильную основу для последующей реализации Runtime Service без нарушения уже сформированной архитектуры подсистемы Market Data Acquisition. \ No newline at end of file