1403 lines
62 KiB
Markdown
1403 lines
62 KiB
Markdown
# 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.md — архитектурная спецификация Build.
|
||
- build_060_19.md — Engineering Migration Report предыдущего 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 исключительно как инфраструктурный слой хранения состояния и не создавать собственных внутренних механизмов хранения.
|
||
|
||
Данный принцип считается базовым архитектурным инвариантом проекта. |