Build 060.21: integrate acquisition runtime protocols

This commit is contained in:
2026-07-26 09:20:24 +03:00
parent ad6631f773
commit 433ac5d375
4 changed files with 1914 additions and 2 deletions

View File

@@ -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):
@@ -77,3 +119,47 @@ class WebSocketSubscriptionManagerProtocol(Protocol):
async def clear_subscriptions(self) -> None:
"""Очистить runtime-состояние активных подписок."""
...
@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.
"""
...

View File

@@ -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)
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,
)

View File

@@ -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.

View File

@@ -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.22060.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.