Files
dzentra_bot/docs/migrations/build_060_22_architecture.md

1426 lines
40 KiB
Markdown
Raw Permalink 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
**Статус:** Accepted
**Тип документа:** Architecture Specification
**Build:** 060.22
**Ветка:** Trades Feed (Time & Sales)
**Документ:** `build_060_22_architecture.md`
**Связанные документы:**
- [Build 060.21 Architecture](build_060_21_architecture.md) —
архитектурная спецификация Runtime Integration Contracts;
- [Build 060.21 Engineering Migration Report](build_060_21.md) —
предыдущий Build;
- [Build 060.20.1 Architecture](build_060_20_1_architecture.md) —
спецификация владения состоянием Trade Stream.
---
# Назначение документа
Настоящий документ является официальной архитектурной спецификацией **Build 060.22 — Acquisition Runtime Service**.
Документ определяет создание первого исполняемого сервисного компонента Runtime Layer подсистемы **Market Data Acquisition**.
Build 060.21 сформировал типизированную контрактную границу Runtime:
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeEvent
AcquisitionRuntimeCommandDispatcherProtocol
AcquisitionRuntimeEventPublisherProtocol
```
Однако созданные контракты пока не имеют производственной реализации.
В системе отсутствует компонент, который:
- принимает типизированные Runtime-команды;
- маршрутизирует их к соответствующим инфраструктурным зависимостям;
- публикует результирующие Runtime-события;
- формирует единую точку исполнения команд Acquisition Runtime.
Build 060.22 закрывает именно эту архитектурную задачу.
Документ служит единственным источником истины при реализации данного Build.
Изменение зафиксированных решений в процессе написания кода не допускается без отдельного архитектурного пересмотра.
---
# Статус Build
Build 060.22 является следующим этапом серии Build 060, посвящённой формированию полноценной подсистемы **Trades Feed (Time & Sales)**.
К моменту начала данного Build в проекте уже существуют:
- транспортные модели WebSocket;
- Runtime Commands;
- Runtime Events;
- WebSocket transport protocol;
- WebSocket session protocol;
- WebSocket subscription manager protocol;
- контракт диспетчеризации Runtime-команд;
- контракт публикации Runtime-событий;
- Canonical Trade Model;
- Trades Feed;
- Trade Stream Consistency;
- Trade Recovery;
- специализированное хранилище `TradeStreamStateStore`.
При этом Runtime Layer пока состоит только из контрактов и моделей сообщений.
Фактический сервис исполнения Runtime-команд отсутствует.
Следовательно, Runtime ещё не является работающей подсистемой.
Build 060.22 вводит минимальную исполняемую реализацию сервисного уровня, не подключая её пока к Trades Feed и корневому Acquisition Service.
---
# Контекст
После завершения Build 060.21 архитектура Runtime Protocol Layer выглядит следующим образом.
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeCommandDispatcherProtocol
```
и:
```text
AcquisitionRuntimeEvent
AcquisitionRuntimeEventPublisherProtocol
```
Контракты определяют:
- какие команды допустимы внутри Acquisition Runtime;
- какие события может публиковать Runtime;
- каким образом вызывающий компонент передаёт команду;
- каким образом инфраструктурный результат передаётся потребителям.
Однако контракт сам по себе не выполняет команду.
Например:
```text
ConnectCommand
```
уже существует как типизированная команда, но в системе пока отсутствует компонент, который обязан:
```text
ConnectCommand
WebSocketSessionProtocol.start()
```
Аналогично:
```text
DisconnectCommand
WebSocketSessionProtocol.stop()
```
Для команд управления подписками и отправки сообщений также пока отсутствует единая точка исполнения.
Таким образом между публичным Runtime Protocol Layer и низкоуровневыми WebSocket-контрактами остаётся незаполненная сервисная граница.
---
# Предпосылки
Настоящий Build опирается на архитектурные решения, принятые в предыдущих этапах.
## Build 059.10
Сформированы базовые контракты WebSocket Runtime:
```text
WebSocketTransportProtocol
WebSocketSessionProtocol
WebSocketSubscriptionManagerProtocol
```
Данные контракты описывают отдельные инфраструктурные возможности, но не объединяют их в единый сервис исполнения команд.
---
## Build 059.11059.12
Сформированы транспортные команды и события Runtime.
Появились типизированные модели:
```text
ConnectCommand
DisconnectCommand
SubscribeCommand
UnsubscribeCommand
SendTextCommand
SendBinaryCommand
```
а также соответствующие инфраструктурные события.
---
## Build 060.18
Создана подсистема Trade Stream Consistency.
Она полностью независима от Runtime Transport и принимает только канонические объекты `Trade`.
---
## Build 060.19
Создана подсистема Trade Recovery.
Recovery остаётся stateless и зависит только от:
```text
TradeStreamConsistencyProtocol
```
---
## Build 060.20.1
Владение состоянием Trade Stream перенесено в:
```text
TradeStreamStateStore
```
Runtime Transport окончательно перестал рассматриваться как владелец состояния предметной области.
---
## Build 060.21
Сформирован интеграционный контракт Runtime Protocol Layer.
Добавлены:
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeEvent
AcquisitionRuntimeCommandDispatcherProtocol
AcquisitionRuntimeEventPublisherProtocol
```
Имена были намеренно ограничены контекстом `Acquisition`, чтобы исключить конфликт с существующей общесистемной подсистемой:
```text
src/runtime_events/
```
Build 060.21 определил границы взаимодействия, но сознательно не создавал производственную реализацию.
---
# Проблема
В текущем состоянии каждая Runtime-команда является только неизменяемым объектом данных.
Например:
```python
ConnectCommand()
```
не содержит логики подключения.
Она только выражает намерение вызывающего компонента.
То же относится к:
```python
DisconnectCommand()
SubscribeCommand(...)
UnsubscribeCommand(...)
SendTextCommand(...)
SendBinaryCommand(...)
```
Для выполнения команд необходим отдельный сервис, который:
1. принимает объект команды;
2. определяет его конкретный тип;
3. выбирает соответствующую инфраструктурную зависимость;
4. выполняет строго определённую операцию;
5. не содержит знаний о Trade, Consistency или Recovery.
Без такого компонента Runtime Protocol Layer остаётся декларативным и не может использоваться следующими уровнями системы.
---
# Основная идея Build
Главная идея Build 060.22 заключается в создании тонкого сервиса маршрутизации Runtime-команд.
Новый компонент:
```text
AcquisitionRuntimeService
```
становится производственной реализацией:
```text
AcquisitionRuntimeCommandDispatcherProtocol
```
Сервис принимает типизированную команду и делегирует выполнение уже существующим инфраструктурным контрактам.
Концептуально:
```text
AcquisitionRuntimeCommand
AcquisitionRuntimeService
┌─────┴─────┐
▼ ▼
WebSocket Subscription
Session Manager
```
Сервис не реализует WebSocket самостоятельно.
Он не содержит сетевого клиента.
Он не создаёт транспорт.
Он только координирует существующие зависимости.
---
# Цель Build
Build обязан создать единственную производственную точку исполнения типизированных Acquisition Runtime Commands.
После завершения этапа система должна обеспечивать:
- приём всех команд из `AcquisitionRuntimeCommand`;
- детерминированную маршрутизацию каждой команды;
- делегирование lifecycle-команд WebSocket Session;
- делегирование управления подписками Subscription Manager;
- делегирование отправки транспортных сообщений соответствующей зависимости;
- соответствие `AcquisitionRuntimeCommandDispatcherProtocol`;
- независимое unit-тестирование без реального сетевого соединения.
Build не должен изменять поведение существующих Runtime Commands и Runtime Events.
---
# Что НЕ входит в Scope Build
Настоящий Build сознательно не реализует:
- интеграцию Runtime Service в корневой Acquisition Service;
- интеграцию Runtime Service с Trades Feed;
- Composition Root;
- реальный WebSocket client;
- Runtime Supervisor;
- Reconnect Controller;
- Heartbeat;
- Scheduler;
- автоматическое восстановление подписок;
- автоматический REST Recovery после reconnect;
- обработку входящих Trade-сообщений;
- публикацию Canonical Trade Stream;
- регистрацию Runtime Service в Registry;
- общесистемную шину событий;
- конкурентную очередь команд;
- фоновый worker;
- сохранение Runtime-состояния между перезапусками.
Эти задачи относятся к Build 060.23060.25 либо к отдельным последующим этапам.
Build 060.22 отвечает только за синхронную с точки зрения порядка и асинхронную с точки зрения Python API диспетчеризацию одной команды за один вызов.
---
# Архитектурные принципы
При реализации Build 060.22 используются следующие обязательные принципы.
## 1. Unique File Naming
Во всём репозитории Dzentra запрещено создание нескольких файлов с одинаковым именем независимо от расположения в каталогах.
Единственное разрешённое исключение:
```text
__init__.py
```
Поэтому в Build не создаются файлы:
```text
service.py
protocol.py
exceptions.py
test_service.py
```
Утверждённые уникальные имена:
```text
acquisition_runtime_service.py
acquisition_runtime_service_protocol.py
test_acquisition_runtime_service.py
```
Имя каждого файла должно однозначно определять его назначение без учёта родительского каталога.
---
## 2. Protocol Before Implementation
Публичный контракт Runtime Service фиксируется отдельно от его реализации.
Внешние потребители в последующих Build должны зависеть от:
```text
AcquisitionRuntimeServiceProtocol
```
а не от конкретного класса:
```text
AcquisitionRuntimeService
```
---
## 3. Thin Application Service
Runtime Service является тонким координатором.
Он:
- принимает команду;
- определяет её тип;
- делегирует операцию;
- завершает вызов.
Сервис не переносит в себя логику нижележащих компонентов.
---
## 4. No Domain Knowledge
Runtime Service не должен импортировать:
```text
Trade
TradeStreamConsistencyProtocol
TradeRecoveryProtocol
TradesFeed
TradeStreamStateStore
```
Он работает исключительно с транспортными командами и инфраструктурными контрактами.
---
## 5. Dependency Injection
Все зависимости передаются в конструктор.
Сервис не создаёт самостоятельно:
- WebSocket Session;
- Transport;
- Subscription Manager;
- Publisher;
- Adapter.
Создание графа зависимостей относится к Composition Root и не входит в Scope Build.
---
## 6. One Command — One Deterministic Route
Каждый тип команды должен иметь ровно один допустимый маршрут исполнения.
Команда не может быть обработана несколькими зависимостями одновременно, если это отдельно не определено спецификацией.
---
## 7. No Premature Orchestration
Runtime Service не является Supervisor.
Он не запускает фоновые задачи.
Не выполняет retries.
Не планирует reconnect.
Не контролирует heartbeat.
Перечисленные обязанности появятся в специализированных компонентах позднее.
---
## 8. Existing Contracts Remain Stable
Build не изменяет публичные модели:
```text
runtime_commands.py
runtime_events.py
transport_messages.py
websocket_protocol.py
```
Если реализация обнаружит недостаточность существующего контракта, изменение должно быть отдельно согласовано до внесения в код.
---
# Acquisition Runtime Service
## Назначение
`AcquisitionRuntimeService` является первым исполняемым компонентом Runtime Layer.
Его единственная задача — выполнение типизированных инфраструктурных Runtime-команд.
Сервис не содержит собственной бизнес-логики.
Он выполняет только маршрутизацию команд к уже существующим инфраструктурным зависимостям.
Концептуально:
```text
Runtime Command
AcquisitionRuntimeService
├──────────────► WebSocketSession
├──────────────► WebSocketTransport
└──────────────► SubscriptionManager
```
Таким образом сервис становится единственной производственной точкой исполнения Runtime-команд.
---
# Почему появляется отдельный Service
Во время проектирования были рассмотрены несколько вариантов.
---
## Вариант №1
Каждый вызывающий компонент самостоятельно выполняет команды.
Например:
```text
TradesFeed
if ConnectCommand
session.start()
```
Отклонён.
Причины:
- дублирование логики;
- нарушение принципа единственной ответственности;
- отсутствие единой точки маршрутизации.
---
## Вариант №2
Перенести выполнение команд внутрь WebSocketSession.
Например:
```text
session.dispatch(command)
```
Отклонён.
Причины:
Session начинает знать о:
- Subscription;
- Transport;
- Runtime Commands.
Тем самым нарушается разделение обязанностей.
---
## Вариант №3
Создать специализированный Runtime Service.
Принят.
Именно Runtime Service становится единственным компонентом, который понимает соответствие между:
```text
Runtime Command
Infrastructure Action
```
---
# Граница ответственности
Runtime Service отвечает исключительно за выполнение Runtime-команд.
Он НЕ отвечает за:
- сетевой протокол;
- обработку сообщений;
- Consistency;
- Recovery;
- Trades Feed;
- управление жизненным циклом приложения;
- Supervisor;
- Scheduler;
- Heartbeat;
- Reconnect.
Все перечисленные обязанности принадлежат другим Build.
---
# AcquisitionRuntimeServiceProtocol
## Назначение
Build 060.22 вводит отдельный публичный контракт сервисного уровня.
```text
AcquisitionRuntimeServiceProtocol
```
Он описывает единственную публичную возможность Runtime Service.
```python
dispatch(...)
```
Все последующие Build должны зависеть именно от данного Protocol.
Конкретная реализация может изменяться без влияния на потребителей.
---
# Почему вводится отдельный Protocol
На первый взгляд может показаться, что уже существует:
```text
AcquisitionRuntimeCommandDispatcherProtocol
```
Однако данный Protocol описывает инфраструктурную возможность диспетчеризации команд.
Он не определяет существование самостоятельного сервисного компонента.
Build 060.22 впервые вводит именно сервисный уровень Runtime.
Поэтому появляется отдельный сервисный контракт.
Это позволяет в будущем заменить реализацию Runtime Service без изменения Composition Root и вышестоящих компонентов.
---
# Публичный API
Сервис предоставляет только один публичный метод.
```python
async def dispatch(
command: AcquisitionRuntimeCommand,
) -> None
```
Других публичных методов Build 060.22 не вводит.
В частности отсутствуют:
```python
connect()
disconnect()
subscribe()
unsubscribe()
send()
```
Все операции выражаются исключительно через типизированные команды.
---
# Почему отсутствуют отдельные методы
Во время проектирования рассматривалась следующая модель.
```python
runtime.connect()
runtime.disconnect()
runtime.subscribe(...)
```
Она была отклонена.
Причина:
типизированные Runtime Commands уже являются официальным языком взаимодействия Runtime Layer.
Создание второго API привело бы к существованию двух независимых способов выполнения одной и той же операции.
Архитектура должна содержать единственный публичный механизм.
---
# Зависимости Runtime Service
Сервис получает все зависимости через Dependency Injection.
Минимальный набор зависимостей выглядит следующим образом.
```text
AcquisitionRuntimeService
├────────► WebSocketSessionProtocol
├────────► WebSocketTransportProtocol
├────────► WebSocketSubscriptionManagerProtocol
└────────► AcquisitionRuntimeEventPublisherProtocol
```
Никакие другие зависимости Build 060.22 не предусматривает.
---
# Почему внедряется Event Publisher
Хотя Build 060.22 ещё не реализует полноценную публикацию Runtime-событий, сервис уже принимает зависимость:
```text
AcquisitionRuntimeEventPublisherProtocol
```
Это принципиальное архитектурное решение.
Причины:
- исключается изменение конструктора в следующих Build;
- Runtime Service сразу проектируется как источник инфраструктурных событий;
- интеграция публикации становится локальным изменением без перестройки графа зависимостей.
На данном этапе Publisher допускается не использовать.
---
# Почему внедряется WebSocketTransportProtocol
Большинство текущих операций выполняются через:
```text
WebSocketSessionProtocol
```
Однако команды:
```text
SendTextCommand
SendBinaryCommand
```
относятся к транспортному уровню.
Поэтому Runtime Service сразу получает доступ к Transport.
Это предотвращает последующее изменение конструктора.
---
# Маршрутизация команд
Главной обязанностью Runtime Service является детерминированная маршрутизация.
Каждый тип команды имеет ровно один маршрут исполнения.
---
## ConnectCommand
```text
ConnectCommand
WebSocketSession.start()
```
После успешного завершения управление возвращается вызывающему компоненту.
Сам Runtime Service не создаёт сетевое соединение.
---
## DisconnectCommand
```text
DisconnectCommand
WebSocketSession.stop()
```
Сервис не закрывает транспорт напрямую.
Эта ответственность принадлежит Session.
---
## SubscribeCommand
```text
SubscribeCommand
WebSocketSubscriptionManager.subscribe()
```
Runtime Service не хранит список активных подписок.
Он лишь делегирует выполнение соответствующему компоненту.
---
## UnsubscribeCommand
```text
UnsubscribeCommand
WebSocketSubscriptionManager.unsubscribe()
```
После завершения операции Runtime Service не изменяет собственного состояния.
---
## SendTextCommand
```text
SendTextCommand
WebSocketTransport.send_text()
```
Передаваемое сообщение считается полностью сформированным.
Runtime Service не сериализует полезную нагрузку повторно.
---
## SendBinaryCommand
```text
SendBinaryCommand
WebSocketTransport.send_binary()
```
Команда делегируется транспортному уровню без каких-либо преобразований.
---
# Официальная матрица маршрутизации
| Runtime Command | Исполнитель |
|-----------------|-------------|
| ConnectCommand | WebSocketSessionProtocol |
| DisconnectCommand | WebSocketSessionProtocol |
| SubscribeCommand | WebSocketSubscriptionManagerProtocol |
| UnsubscribeCommand | WebSocketSubscriptionManagerProtocol |
| SendTextCommand | WebSocketTransportProtocol |
| SendBinaryCommand | WebSocketTransportProtocol |
Данная таблица считается официальной спецификацией Build 060.22.
Любое изменение маршрутов требует отдельного архитектурного решения (ADR).
---
# Детерминированность маршрутизации
Runtime Service рассматривается как детерминированный маршрутизатор.
Для каждого входящего объекта существует единственный допустимый маршрут.
Формально:
```text
Runtime Command
Dispatch Table
Infrastructure Operation
```
Никакие дополнительные проверки, эвристики или выбор стратегии Build 060.22 не предусматривает.
---
# Обработка неизвестной команды
Build 060.22 не допускает существование неизвестных Runtime-команд.
Если в сервис поступает объект, не входящий в объединение:
```text
AcquisitionRuntimeCommand
```
это считается внутренней архитектурной ошибкой.
В таком случае сервис обязан немедленно завершить выполнение исключением.
Это гарантирует, что добавление новой команды никогда не останется незамеченным и потребует явного обновления таблицы маршрутизации.
---
# Диаграмма взаимодействия
После реализации Build 060.22 выполнение Runtime-команд приобретает следующий вид.
```text
Caller
│ dispatch(command)
AcquisitionRuntimeService
├──────────────► WebSocketSessionProtocol
├──────────────► WebSocketTransportProtocol
├──────────────► WebSocketSubscriptionManagerProtocol
└──────────────► AcquisitionRuntimeEventPublisherProtocol
```
Важно отметить, что Runtime Service остаётся полностью синхронным с точки зрения архитектуры.
Он:
- не создаёт фоновых задач;
- не ставит команды в очередь;
- не выполняет повторные попытки;
- не содержит собственного event loop.
Каждый вызов `dispatch()` завершается только после завершения соответствующей инфраструктурной операции.
---
# Жизненный цикл Runtime Service
Runtime Service является долгоживущим инфраструктурным сервисом.
Типичный жизненный цикл выглядит следующим образом.
```text
Composition Root
создание зависимостей
создание Runtime Service
передача Runtime Service вызывающим компонентам
многократные вызовы dispatch()
завершение приложения
```
Сам Runtime Service не требует отдельной инициализации.
Также отсутствуют методы:
```python
start()
stop()
shutdown()
dispose()
```
Экземпляр полностью готов к работе сразу после создания.
---
# Отношение к состоянию
Runtime Service является stateless-компонентом.
Он не хранит:
- состояние подключения;
- список подписок;
- очередь сообщений;
- информацию о последней выполненной команде;
- счётчики reconnect;
- состояние Recovery;
- состояние Consistency.
Все перечисленные данные принадлежат специализированным компонентам.
Следовательно экземпляр Runtime Service можно считать чистым координатором.
---
# Обработка исключений
Build 060.22 сознательно не вводит собственую иерархию исключений.
Причина проста.
Runtime Service не принимает самостоятельных решений.
Он лишь вызывает инфраструктурные зависимости.
Следовательно любые исключения должны передаваться вызывающему компоненту без изменения.
Например:
```text
Caller
Runtime Service
WebSocket Session
ConnectionError
```
Исключение должно пройти обратно без обёртки.
Это сохраняет прозрачность поведения системы.
---
# Почему исключения не преобразуются
Во время проектирования рассматривался вариант создания:
```text
RuntimeDispatchError
```
Он был отклонён.
Причины:
- потеря информации о первичном исключении;
- необходимость лишнего уровня обработки;
- усложнение диагностики;
- нарушение принципа прозрачной инфраструктуры.
Runtime Service не должен скрывать происхождение ошибки.
---
# Влияние на существующую архитектуру
Build 060.22 практически не изменяет существующую систему.
Новый сервис располагается между контрактами Runtime и инфраструктурными зависимостями.
До Build:
```text
Runtime Commands
(нет реализации)
```
После Build:
```text
Runtime Commands
AcquisitionRuntimeService
Infrastructure
```
Никакие другие подсистемы не изменяются.
---
# Влияние на Trade Stream Consistency
Подсистема:
```text
Trade Stream Consistency
```
не получает никаких новых зависимостей.
Она продолжает работать исключительно с:
```text
Trade
```
и
```text
TradeStreamConsistencyProtocol
```
Build 060.22 не создаёт прямой связи между Runtime и Consistency.
---
# Влияние на Trade Recovery
Trade Recovery также не изменяется.
Контроллер восстановления по-прежнему зависит только от:
```text
TradeStreamConsistencyProtocol
```
Runtime Service не импортирует Recovery.
Recovery не импортирует Runtime Service.
Архитектурная независимость сохраняется полностью.
---
# Влияние на Trades Feed
На данном этапе Trades Feed ещё не использует Runtime Service.
Интеграция будет выполнена отдельным Build:
```text
060.23
```
Это позволяет протестировать Runtime Service изолированно.
---
# План изменения структуры проекта
Build добавляет только два новых файла.
```text
runtime/
├── acquisition_runtime_service.py
└── acquisition_runtime_service_protocol.py
```
Также добавляется один файл тестов.
```text
tests/
runtime/
└── test_acquisition_runtime_service.py
```
Другие файлы Runtime Build 060.22 не изменяет.
---
# Почему используется отдельный Protocol
Наличие собственного:
```text
AcquisitionRuntimeServiceProtocol
```
позволяет в будущем заменить реализацию.
Например:
```text
Simple Runtime Service
Queued Runtime Service
Distributed Runtime Service
```
Все перечисленные варианты смогут реализовывать один и тот же публичный контракт.
Поэтому вышестоящие компоненты не будут зависеть от конкретного класса.
---
# Стратегия тестирования
Build 060.22 покрывается исключительно Unit Test.
Никакие интеграционные тесты пока не требуются.
Каждая Runtime-команда проверяется отдельно.
Минимальный набор сценариев включает:
- соответствие `AcquisitionRuntimeServiceProtocol`;
- корректную маршрутизацию `ConnectCommand`;
- корректную маршрутизацию `DisconnectCommand`;
- корректную маршрутизацию `SubscribeCommand`;
- корректную маршрутизацию `UnsubscribeCommand`;
- корректную маршрутизацию `SendTextCommand`;
- корректную маршрутизацию `SendBinaryCommand`;
- отсутствие побочных эффектов;
- прозрачное распространение исключений;
- отсутствие собственного состояния между вызовами.
Все зависимости заменяются простыми Fake-реализациями.
Использование реального WebSocket Build 060.22 запрещает.
---
# ADR (Architecture Decision Record)
## ADR-060.22-01
**Решение**
Создать специализированный сервис:
```text
AcquisitionRuntimeService
```
реализующий выполнение всех типизированных Runtime-команд.
**Статус**
Accepted.
---
## ADR-060.22-02
**Решение**
Ввести отдельный публичный контракт:
```text
AcquisitionRuntimeServiceProtocol
```
независимо от уже существующего:
```text
AcquisitionRuntimeCommandDispatcherProtocol
```
**Статус**
Accepted.
---
## ADR-060.22-03
**Решение**
Runtime Service остаётся stateless.
**Статус**
Accepted.
---
## ADR-060.22-04
**Решение**
Все зависимости передаются исключительно через Dependency Injection.
**Статус**
Accepted.
---
## ADR-060.22-05
**Решение**
Не создавать новых файлов с именами:
```text
service.py
protocol.py
test_service.py
```
Использовать только уникальные имена файлов.
**Статус**
Accepted.
---
# Definition of Done
Build считается завершённым только при выполнении всех условий.
Обязательно должны существовать:
- `acquisition_runtime_service_protocol.py`;
- `acquisition_runtime_service.py`;
- `test_acquisition_runtime_service.py`.
Runtime Service обязан:
- реализовывать `AcquisitionRuntimeServiceProtocol`;
- принимать зависимости через конструктор;
- поддерживать все типы `AcquisitionRuntimeCommand`;
- выполнять детерминированную маршрутизацию;
- не хранить собственного состояния;
- не создавать зависимости самостоятельно;
- прозрачно распространять исключения.
Все новые Unit Test должны успешно проходить.
Build не должен изменять поведение существующих Runtime Commands, Runtime Events, Trade Recovery и Trade Stream Consistency.
---
# Следующий Build
После завершения Build 060.22 архитектура Runtime впервые получит полноценный исполняемый сервисный слой.
Следующим этапом станет
[Build 060.23 — Acquisition Integration](build_060_23_architecture.md).
На этом этапе `AcquisitionRuntimeService` будет интегрирован в подсистему получения рыночных данных и станет использоваться как единая точка исполнения инфраструктурных Runtime-команд.
После Build 060.23 Runtime перестанет существовать только как набор контрактов и сервисов и начнёт участвовать в реальном конвейере получения данных от биржи.