Files
dzentra_bot/docs/migrations/build_060_20.md

1406 lines
62 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.20 — Trade Runtime
**Engineering Migration Report**
---
# Контроль документа
| Свойство | Значение |
|----------|----------|
| Build | 060.20 |
| Название | Trade Runtime |
| Статус | Completed |
| Проект | Dzentra |
| Подсистема | Market Data Acquisition |
| Компонент | Trade Runtime |
| Версия | 1.0 |
---
# Связанные документы
- [Build 060.20 Architecture](build_060_20_architecture.md) —
архитектурная спецификация Build.
- [Build 060.19 Engineering Migration Report](build_060_19.md) —
предыдущий Build.
---
# Цель Build
Build 060.19 завершил построение самостоятельной подсистемы **Trade Recovery**, обеспечивающей безопасное восстановление исторических сделок посредством REST API.
К началу настоящего Build подсистема Trades Feed уже обладала всеми основными функциональными компонентами обработки сделок.
Система обеспечивала:
- получение сделок через REST API;
- построение канонической модели `Trade`;
- проверку согласованности непрерывного потока;
- восстановление пропущенных участков истории;
- формирование единственного канонического Trade Stream.
Несмотря на это, архитектура оставалась неполной.
Все реализованные компоненты существовали как независимые сервисы, однако отсутствовала единая модель хранения их долгоживущего состояния.
Особенно это касалось компонента
```text
TradeStreamConsistencyController
```
который являлся первым по-настоящему stateful-компонентом новой архитектуры Acquisition Layer.
Контроллер сопровождал состояние непрерывного потока сделок и не мог безопасно создаваться заново для каждой отдельной операции.
Следовательно возникла необходимость определить инфраструктурный механизм хранения runtime-состояния, независимый от отдельных операций Recovery, REST или будущего WebSocket Feed.
Главной задачей настоящего Build стало построение первого слоя **Trade Runtime**, предназначенного для хранения долгоживущего состояния подсистемы Trades Feed.
После завершения Build система получает:
- инфраструктурный контракт `TradeRuntimeProtocol`;
- универсальный `TradeRuntimeRegistry`;
- специализированную иерархию Runtime-исключений;
- интеграцию `TradeStreamConsistencyController` с Runtime;
- единый механизм хранения runtime-состояния;
- полноценное покрытие новой инфраструктуры unit-тестами.
При этом Build принципиально не изменяет:
- каноническую модель `Trade`;
- REST Pipeline;
- Recovery Pipeline;
- алгоритмы Stream Consistency;
- алгоритмы Recovery;
- Trades Feed;
- WebSocket Runtime;
- Runtime Orchestration;
- Composition Root.
Все перечисленные задачи относятся к следующим этапам развития подсистемы Trades Feed.
---
# Предпосылки
К началу Build архитектура Acquisition Layer уже обеспечивала полный цикл получения и обработки сделок независимо от источника данных.
Конвейер обработки выглядел следующим образом.
```text
Transport Message
Schema Validation
Parser
Value Validation
Mapper
Trade
Trade Stream Consistency
Canonical Trade Stream
```
При восстановлении истории использовался аналогичный конвейер, завершающийся передачей сделок в тот же экземпляр Trade Stream Consistency.
Таким образом к началу настоящего Build система уже обладала единым алгоритмом обработки сделок независимо от транспортного источника.
Однако оставался нерешённым вопрос хранения накопленного состояния Trade Stream между отдельными вызовами компонентов системы.
В существующей архитектуре отсутствовало понятие Runtime как самостоятельного инфраструктурного слоя.
В результате долгоживущее состояние было неотделимо от жизненного цикла конкретных объектов.
Подобная модель не могла стать фундаментом для последующей интеграции WebSocket Feed, Gap Detection и Runtime Orchestration.
Именно эту архитектурную задачу решает Build 060.20.
---
# Результаты архитектурного аудита
Перед началом реализации Build был выполнен полный аудит существующей подсистемы **Market Data Acquisition**.
Целью аудита являлась проверка соответствия фактической архитектуры решениям, зафиксированным в `build_060_20_architecture.md`, а также определение оптимальной модели хранения долгоживущего состояния подсистемы Trades Feed.
Особое внимание уделялось компоненту
```text
TradeStreamConsistencyController
```
поскольку именно он являлся первым компонентом Acquisition Layer, сохраняющим состояние между последовательными операциями обработки сделок.
Первоначально предполагалось реализовать инфраструктурный Runtime как реестр долгоживущих сервисов подсистемы Trades Feed.
Однако проведённый аудит показал, что подобная модель не соответствует фактическому устройству системы.
В результате архитектура Runtime была существенно упрощена.
---
## Анализ существующей модели состояния
К началу настоящего Build единственным компонентом, действительно обладающим внутренним состоянием, являлся
```text
TradeStreamConsistencyController
```
Внутри контроллера поддерживалась коллекция состояний отдельных торговых символов.
Концептуально архитектура выглядела следующим образом.
```text
TradeStreamConsistencyController
_states
├── BTCUSDT
├── ETHUSDT
├── ...
```
Каждый объект состояния содержал сведения, необходимые для сопровождения непрерывного потока сделок конкретного торгового инструмента.
Например:
- последнюю принятую сделку;
- информацию о порядке поступления;
- сведения, необходимые для дедупликации;
- информацию о состоянии последовательности.
Таким образом состояние уже существовало, однако полностью принадлежало внутренней реализации одного конкретного контроллера.
---
## Недостатки внутреннего хранения состояния
Подобная архитектура успешно решала задачи Stream Consistency, однако обладала существенным ограничением.
Состояние было неотделимо от конкретной реализации контроллера.
Это приводило сразу к нескольким архитектурным последствиям.
Во-первых, другие Runtime-компоненты не могли использовать единый механизм хранения собственного состояния.
Во-вторых, каждый новый stateful-компонент неизбежно создавал бы собственное внутреннее хранилище.
В-третьих, инфраструктурный слой Runtime фактически отсутствовал.
Таким образом архитектура постепенно двигалась бы к появлению нескольких независимых механизмов хранения состояния внутри различных компонентов системы.
Подобное решение противоречило принципам модульной архитектуры Dzentra.
---
## Первоначальная модель Runtime Registry
На этапе проектирования предполагалось, что Runtime будет реализован как реестр долгоживущих компонентов.
Концептуально предполагалась следующая структура.
```text
TradeRuntimeRegistry
├── TradeStreamConsistencyController
├── TradeRecoveryController
└── будущие Runtime-модули
```
Подобная модель выглядела естественной с точки зрения жизненного цикла объектов.
Однако аудит показал, что она создаёт ненужный уровень косвенности.
Registry начинал хранить сами сервисы, хотя их создание и композиция уже относятся к ответственности Composition Root.
В результате инфраструктурный компонент начинал пересекаться с механизмом внедрения зависимостей.
---
## Переосмысление роли Runtime
После завершения анализа было принято принципиально иное архитектурное решение.
Runtime должен хранить не сервисы.
Runtime должен хранить состояние.
Именно состояние является долгоживущим объектом системы.
Контроллеры лишь используют это состояние посредством публичного контракта Runtime.
Таким образом Runtime перестаёт быть каталогом компонентов и превращается в инфраструктурный контейнер runtime-данных.
---
## Новая модель Trade Runtime
По результатам аудита было принято окончательное архитектурное решение.
Trade Runtime становится универсальным хранилищем объектов состояния.
Каждый Runtime-компонент самостоятельно определяет структуру собственного состояния и использует Runtime исключительно как инфраструктурный механизм его хранения.
Концептуально Runtime приобретает следующий вид.
```text
TradeRuntimeRegistry
├── consistency:BTCUSDT
├── consistency:ETHUSDT
├── consistency:...
└── ...
```
Registry больше не знает ничего о природе сохраняемых объектов.
Он предоставляет только операции регистрации, получения и повторного использования runtime-данных.
Подобное решение полностью отделяет инфраструктуру хранения состояния от бизнес-логики отдельных компонентов.
---
## Универсальное пространство ключей Runtime
Следующим результатом архитектурного аудита стало введение универсальной модели адресации объектов Runtime.
Вместо хранения отдельных специализированных коллекций Runtime использует единое пространство строковых ключей.
Например.
```text
consistency:BTCUSDT
consistency:ETHUSDT
```
В дальнейшем аналогичным образом могут появляться новые пространства.
Например.
```text
gap:BTCUSDT
scheduler:BTCUSDT
monitoring:BTCUSDT
```
Подобная модель делает Runtime полностью независимым от конкретных Runtime-модулей.
Добавление нового типа состояния не требует изменения самого Registry.
Достаточно определить собственное пространство ключей.
---
## Интеграция Stream Consistency
После определения новой модели Runtime был выполнен повторный анализ архитектуры Stream Consistency.
Проверка показала, что контроллер не должен владеть собственным хранилищем состояний.
Вместо внутренней коллекции
```text
_states
```
контроллер получает зависимость
```text
TradeRuntimeProtocol
```
и запрашивает необходимое состояние посредством Runtime.
При отсутствии зарегистрированного состояния соответствующий объект создаётся автоматически и регистрируется внутри Runtime.
Таким образом Stream Consistency полностью сохраняет собственную бизнес-логику, но перестаёт быть владельцем механизма хранения состояния.
---
## Анализ интеграции Recovery
Отдельной задачей архитектурного аудита стала проверка необходимости интеграции Runtime непосредственно с
```text
TradeRecoveryController
```
Первоначальная архитектурная спецификация предполагала, что Recovery станет ещё одним Runtime-компонентом и будет напрямую использовать Runtime Registry.
Однако после анализа существующей реализации выяснилось, что подобное изменение не приносит архитектурной пользы.
Recovery не обладает собственным долгоживущим состоянием.
Он представляет собой полностью stateless-сервис, координирующий выполнение Recovery Pipeline.
Единственным компонентом, использующим runtime-состояние, остаётся Stream Consistency.
Следовательно Recovery уже получает доступ к Runtime опосредованно через переданный экземпляр `TradeStreamConsistencyProtocol`.
Дополнительная интеграция Runtime в Recovery была признана избыточной и сознательно исключена из Scope Build.
---
## Отказ от Runtime-компонентов
Одним из наиболее важных результатов архитектурного аудита стал пересмотр самого понятия Runtime-компонента.
Первоначальная архитектурная спецификация рассматривала Runtime как совокупность долгоживущих сервисов.
Предполагалось, что Runtime включает:
```text
Trade Runtime
├── Stream Consistency Module
└── Recovery Module
```
Однако детальный анализ показал, что подобная модель смешивает два различных понятия.
С одной стороны существуют сервисы, реализующие бизнес-логику.
С другой стороны существует инфраструктурное состояние, которое эти сервисы используют.
Сервисы сами по себе не требуют специального хранения.
Они могут безопасно создаваться посредством Dependency Injection.
Долгоживущим объектом является исключительно состояние.
Поэтому было принято окончательное архитектурное решение.
Trade Runtime не является каталогом сервисов.
Trade Runtime является инфраструктурным контейнером runtime-состояния.
Это решение существенно упростило архитектуру всей подсистемы Trades Feed.
---
## Разделение ответственности
После завершения архитектурного аудита были окончательно разделены три независимые области ответственности.
### Runtime
Runtime отвечает исключительно за хранение и предоставление объектов состояния.
Runtime не знает:
- какие алгоритмы используют состояние;
- какие контроллеры существуют;
- каким образом состояние будет изменяться.
---
### Trade Stream Consistency
`TradeStreamConsistencyController` отвечает исключительно за сопровождение непрерывного канонического потока сделок.
Именно данный компонент:
- принимает решения относительно порядка сделок;
- выполняет дедупликацию;
- обнаруживает конфликтующие повторы;
- обновляет состояние потока.
При этом механизм хранения состояния полностью делегируется Runtime.
---
### Trade Recovery
`TradeRecoveryController` остаётся полностью stateless-компонентом.
Recovery:
- получает исторические сделки;
- использует существующий REST Pipeline;
- передаёт сделки в Stream Consistency;
- формирует результат восстановления.
Recovery не хранит собственного состояния.
Recovery не использует Runtime напрямую.
Recovery не становится владельцем Runtime.
---
# Архитектурное решение
По результатам проведённого аудита было принято решение реализовать Trade Runtime как самостоятельный инфраструктурный слой хранения состояния.
Runtime предоставляет универсальный контракт доступа к данным.
Все алгоритмы обработки продолжают принадлежать специализированным сервисам.
После завершения Build архитектура принимает следующий вид.
```text
Trade Runtime
TradeRuntimeRegistry
consistency:<symbol>
TradeStreamState
TradeStreamConsistencyController
TradeRecoveryController
```
Таким образом Runtime перестаёт быть участником бизнес-конвейера обработки сделок.
Он становится исключительно инфраструктурным слоем сопровождения состояния.
Подобное решение обеспечивает слабую связанность компонентов, повторное использование общего состояния и полностью соответствует принципам модульной архитектуры Dzentra.
---
# Почему Runtime не становится Composition Root
Во время проектирования отдельно анализировался вопрос о возможности использовать Runtime как механизм создания и хранения сервисов.
На первый взгляд подобный подход выглядел достаточно привлекательным.
Runtime мог бы самостоятельно создавать необходимые контроллеры и предоставлять их другим компонентам системы.
Однако подобная архитектура нарушала бы фундаментальный принцип разделения ответственности.
Runtime отвечает за инфраструктурное хранение состояния.
Composition Root отвечает за создание графа зависимостей приложения.
Это две различные задачи.
Попытка объединить их в одном компоненте неизбежно привела бы к смешению инфраструктуры хранения состояния и механизма Dependency Injection.
Поэтому было принято решение полностью разделить эти уровни.
Trade Runtime остаётся исключительно инфраструктурным контейнером runtime-состояния.
Создание контроллеров будет реализовано позднее в отдельном Composition Root.
---
# Новая подсистема Trade Runtime
Главным результатом настоящего Build становится появление в составе **Market Data Acquisition** нового инфраструктурного уровня — **Trade Runtime**.
До начала настоящего Build каждый stateful-компонент был вынужден самостоятельно хранить собственное состояние.
После завершения Build всё долгоживущее состояние переносится в специализированный Runtime.
Архитектура взаимодействия принимает следующий вид.
```text
TradeStreamConsistencyController
TradeRuntimeProtocol
TradeRuntimeRegistry
TradeStreamState
```
Появление данного уровня является принципиальным расширением архитектуры Acquisition Layer.
Теперь любой будущий stateful-компонент сможет использовать единый механизм хранения собственного состояния без создания дополнительных внутренних коллекций.
---
# Основные архитектурные принципы Runtime
Настоящий Build закрепляет несколько новых архитектурных принципов.
---
## Runtime хранит состояние
Runtime не хранит сервисы.
Runtime хранит исключительно объекты состояния.
---
## Runtime не знает бизнес-логики
Runtime не знает:
- что такое сделка;
- что такое Recovery;
- что такое Consistency;
- каким образом используются зарегистрированные объекты.
Runtime предоставляет только инфраструктурный механизм хранения.
---
## Runtime масштабируется пространствами ключей
Каждый Runtime-модуль самостоятельно определяет собственное пространство ключей.
Например.
```text
consistency:<symbol>
```
Добавление нового Runtime-модуля не требует изменения Runtime Registry.
---
## Runtime не зависит от транспорта
REST,
WebSocket,
Replay,
или любой будущий источник данных используют один и тот же механизм хранения состояния.
Runtime полностью независим от способа получения сделок.
---
## Runtime готов к дальнейшему развитию
Появление новых Runtime-модулей больше не требует создания отдельных Registry.
Любой компонент может использовать существующий Runtime посредством собственного пространства ключей.
Именно эта модель рассматривается как целевая архитектура дальнейшего развития Runtime Layer.
---
# TradeRuntimeProtocol
Одной из целей настоящего Build являлось формирование полноценного инфраструктурного контракта новой подсистемы Runtime.
До начала Build единый контракт хранения долгоживущего состояния отсутствовал.
Каждый компонент самостоятельно определял способ хранения собственных данных.
В рамках реализации был добавлен новый Protocol.
```text
TradeRuntimeProtocol
```
Protocol определяет минимальный универсальный контракт доступа к Runtime.
Контракт предоставляет операции:
- регистрации объекта состояния;
- получения объекта состояния;
- проверки существования объекта состояния.
При этом Protocol сознательно не определяет:
- структуру хранимых объектов;
- тип состояния;
- бизнес-назначение состояния;
- жизненный цикл объектов;
- механизм их изменения.
Все перечисленные детали относятся исключительно к компонентам, использующим Runtime.
Благодаря подобному разделению любые последующие Runtime-модули смогут зависеть только от публичного контракта Runtime, а не от конкретной реализации Registry.
Это полностью соответствует принципу **Dependency Inversion**, принятому в архитектуре Dzentra.
---
# Почему Runtime использует универсальный Protocol
Во время проектирования рассматривались различные варианты публичного интерфейса Runtime.
В частности анализировались следующие подходы.
Первый вариант предполагал создание специализированных методов.
Например.
```text
get_consistency_state()
get_gap_detector()
get_scheduler()
```
Подобный подход был признан ошибочным.
Каждый новый Runtime-модуль требовал бы изменения самого Runtime Protocol.
Это нарушало бы принцип открытости для расширения.
После анализа было принято решение использовать единый универсальный контракт.
Runtime предоставляет только базовые операции хранения объектов.
Интерпретация этих объектов полностью принадлежит вызывающему компоненту.
Благодаря этому Runtime остаётся независимым от последующего развития системы.
---
# TradeRuntimeRegistry
Центральным компонентом настоящего Build становится
```text
TradeRuntimeRegistry
```
Именно он завершает построение новой инфраструктурной подсистемы Runtime.
Следует подчеркнуть, что Registry не является контейнером сервисов.
Он представляет собой исключительно инфраструктурное хранилище runtime-состояния.
Registry не знает назначения объектов.
Не анализирует их содержимое.
Не изменяет зарегистрированные данные.
Все перечисленные задачи принадлежат компонентам, использующим Runtime.
---
## Архитектура Registry
Конструкция Registry намеренно сделана максимально универсальной.
Внутри Registry отсутствуют специализированные коллекции.
Все объекты хранятся в едином пространстве Runtime.
Концептуально структура выглядит следующим образом.
```text
TradeRuntimeRegistry
├── consistency:BTCUSDT
├── consistency:ETHUSDT
├── gap:BTCUSDT
├── scheduler:BTCUSDT
└── ...
```
Подобная модель позволяет использовать Registry независимо от количества Runtime-модулей.
---
## Ответственность Registry
Во время проектирования особое внимание уделялось разделению ответственности между Runtime и компонентами обработки данных.
В результате Registry получил исключительно инфраструктурные обязанности.
Он отвечает за:
- регистрацию объектов Runtime;
- получение объектов Runtime;
- проверку существования объектов;
- обеспечение повторного использования зарегистрированных экземпляров.
При этом Registry сознательно не реализует:
- бизнес-логику;
- дедупликацию;
- сопровождение Trade Stream;
- Recovery;
- управление жизненным циклом приложения;
- Dependency Injection.
Подобное разделение делает Runtime полностью независимым от остальных подсистем Acquisition Layer.
---
# Stateless-архитектура Runtime
Одним из важнейших архитектурных решений настоящего Build становится полный отказ Runtime от собственной бизнес-логики.
После завершения Build Runtime не содержит:
- алгоритмов обработки сделок;
- информации о транспортном источнике;
- информации о Recovery;
- информации о Stream Consistency;
- информации о канонической модели `Trade`.
Registry представляет собой исключительно инфраструктурный контейнер данных.
Все решения относительно изменения состояния принимаются исключительно компонентами, использующими Runtime.
Подобное решение существенно упрощает сопровождение инфраструктурного слоя и позволяет безопасно расширять Runtime без изменения его внутренней реализации.
---
# Интеграция Stream Consistency с Runtime
После завершения реализации Build механизм сопровождения непрерывного Trade Stream получает новую модель хранения состояния.
До настоящего Build контроллер содержал собственное внутреннее хранилище.
Концептуально схема выглядела следующим образом.
```text
TradeStreamConsistencyController
_states
TradeStreamState
```
После завершения Build внутреннее хранилище полностью исключается.
Контроллер получает Runtime посредством Dependency Injection.
Архитектура принимает следующий вид.
```text
TradeStreamConsistencyController
TradeRuntimeProtocol
TradeRuntimeRegistry
TradeStreamState
```
При отсутствии зарегистрированного состояния контроллер автоматически создаёт новый экземпляр `TradeStreamState` и регистрирует его внутри Runtime.
После этого все последующие операции используют один и тот же объект состояния.
Таким образом Build полностью сохраняет алгоритмы Stream Consistency, изменяя исключительно инфраструктурный механизм хранения данных.
---
# Почему Recovery не изменяется
Во время реализации отдельно анализировалась необходимость изменения
```text
TradeRecoveryController
```
Проверка показала, что существующая архитектура Recovery уже полностью соответствует новым принципам Runtime.
Recovery остаётся stateless-сервисом.
Он не хранит собственного состояния.
Он не создаёт экземпляры Stream Consistency.
Он использует исключительно публичный контракт
```text
TradeStreamConsistencyProtocol
```
через который автоматически получает доступ к Runtime.
Таким образом после интеграции Stream Consistency с Runtime архитектура Recovery начинает использовать Runtime без каких-либо изменений собственного кода.
Именно поэтому Build сознательно не вносит изменений в бизнес-логику Recovery.
Это стало одним из важнейших результатов проведённого архитектурного аудита.
---
# Атомарность Runtime
Одним из фундаментальных требований настоящего Build являлось сохранение полной независимости Runtime от выполняемых бизнес-операций.
Каждая операция обработки сделок должна рассматриваться как отдельная независимая транзакция.
При этом Runtime обязан сохранять накопленное состояние независимо от количества выполненных операций.
После завершения Build взаимодействие компонентов становится полностью детерминированным.
Каждый вызов Stream Consistency проходит одну и ту же последовательность этапов.
```text
Trade
TradeStreamConsistencyController
TradeRuntimeProtocol
TradeRuntimeRegistry
TradeStreamState
```
Каждый этап выполняет строго одну задачу.
Ни один уровень не повторяет обязанности другого.
Подобное разделение полностью соответствует принципу модульной архитектуры Dzentra.
---
# Использование Runtime внутри Stream Consistency
После получения очередной сделки контроллер последовательно выполняет несколько операций.
Сначала определяется Runtime-ключ соответствующего торгового символа.
Например.
```text
consistency:BTCUSDT
```
После этого через Runtime выполняется поиск объекта состояния.
Если объект уже существует, используется ранее накопленное состояние Trade Stream.
Если объект отсутствует, создаётся новый экземпляр
```text
TradeStreamState
```
который немедленно регистрируется внутри Runtime.
Все дальнейшие проверки последовательности выполняются уже над этим объектом.
Таким образом Runtime полностью инкапсулирует механизм хранения состояния, оставаясь полностью прозрачным для бизнес-логики контроллера.
---
# Lazy Registration
Следующим важным архитектурным результатом Build становится использование модели ленивой регистрации состояния.
Во время проектирования отдельно рассматривались два различных подхода.
Первый вариант предполагал предварительное создание объектов состояния для всех торговых символов.
После анализа данный подход был отклонён.
Он приводил бы к появлению большого количества неиспользуемых объектов.
Кроме того, Runtime оказался бы зависимым от информации о доступных рынках.
Поэтому было принято решение использовать модель Lazy Registration.
Состояние создаётся только при первом обращении соответствующего Runtime-компонента.
До этого момента Runtime не содержит никаких объектов данного типа.
Подобная модель обеспечивает минимальное потребление памяти и полностью соответствует принципу создания объектов по требованию.
---
# Runtime-ключи
Во время реализации отдельно анализировался вопрос идентификации объектов Runtime.
Первоначально рассматривалась возможность использования нескольких независимых внутренних коллекций.
Например.
```text
consistency_states
gap_states
scheduler_states
```
Однако подобная модель быстро привела бы к постоянному расширению самого Runtime Registry.
Каждый новый Runtime-модуль потребовал бы изменения его внутренней реализации.
Поэтому было принято решение использовать единое пространство строковых ключей.
Например.
```text
consistency:BTCUSDT
consistency:ETHUSDT
```
В дальнейшем аналогичным образом смогут использоваться любые другие пространства.
Подобная модель делает Runtime полностью открытым для расширения без изменения существующего кода Registry.
---
# Runtime-исключения
Следующим результатом Build становится появление специализированной иерархии инфраструктурных исключений Runtime.
До настоящего Build подобные ошибки отсутствовали.
В рамках реализации добавлены специализированные Runtime-исключения, отражающие нарушения инфраструктурных инвариантов Runtime Layer.
В частности предусмотрены ошибки:
- отсутствия зарегистрированного объекта;
- повторной регистрации несовместимого объекта;
- использования некорректного Runtime-ключа;
- нарушения правил доступа к Runtime.
Все данные исключения относятся исключительно к инфраструктурному уровню.
Они сознательно не используются для сигнализации ошибок бизнес-логики Stream Consistency или Recovery.
Подобное разделение позволяет чётко различать инфраструктурные проблемы и ошибки обработки рыночных данных.
---
# Производительность
Во время проектирования новой подсистемы одним из обязательных требований являлось сохранение постоянной сложности операций Runtime.
Registry не выполняет операций поиска по коллекциям объектов определённого типа.
Все обращения осуществляются непосредственно по Runtime-ключу.
Основные операции имеют следующую вычислительную сложность.
| Операция | Средняя сложность |
|----------|-------------------|
| Проверка существования объекта | O(1) |
| Получение объекта Runtime | O(1) |
| Регистрация нового объекта | O(1) |
| Замена существующего объекта | O(1) |
Таким образом внедрение Runtime не оказывает заметного влияния на производительность обработки Trade Stream.
Все дополнительные инфраструктурные операции выполняются за постоянное время.
---
# Изменённые файлы
В рамках Build была создана новая инфраструктурная подсистема **Trade Runtime**.
Все изменения были сознательно локализованы внутри нового каталога
```text
src/market_data/acquisition/runtime/
```
Подобное решение позволило полностью отделить Runtime от бизнес-компонентов Acquisition Layer.
Алгоритмы обработки сделок не потребовали изменения собственной логики.
Новая функциональность была встроена посредством внедрения нового инфраструктурного слоя.
---
## Trade Runtime Protocol
```text
src/market_data/acquisition/runtime/trade_runtime_protocol.py
```
Добавлен новый инфраструктурный Protocol.
```text
TradeRuntimeProtocol
```
Protocol определяет универсальный публичный контракт доступа к Runtime.
Все Runtime-компоненты взаимодействуют исключительно через данный контракт.
Конкретная реализация Registry остаётся внутренней деталью Runtime.
---
## Trade Runtime Registry
```text
src/market_data/acquisition/runtime/trade_runtime_registry.py
```
Реализован центральный компонент новой подсистемы.
Registry отвечает исключительно за регистрацию и предоставление объектов Runtime.
Внутри Registry отсутствует какая-либо бизнес-логика обработки сделок.
Все решения относительно изменения состояния принимаются исключительно компонентами, использующими Runtime.
---
## Trade Runtime Exceptions
```text
src/market_data/acquisition/runtime/trade_runtime_exceptions.py
```
Добавлена специализированная инфраструктурная иерархия исключений Runtime.
Все новые исключения относятся исключительно к инфраструктурному уровню хранения состояния.
Бизнес-компоненты Acquisition Layer продолжают использовать собственные доменные исключения.
---
## Интеграция Stream Consistency
Изменения в существующей подсистеме Stream Consistency были сознательно сведены к минимуму.
Основная бизнес-логика обработки сделок осталась неизменной.
Изменился исключительно механизм хранения состояния.
До начала настоящего Build контроллер самостоятельно управлял внутренней коллекцией состояний.
После завершения Build единственным владельцем долгоживущего состояния становится Runtime.
Контроллер получает состояние через публичный контракт `TradeRuntimeProtocol`, после чего продолжает использовать его так же, как и ранее.
Таким образом архитектура Stream Consistency была расширена без изменения алгоритмов обработки Trade Stream.
---
## Отсутствие изменений в Recovery Pipeline
Несмотря на первоначальные предположения, Build практически не затронул Recovery Pipeline.
Во время архитектурного аудита было подтверждено, что Recovery полностью соответствует принципам stateless-сервисов.
Recovery не хранит накопленное состояние.
Recovery не создаёт Runtime-объекты.
Recovery не управляет жизненным циклом Runtime.
Единственной зависимостью Recovery остаётся интерфейс
```text
TradeStreamConsistencyProtocol
```
через который автоматически используется Runtime.
Это позволило сохранить существующую архитектуру Recovery без внесения дополнительных изменений.
---
## Обратная совместимость
Одним из обязательных требований Build являлось сохранение полной обратной совместимости существующей подсистемы Trades Feed.
После внедрения Runtime не изменились:
- формат канонической модели `Trade`;
- последовательность обработки Trade Pipeline;
- алгоритмы проверки непрерывности потока;
- алгоритмы дедупликации;
- механизм восстановления пропущенных сделок;
- формат результатов Recovery;
- публичные контракты компонентов, не связанных с Runtime.
Таким образом Build представляет собой исключительно инфраструктурное расширение существующей архитектуры.
---
# Unit-тестирование
После завершения реализации новая подсистема Runtime была полностью покрыта unit-тестами.
Основная цель тестирования заключалась не только в проверке отдельных методов Registry, но и в подтверждении архитектурных инвариантов новой Runtime-модели.
Особое внимание уделялось следующим сценариям:
- регистрация нового объекта Runtime;
- повторное получение зарегистрированного объекта;
- проверка существования Runtime-ключа;
- корректная работа с несколькими независимыми пространствами ключей;
- обработка инфраструктурных ошибок Runtime;
- интеграция Stream Consistency с Runtime Registry.
Все тесты выполнялись независимо от компонентов REST Pipeline и Recovery Pipeline.
Это подтверждает самостоятельность новой подсистемы Runtime.
---
# Регрессионное тестирование
После завершения интеграции Runtime был выполнен полный регрессионный аудит подсистемы Trades Feed.
Проверка подтвердила сохранение поведения существующих компонентов.
В частности было подтверждено:
- корректная работа REST Pipeline;
- корректная работа Canonical Trade Model;
- сохранение алгоритмов Stream Consistency;
- корректная работа Recovery Pipeline;
- отсутствие изменений публичных контрактов существующих сервисов;
- отсутствие изменений в обработке Trade Sequence.
Таким образом внедрение Runtime не повлияло на функциональное поведение существующей подсистемы.
---
# Архитектурные результаты
Build 060.20 завершает формирование первого инфраструктурного уровня Runtime внутри подсистемы Market Data Acquisition.
Главными архитектурными результатами становятся:
- появление самостоятельного Runtime Layer;
- введение универсального контракта `TradeRuntimeProtocol`;
- реализация универсального `TradeRuntimeRegistry`;
- перенос хранения состояния из бизнес-компонентов в инфраструктурный слой;
- разделение понятий сервиса и состояния;
- сохранение stateless-характера Recovery;
- интеграция Stream Consistency с Runtime без изменения алгоритмов обработки сделок.
В результате архитектура Acquisition Layer становится значительно более модульной и масштабируемой.
---
# Подтверждённые архитектурные инварианты
После завершения Build были окончательно закреплены следующие архитектурные инварианты.
### Runtime хранит только состояние
Никакие сервисы не регистрируются внутри Runtime.
Runtime является исключительно инфраструктурным контейнером объектов состояния.
---
### Runtime не содержит бизнес-логики
Registry не знает:
- структуры сделок;
- алгоритмов обработки;
- механизмов Recovery;
- логики Stream Consistency.
Все подобные решения принимаются исключительно специализированными сервисами.
---
### Runtime использует единое пространство ключей
Все Runtime-объекты идентифицируются строковыми ключами.
Добавление новых Runtime-модулей не требует изменения реализации Registry.
---
### Stream Consistency остаётся владельцем бизнес-логики
Runtime не принимает решений относительно обработки сделок.
Он лишь предоставляет контроллеру доступ к ранее зарегистрированному состоянию.
---
### Recovery остаётся stateless
Recovery не использует собственное Runtime-состояние.
Взаимодействие с Runtime осуществляется исключительно через `TradeStreamConsistencyProtocol`.
---
### Composition Root не входит в Scope Build
Создание и связывание компонентов приложения не относится к задачам настоящего Build.
Runtime не выполняет функции Dependency Injection.
Реализация Composition Root переносится на один из последующих этапов развития архитектуры.
---
# Заключение
Build 060.20 завершает построение первого инфраструктурного слоя сопровождения состояния внутри подсистемы Trades Feed.
Первоначальная архитектурная спецификация предполагала создание Runtime как реестра долгоживущих компонентов.
Однако проведённый архитектурный аудит показал, что более правильной моделью является хранение не сервисов, а их состояния.
В результате Runtime был переосмыслен как универсальный инфраструктурный контейнер runtime-данных.
Это решение позволило:
- полностью отделить хранение состояния от бизнес-логики;
- сохранить stateless-архитектуру сервисов;
- упростить интеграцию новых Runtime-компонентов;
- подготовить архитектуру к последующему появлению Gap Detection, WebSocket Runtime, Runtime Orchestration и Composition Root.
Тем самым Build 060.20 завершает формирование базовой инфраструктуры Runtime и создаёт прочный фундамент для дальнейшего развития подсистемы **Trades Feed (Time & Sales)**.
---
# Что не входит в Scope Build
Настоящий Build сознательно ограничивается созданием инфраструктурного слоя Runtime.
Следующие задачи не входят в область настоящего этапа и будут реализованы позднее.
- Composition Root;
- Runtime Orchestration;
- WebSocket Runtime;
- Gap Detection Runtime;
- автоматическое управление жизненным циклом Runtime;
- очистка Runtime;
- сохранение Runtime между перезапусками приложения;
- потокобезопасная реализация Runtime Registry;
- распределённый Runtime.
Отсутствие перечисленных компонентов является осознанным архитектурным решением и не рассматривается как незавершённость Build.
---
# Итог Build
После завершения Build 060.20 подсистема Trades Feed обладает следующими возможностями.
✓ Runtime представлен самостоятельным инфраструктурным слоем.
Все долгоживущие состояния отделены от бизнес-логики.
✓ Stream Consistency использует Runtime через публичный Protocol.
✓ Recovery сохраняет полностью stateless-архитектуру.
✓ Runtime готов к появлению новых Runtime-модулей без изменения собственной реализации.
✓ Архитектура полностью подготовлена к реализации Composition Root.
Build считается полностью завершённым.
---
# Архитектурное значение Build
Несмотря на сравнительно небольшой объём изменений исходного кода, Build 060.20 является одним из ключевых архитектурных этапов развития подсистемы Trades Feed.
Именно в рамках настоящего Build было окончательно разделено понятие сервиса и сопровождаемого им состояния.
Это решение определяет дальнейшую архитектуру всех Runtime-компонентов Dzentra.
Все последующие stateful-модули должны использовать Runtime исключительно как инфраструктурный слой хранения состояния и не создавать собственных внутренних механизмов хранения.
Данный принцип считается базовым архитектурным инвариантом проекта.