Files
dzentra_bot/docs/migrations/build_060_22.md

1225 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build 060.22 — Acquisition Runtime Service
**Engineering Migration Report**
---
# Контроль документа
| Свойство | Значение |
|----------|----------|
| Build | 060.22 |
| Название | Acquisition Runtime Service |
| Статус | Completed |
| Проект | Dzentra |
| Подсистема | Market Data Acquisition |
| Компонент | Acquisition Runtime Service Layer |
| Версия | 1.0 |
---
# Связанные документы
- `build_060_22_architecture.md` — архитектурная спецификация Build.
- `build_060_21.md` — Engineering Migration Report предыдущего Build.
- `build_060_21_architecture.md` — спецификация Runtime Integration Contracts.
- `build_060_20_1.md` — Engineering Migration Report корректирующего Build владения состоянием Trade Stream.
---
# Цель Build
Build 060.21 завершил формирование контрактного слоя Acquisition Runtime.
После предыдущего этапа в системе уже существовали:
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeEvent
AcquisitionRuntimeCommandDispatcherProtocol
AcquisitionRuntimeEventPublisherProtocol
```
Также были доступны базовые WebSocket-контракты:
```text
WebSocketTransportProtocol
WebSocketSessionProtocol
WebSocketSubscriptionManagerProtocol
```
Однако Runtime Layer оставался декларативным.
Команды описывали инфраструктурные намерения, но в системе отсутствовал производственный компонент, который:
- принимал типизированную Runtime-команду;
- определял её конкретный тип;
- выбирал соответствующую инфраструктурную зависимость;
- выполнял одну детерминированную операцию;
- сохранял независимость от Trades Feed, Consistency и Recovery.
Главной задачей Build 060.22 стало создание первого исполняемого сервисного компонента Acquisition Runtime:
```text
AcquisitionRuntimeService
```
Одновременно был введён отдельный публичный контракт сервисного уровня:
```text
AcquisitionRuntimeServiceProtocol
```
После завершения Build система получает:
- единый сервис исполнения Acquisition Runtime Commands;
- детерминированную маршрутизацию всех шести существующих команд;
- отдельный публичный Protocol сервиса;
- расширенный контракт управления WebSocket-подписками;
- полное unit-покрытие нового сервисного слоя;
- подтверждённую регрессионную совместимость Runtime, Consistency, Recovery и Feeds.
При этом Build сознательно не выполняет интеграцию Runtime Service в корневой Acquisition Service или Trades Feed.
Эта задача относится к Build 060.23.
---
# Предпосылки
К началу настоящего Build Runtime Layer уже обладал полным набором неизменяемых команд:
```text
ConnectCommand
DisconnectCommand
SubscribeCommand
UnsubscribeCommand
SendTextCommand
SendBinaryCommand
```
Также существовали инфраструктурные события:
```text
ConnectedEvent
DisconnectedEvent
ConnectFailedEvent
MessageReceivedEvent
MessageSentEvent
ReconnectStartedEvent
ReconnectCompletedEvent
ReconnectFailedEvent
HeartbeatTimeoutEvent
```
Build 060.21 определил публичные границы передачи команд и событий, но сознательно не создавал исполняемую реализацию.
Архитектура выглядела следующим образом:
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeCommandDispatcherProtocol
(реализация отсутствует)
```
Таким образом следующий этап должен был закрыть именно сервисную границу между типизированными командами и существующими инфраструктурными компонентами.
---
# Результаты архитектурного аудита
Перед началом реализации был выполнен аудит следующих компонентов:
```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
src/market_data/acquisition/runtime/supervisor.py
src/market_data/acquisition/runtime/reconnect.py
src/market_data/acquisition/runtime/scheduler.py
src/market_data/acquisition/runtime/heartbeat.py
```
Дополнительно были проанализированы:
```text
src/market_data/acquisition/service.py
src/market_data/acquisition/protocol.py
src/market_data/acquisition/registry.py
```
а также соответствующие unit-тесты.
Аудит подтвердил следующее.
## Runtime orchestration ещё не реализован
Файлы:
```text
supervisor.py
reconnect.py
scheduler.py
heartbeat.py
```
не содержали производственной реализации.
Следовательно Build 060.22 не являлся рефакторингом существующего Runtime Service.
Он создавал первую минимальную сервисную реализацию с нуля.
---
## Supervisor преждевременен
На раннем этапе рассматривалась возможность реализовать сервис внутри:
```text
supervisor.py
```
Данный вариант был отклонён.
Supervisor является компонентом более высокого уровня и должен координировать:
- Runtime Service;
- reconnect;
- heartbeat;
- scheduler;
- lifecycle длительно работающей подсистемы.
Поскольку перечисленные механизмы ещё не реализованы, использование Supervisor в Build 060.22 создало бы преждевременную orchestration-логику.
Поэтому был введён отдельный тонкий сервис:
```text
AcquisitionRuntimeService
```
---
## Registry для Runtime Service не требуется
Существующие Feed Registry предназначены для выбора одной реализации из нескольких зарегистрированных источников.
Runtime Service в текущей архитектуре существует в единственном экземпляре и не представляет семейство взаимозаменяемых Feed-реализаций.
Создание отдельного Runtime Registry было признано искусственным и исключено из Scope Build.
---
## Корневые Acquisition-файлы не изменяются
Аудит подтвердил отсутствие необходимости изменять:
```text
src/market_data/acquisition/service.py
src/market_data/acquisition/protocol.py
src/market_data/acquisition/registry.py
```
Интеграция нового Runtime Service с Acquisition Layer относится к следующему этапу:
```text
Build 060.23 — Acquisition Integration
```
---
# Архитектурное решение
По результатам аудита была утверждена следующая модель.
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeService
┌─────┼──────────────┐
▼ ▼ ▼
Session Transport Subscription Manager
```
`AcquisitionRuntimeService` реализует одновременно:
```text
AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeCommandDispatcherProtocol
```
Сервис не реализует WebSocket самостоятельно.
Он только делегирует каждую команду одной заранее определённой инфраструктурной зависимости.
---
# Правило уникальности имён файлов
Build 060.22 продолжает обязательное архитектурное правило проекта Dzentra:
> Во всём репозитории не допускается создание нескольких новых файлов с одинаковым именем независимо от их расположения в каталогах.
Единственным служебным исключением остаётся:
```text
__init__.py
```
Поэтому Build сознательно не создаёт файлы с общими именами:
```text
service.py
protocol.py
test_service.py
```
Вместо них добавлены уникальные имена:
```text
acquisition_runtime_service.py
acquisition_runtime_service_protocol.py
test_acquisition_runtime_service.py
```
Имена однозначно отражают назначение файлов без необходимости учитывать родительский каталог.
Repository-wide поиск показал, что в проекте уже существуют старые файлы с общими именами, созданные до введения данного правила.
Build 060.22 не добавляет новых файлов с такими именами и не выполняет ретроспективное переименование существующего рабочего кода вне согласованного Scope.
---
# AcquisitionRuntimeServiceProtocol
В рамках Build создан новый публичный контракт:
```text
AcquisitionRuntimeServiceProtocol
```
Файл:
```text
src/market_data/acquisition/runtime/
acquisition_runtime_service_protocol.py
```
Protocol определяет единственную публичную операцию:
```python
async def dispatch(
command: AcquisitionRuntimeCommand,
) -> None
```
Другие публичные методы не вводятся.
В частности отсутствуют:
```text
connect()
disconnect()
subscribe()
unsubscribe()
send()
```
Все операции Runtime выражаются исключительно через типизированные команды.
Это исключает появление второго параллельного API для одной и той же инфраструктурной операции.
---
# Почему Service Protocol существует отдельно
В Build 060.21 уже был добавлен:
```text
AcquisitionRuntimeCommandDispatcherProtocol
```
Он определяет инфраструктурную способность принимать команды.
Build 060.22 дополнительно вводит:
```text
AcquisitionRuntimeServiceProtocol
```
Данный контракт фиксирует отдельную сервисную границу Runtime Layer.
Такое разделение позволяет последующим компонентам зависеть именно от сервисной абстракции, а не от конкретной реализации.
В будущем под тем же Protocol могут существовать различные реализации:
```text
Simple Acquisition Runtime Service
Queued Acquisition Runtime Service
Supervised Acquisition Runtime Service
```
Build 060.22 реализует только минимальный прямой вариант.
---
# AcquisitionRuntimeService
Главным production-компонентом Build становится:
```text
AcquisitionRuntimeService
```
Файл:
```text
src/market_data/acquisition/runtime/
acquisition_runtime_service.py
```
Сервис является тонким stateless-координатором.
Он:
- принимает одну типизированную команду;
- определяет её конкретный тип;
- вызывает соответствующую инфраструктурную зависимость;
- завершает выполнение после завершения делегированной операции.
Сервис не:
- создаёт фоновые задачи;
- хранит очередь команд;
- выполняет retries;
- управляет reconnect;
- запускает heartbeat;
- хранит подписки;
- обрабатывает Trade;
- вызывает Consistency;
- вызывает Recovery.
---
# Зависимости сервиса
Все зависимости передаются через конструктор.
```text
AcquisitionRuntimeService
├── WebSocketSessionProtocol
├── WebSocketTransportProtocol
├── WebSocketSubscriptionManagerProtocol
└── AcquisitionRuntimeEventPublisherProtocol
```
Сервис не создаёт зависимости самостоятельно.
Создание графа объектов относится к Composition Root и не входит в Scope Build 060.22.
---
# Почему Event Publisher внедрён, но пока не используется
Конструктор Runtime Service принимает:
```text
AcquisitionRuntimeEventPublisherProtocol
```
Однако Build 060.22 не публикует события после выполнения команд.
Это осознанное решение.
Publisher включён в окончательную форму конструктора, чтобы последующие Build могли подключить публикацию событий без изменения публичной композиции сервиса.
При этом преждевременная event-orchestration не добавляется.
Unit-тест отдельно подтверждает, что Publisher пока остаётся неиспользованным.
---
# Расширение WebSocketSubscriptionManagerProtocol
Во время реализации было обнаружено расхождение между первоначальной архитектурной матрицей и фактическим контрактом Subscription Manager.
До Build Protocol содержал только:
```python
restore_subscriptions()
clear_subscriptions()
```
Но Runtime Service должен исполнять:
```text
SubscribeCommand
UnsubscribeCommand
```
Следовательно существующий контракт был недостаточен для детерминированной маршрутизации.
В Build `WebSocketSubscriptionManagerProtocol` локально расширен методами:
```python
async def subscribe(
subscription_key: str,
message: AcquisitionSubscriptionMessage,
) -> None
```
и:
```python
async def unsubscribe(
subscription_key: str,
message: AcquisitionSubscriptionMessage,
) -> None
```
Существующие методы:
```text
restore_subscriptions()
clear_subscriptions()
```
полностью сохранены.
Расширение является обратно совместимым с точки зрения существующих моделей данных и не изменяет поведение уже реализованных компонентов.
---
# AcquisitionSubscriptionMessage
Для операций подписки введён типовой alias:
```text
AcquisitionSubscriptionMessage
```
Он объединяет:
```text
TransportTextMessage
TransportBinaryMessage
```
Благодаря этому Subscription Manager принимает не произвольные `str | bytes`, а уже существующие неизменяемые модели транспортных сообщений.
Это обеспечивает соответствие моделям:
```text
SubscribeCommand
UnsubscribeCommand
```
и исключает повторную интерпретацию payload внутри Runtime Service.
---
# Официальная матрица маршрутизации
Build 060.22 реализует следующую детерминированную таблицу.
| Runtime Command | Действие |
|-----------------|----------|
| `ConnectCommand` | `WebSocketSessionProtocol.start()` |
| `DisconnectCommand` | `WebSocketSessionProtocol.stop()` |
| `SubscribeCommand` | `WebSocketSubscriptionManagerProtocol.subscribe(...)` |
| `UnsubscribeCommand` | `WebSocketSubscriptionManagerProtocol.unsubscribe(...)` |
| `SendTextCommand` | `WebSocketTransportProtocol.send(message.payload)` |
| `SendBinaryCommand` | `WebSocketTransportProtocol.send(message.payload)` |
Каждый тип команды имеет ровно один маршрут.
Сервис не выполняет дополнительные действия и не вызывает несколько зависимостей для одной команды.
---
# ConnectCommand
Маршрут:
```text
ConnectCommand
WebSocketSessionProtocol.start()
```
Runtime Service не вызывает `transport.connect()` напрямую.
Жизненный цикл соединения принадлежит Session.
---
# DisconnectCommand
Маршрут:
```text
DisconnectCommand
WebSocketSessionProtocol.stop()
```
Runtime Service не вызывает `transport.disconnect()` напрямую.
Завершение сессии остаётся обязанностью Session.
---
# SubscribeCommand
Маршрут:
```text
SubscribeCommand
WebSocketSubscriptionManagerProtocol.subscribe(
subscription_key,
message,
)
```
Runtime Service передаёт исходные значения без изменения.
Сервис не хранит список подписок и не сериализует сообщение.
---
# UnsubscribeCommand
Маршрут:
```text
UnsubscribeCommand
WebSocketSubscriptionManagerProtocol.unsubscribe(
subscription_key,
message,
)
```
Сервис не изменяет внутреннее состояние Subscription Manager самостоятельно.
---
# SendTextCommand
Маршрут:
```text
SendTextCommand
WebSocketTransportProtocol.send(str)
```
В Transport передаётся:
```text
command.message.payload
```
Runtime Service не вводит отдельный метод `send_text()` и не расширяет Transport Protocol без необходимости.
---
# SendBinaryCommand
Маршрут:
```text
SendBinaryCommand
WebSocketTransportProtocol.send(bytes)
```
В Transport передаётся бинарный payload без преобразования.
Существующий единый метод:
```python
send(message: str | bytes)
```
полностью покрывает оба типа сообщений.
---
# Обработка неизвестной команды
Тип `AcquisitionRuntimeCommand` ограничивает штатный публичный вход сервиса.
Однако Runtime Service также содержит защитную ветку для произвольного неподдерживаемого объекта.
В этом случае генерируется:
```text
TypeError
```
с диагностическим сообщением, включающим имя фактического типа.
Это гарантирует, что появление новой Runtime-команды потребует явного обновления маршрутизации и не останется незамеченным.
---
# Обработка исключений
Build не вводит собственную иерархию ошибок Runtime Service.
Любое исключение инфраструктурной зависимости распространяется вызывающему компоненту без обёртки.
Например:
```text
ConnectCommand
AcquisitionRuntimeService
WebSocketSession.start()
RuntimeError
```
Сервис не преобразует ошибку в общий `RuntimeDispatchError`.
Такое решение сохраняет:
- исходный тип исключения;
- исходное диагностическое сообщение;
- прозрачность инфраструктурного поведения;
- возможность точной обработки на более высоком уровне.
---
# Stateless-архитектура сервиса
`AcquisitionRuntimeService` не хранит собственное операционное состояние.
Внутри объекта сохраняются только внедрённые зависимости.
Сервис не хранит:
- состояние соединения;
- выполненные команды;
- активные подписки;
- историю отправленных сообщений;
- reconnect attempts;
- Recovery state;
- Consistency state.
Все перечисленные данные принадлежат специализированным компонентам.
---
# Изменённые файлы
## Новый Service Protocol
```text
src/market_data/acquisition/runtime/
acquisition_runtime_service_protocol.py
```
Добавлен:
```text
AcquisitionRuntimeServiceProtocol
```
Protocol содержит единственный асинхронный метод `dispatch()`.
---
## Новый Runtime Service
```text
src/market_data/acquisition/runtime/
acquisition_runtime_service.py
```
Добавлен:
```text
AcquisitionRuntimeService
```
Класс реализует:
```text
AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeCommandDispatcherProtocol
```
и маршрутизирует все шесть Runtime Commands.
---
## Расширение WebSocket Protocol Layer
```text
src/market_data/acquisition/runtime/websocket_protocol.py
```
Добавлены:
```text
AcquisitionSubscriptionMessage
```
и методы:
```text
WebSocketSubscriptionManagerProtocol.subscribe()
WebSocketSubscriptionManagerProtocol.unsubscribe()
```
Существующие Protocol и alias сохранены.
---
## Unit-тест Runtime Service
```text
tests/unit/market_data/acquisition/runtime/
test_acquisition_runtime_service.py
```
Добавлены тестовые реализации:
- `FakeSession`;
- `FakeTransport`;
- `FakeSubscriptionManager`;
- `FakeEventPublisher`.
Проверена маршрутизация всех поддерживаемых команд и распространение исключений.
---
## Обновление теста WebSocket Protocol
```text
tests/unit/market_data/acquisition/runtime/
test_websocket_protocol.py
```
`FakeWebSocketSubscriptionManager` расширен методами:
```text
subscribe()
unsubscribe()
```
Также добавлена негативная проверка неполной реализации обновлённого Protocol.
---
# Файлы, которые не изменялись
Build не изменяет:
```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
src/market_data/acquisition/runtime/supervisor.py
src/market_data/acquisition/runtime/reconnect.py
src/market_data/acquisition/runtime/scheduler.py
src/market_data/acquisition/runtime/heartbeat.py
```
Также не изменяются:
- корневой Acquisition Service;
- Acquisition Registry;
- Acquisition Protocol;
- Trades Feed;
- Quotes Feed;
- Candles Feed;
- Trade Stream Consistency;
- Trade Stream State Store;
- Trade Recovery;
- WebSocket adapters;
- каноническая модель `Trade`.
---
# Unit-тестирование AcquisitionRuntimeService
Новый сервис покрыт отдельным набором unit-тестов.
Проверены следующие сценарии:
- сервис соответствует `AcquisitionRuntimeServiceProtocol`;
- сервис соответствует `AcquisitionRuntimeCommandDispatcherProtocol`;
- `ConnectCommand` вызывает только `session.start()`;
- `DisconnectCommand` вызывает только `session.stop()`;
- `SubscribeCommand` передаёт исходные `subscription_key` и message;
- `UnsubscribeCommand` передаёт исходные `subscription_key` и message;
- `SendTextCommand` передаёт строковый payload в `transport.send()`;
- `SendBinaryCommand` передаёт бинарный payload в `transport.send()`;
- Event Publisher не используется преждевременно;
- неизвестная команда приводит к `TypeError`;
- исключение Session распространяется без изменения.
Результат:
```text
11 passed
```
---
# Unit-тестирование WebSocket Protocol
После расширения Subscription Manager выполнен локальный прогон обновлённого Protocol Layer.
Проверены:
- полная реализация Transport;
- полная реализация Session;
- полная реализация Subscription Manager;
- Command Dispatcher;
- Event Publisher;
- отрицательные сценарии неполных реализаций.
Результат:
```text
9 passed
```
---
# Регрессионное тестирование Runtime Layer
После добавления Runtime Service выполнен полный прогон:
```text
tests/unit/market_data/acquisition/runtime
```
Проверены:
- Acquisition Runtime Service;
- Runtime Commands;
- Runtime Events;
- Transport Messages;
- WebSocket Protocol Layer.
Результат:
```text
46 passed
```
Это подтверждает, что новый сервисный слой не нарушил существующее поведение Runtime.
---
# Расширенное регрессионное тестирование
Для подтверждения архитектурной изоляции выполнен совместный прогон:
```text
Runtime
Consistency
Recovery
Feeds
```
Команда охватывала каталоги:
```text
tests/unit/market_data/acquisition/runtime
tests/unit/market_data/acquisition/consistency
tests/unit/market_data/acquisition/recovery
tests/unit/market_data/acquisition/feeds
```
Результат:
```text
180 passed
```
Тем самым подтверждено:
- Runtime Service работает корректно;
- обновление Subscription Manager не нарушило Protocol Layer;
- Trade Stream Consistency не затронута;
- Trade Stream State Store не затронут;
- Trade Recovery не затронута;
- существующие Feed продолжают работать без изменений;
- преждевременная интеграция Build 060.23 не произошла.
---
# Обратная совместимость
Build сохраняет функциональное поведение существующих подсистем.
Не изменились:
- Runtime Command models;
- Runtime Event models;
- Transport Message models;
- WebSocket Transport API;
- WebSocket Session API;
- Canonical Trade Model;
- Trades Feed API;
- Consistency API;
- Recovery API;
- Feed Registry;
- корневые Acquisition Services.
Единственное расширение существующего контракта относится к `WebSocketSubscriptionManagerProtocol` и добавляет операции, уже необходимые существующим `SubscribeCommand` и `UnsubscribeCommand`.
---
# Производительность
Runtime Service выполняет конечную цепочку `isinstance`-проверок по шести типам команд и один делегированный асинхронный вызов.
Сервис не вводит:
- очереди;
- дополнительные копии payload;
- сериализацию;
- фоновые задачи;
- блокировки;
- retries;
- хранение истории.
Следовательно накладные расходы ограничены постоянным временем:
```text
O(1)
```
для каждой команды.
Основная задержка полностью определяется вызываемой инфраструктурной зависимостью.
---
# Подтверждённые архитектурные инварианты
## Runtime Service не знает предметную область
Сервис не импортирует:
```text
Trade
TradesFeed
TradeStreamConsistencyProtocol
TradeRecoveryProtocol
TradeStreamStateStore
```
---
## Runtime Service stateless
Сервис хранит только ссылки на внедрённые зависимости.
---
## Одна команда имеет один маршрут
Ни одна команда не выполняет несколько инфраструктурных операций.
---
## Session владеет lifecycle соединения
`ConnectCommand` и `DisconnectCommand` делегируются Session, а не Transport напрямую.
---
## Subscription Manager владеет подписками
Runtime Service не хранит список подписок и не управляет их состоянием самостоятельно.
---
## Transport владеет отправкой payload
Text и Binary payload передаются через существующий единый метод `send()`.
---
## Event publication не реализована преждевременно
Publisher внедрён, но не используется до отдельного этапа интеграции событий.
---
## Consistency и Recovery не зависят от Runtime Service
Новые импорты и зависимости в данных подсистемах отсутствуют.
---
## Уникальность новых имён файлов соблюдена
Build не добавляет новых `service.py`, `protocol.py` или `test_service.py`.
---
# Что не входит в Scope Build
Настоящий Build сознательно не реализует:
- интеграцию Runtime Service в корневой Acquisition Service;
- интеграцию Runtime Service с Trades Feed;
- Composition Root;
- Runtime Supervisor;
- Reconnect orchestration;
- Heartbeat;
- Scheduler;
- очередь команд;
- background worker;
- публикацию Runtime Events;
- обработку входящих WebSocket-сообщений;
- маршрутизацию сообщений к Trade Adapter;
- запуск Trade Stream Consistency;
- автоматический Trade Recovery;
- восстановление подписок после reconnect;
- реальный WebSocket transport implementation;
- сохранение Runtime-состояния между перезапусками.
Отсутствие перечисленных компонентов является осознанной границей Build и не рассматривается как незавершённость.
---
# Архитектурное значение Build
Build 060.22 является первым этапом серии Runtime, добавляющим исполняемое поведение.
До него Runtime Layer содержал:
- модели сообщений;
- команды;
- события;
- Protocol.
После завершения Build появляется единая точка выполнения команд:
```text
AcquisitionRuntimeService
```
Это превращает Runtime из исключительно декларативного слоя в минимальную работающую сервисную подсистему.
При этом сервис остаётся полностью изолированным от предметной области и готов к последующей интеграции без изменения уже реализованных Consistency, Recovery и Feed.
---
# Связь с последующими Build
## Build 060.23 — Acquisition Integration
Новый Runtime Service будет подключён к верхнему уровню Acquisition и станет доступен через утверждённую композицию зависимостей.
---
## Build 060.24 — Reconnect & Runtime Recovery
На основе Runtime Service будут построены:
- reconnect orchestration;
- resubscribe;
- восстановление Runtime-потока;
- вызов Trade Recovery после нарушения непрерывности.
---
## Build 060.25 — Integration & Regression
Будет выполнена полная проверка взаимодействия Runtime, Acquisition, Trades Feed, Consistency и Recovery.
---
# Заключение
Build 060.22 завершает создание минимального исполняемого сервисного слоя Acquisition Runtime.
В рамках этапа были реализованы:
```text
AcquisitionRuntimeServiceProtocol
AcquisitionRuntimeService
```
а также расширен:
```text
WebSocketSubscriptionManagerProtocol
```
для поддержки существующих команд подписки и отмены подписки.
Runtime Service детерминированно маршрутизирует все шесть команд к соответствующим инфраструктурным зависимостям и не содержит знаний о рыночных данных.
Архитектура сохраняет строгие границы:
- Session управляет lifecycle;
- Transport отправляет payload;
- Subscription Manager управляет подписками;
- Runtime Service только координирует;
- Consistency и Recovery остаются независимыми.
Все изменения подтверждены локальными и расширенными regression-тестами.
---
# Итог Build
После завершения Build 060.22 система обладает следующими возможностями.
✓ Создан отдельный публичный `AcquisitionRuntimeServiceProtocol`.
✓ Реализован `AcquisitionRuntimeService`.
✓ Поддерживаются все шесть существующих Acquisition Runtime Commands.
✓ Каждая команда имеет один детерминированный маршрут.
`WebSocketSubscriptionManagerProtocol` поддерживает subscribe и unsubscribe.
✓ Text и Binary payload отправляются через существующий Transport API.
✓ Исключения инфраструктурных зависимостей распространяются без изменения.
✓ Event Publisher не используется преждевременно.
Все новые файлы имеют уникальные имена.
✓ Runtime regression завершён результатом `46 passed`.
✓ Расширенный regression завершён результатом `180 passed`.
Build **060.22 — Acquisition Runtime Service** считается полностью завершённым и готовым к фиксации в Git.