From 317b9683efa3e030cbcb88a6b272806e8fd9de1f Mon Sep 17 00:00:00 2001 From: Sergey Date: Thu, 23 Jul 2026 19:56:41 +0300 Subject: [PATCH] Build 060.20.1: implement Trade Runtime Layer --- app/src/market_data/__init__.py | 1 + .../acquisition/runtime/__init__.py | 1 + .../acquisition/runtime/trade/__init__.py | 0 .../runtime/trade/trade_runtime_exceptions.py | 43 + .../runtime/trade/trade_runtime_protocol.py | 85 + .../runtime/trade/trade_runtime_registry.py | 132 + .../trade/test_trade_runtime_registry.py | 126 + docs/migrations/build_060_20_architecture.md | 2459 +++++++++++++++++ 8 files changed, 2847 insertions(+) create mode 100644 app/src/market_data/acquisition/runtime/trade/__init__.py create mode 100644 app/src/market_data/acquisition/runtime/trade/trade_runtime_exceptions.py create mode 100644 app/src/market_data/acquisition/runtime/trade/trade_runtime_protocol.py create mode 100644 app/src/market_data/acquisition/runtime/trade/trade_runtime_registry.py create mode 100644 app/tests/unit/market_data/acquisition/runtime/trade/test_trade_runtime_registry.py create mode 100644 docs/migrations/build_060_20_architecture.md diff --git a/app/src/market_data/__init__.py b/app/src/market_data/__init__.py index e69de29..9b1800e 100644 --- a/app/src/market_data/__init__.py +++ b/app/src/market_data/__init__.py @@ -0,0 +1 @@ +# app/src/market_data/__init__.py \ No newline at end of file diff --git a/app/src/market_data/acquisition/runtime/__init__.py b/app/src/market_data/acquisition/runtime/__init__.py index e69de29..bc7afb8 100644 --- a/app/src/market_data/acquisition/runtime/__init__.py +++ b/app/src/market_data/acquisition/runtime/__init__.py @@ -0,0 +1 @@ +# app/src/market_data/acquisition/runtime/__init__.py \ No newline at end of file diff --git a/app/src/market_data/acquisition/runtime/trade/__init__.py b/app/src/market_data/acquisition/runtime/trade/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/src/market_data/acquisition/runtime/trade/trade_runtime_exceptions.py b/app/src/market_data/acquisition/runtime/trade/trade_runtime_exceptions.py new file mode 100644 index 0000000..7d3841b --- /dev/null +++ b/app/src/market_data/acquisition/runtime/trade/trade_runtime_exceptions.py @@ -0,0 +1,43 @@ +# app/src/market_data/acquisition/runtime/trade/trade_runtime_exceptions.py + +from __future__ import annotations + +""" +Инфраструктурные исключения Trade Runtime. + +Build 060.20 вводит инфраструктурный слой Trade Runtime. + +Данный модуль содержит только инфраструктурные исключения, +связанные с регистрацией и получением Runtime-компонентов. + +Бизнес-исключения отдельных Runtime-модулей (Recovery, +Stream Consistency и других) должны оставаться +внутри соответствующих подсистем. +""" + + +class TradeRuntimeError(Exception): + """ + Базовый класс для всех инфраструктурных исключений Trade Runtime. + """ + + +class RuntimeAlreadyRegisteredError(TradeRuntimeError): + """ + Вызывается при попытке повторной регистрации Runtime-компонента + под уже существующим ключом. + """ + + +class RuntimeNotRegisteredError(TradeRuntimeError): + """ + Вызывается при попытке получить Runtime-компонент, + который отсутствует в TradeRuntimeRegistry. + """ + + +class InvalidRuntimeComponentError(TradeRuntimeError): + """ + Вызывается при попытке зарегистрировать объект, + который не может являться Runtime-компонентом. + """ \ No newline at end of file diff --git a/app/src/market_data/acquisition/runtime/trade/trade_runtime_protocol.py b/app/src/market_data/acquisition/runtime/trade/trade_runtime_protocol.py new file mode 100644 index 0000000..bfeab8c --- /dev/null +++ b/app/src/market_data/acquisition/runtime/trade/trade_runtime_protocol.py @@ -0,0 +1,85 @@ +# app/src/market_data/acquisition/runtime/trade/trade_runtime_protocol.py + +from __future__ import annotations + +""" +Протокол Trade Runtime. + +Build 060.20 вводит единый Runtime Layer для управления +жизненным циклом компонентов, обеспечивающих обработку +биржевого потока сделок. + +TradeRuntimeProtocol определяет минимальный контракт, +которому должна соответствовать реализация Runtime Registry. + +Протокол не описывает внутреннюю реализацию хранения +компонентов и не содержит бизнес-логики. +""" + +from typing import Any, Protocol + + +class TradeRuntimeProtocol(Protocol): + """ + Протокол реестра Runtime-компонентов. + """ + + def register(self, key: str, component: Any) -> None: + """ + Регистрирует Runtime-компонент. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + component: + Экземпляр Runtime-компонента. + """ + ... + + def unregister(self, key: str) -> None: + """ + Удаляет Runtime-компонент из реестра. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + """ + ... + + def get(self, key: str) -> Any: + """ + Возвращает зарегистрированный Runtime-компонент. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + Returns: + Зарегистрированный Runtime-компонент. + + Raises: + RuntimeNotRegisteredError: + Если компонент отсутствует в реестре. + """ + ... + + def is_registered(self, key: str) -> bool: + """ + Проверяет наличие Runtime-компонента в реестре. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + Returns: + True, если компонент зарегистрирован, + иначе False. + """ + ... + + def clear(self) -> None: + """ + Полностью очищает реестр Runtime-компонентов. + """ + ... \ No newline at end of file diff --git a/app/src/market_data/acquisition/runtime/trade/trade_runtime_registry.py b/app/src/market_data/acquisition/runtime/trade/trade_runtime_registry.py new file mode 100644 index 0000000..7f61f76 --- /dev/null +++ b/app/src/market_data/acquisition/runtime/trade/trade_runtime_registry.py @@ -0,0 +1,132 @@ +# app/src/market_data/acquisition/runtime/trade/trade_runtime_registry.py + +from __future__ import annotations + +""" +Реестр компонентов Trade Runtime. + +Build 060.20 вводит единый инфраструктурный реестр, +который владеет экземплярами Runtime-компонентов +и предоставляет их по уникальным строковым ключам. + +Реестр не управляет бизнес-состоянием компонентов +и не содержит логики Stream Consistency, Recovery +или других подсистем обработки потока сделок. +""" + +from typing import Any + +from src.market_data.acquisition.runtime.trade.trade_runtime_exceptions import ( + InvalidRuntimeComponentError, + RuntimeAlreadyRegisteredError, + RuntimeNotRegisteredError, +) +from src.market_data.acquisition.runtime.trade.trade_runtime_protocol import ( + TradeRuntimeProtocol, +) + + +class TradeRuntimeRegistry(TradeRuntimeProtocol): + """ + Реестр Runtime-компонентов потока сделок. + + Каждый компонент регистрируется под уникальным строковым ключом. + Повторная регистрация под тем же ключом запрещена. + """ + + def __init__(self) -> None: + """ + Создаёт пустой реестр Runtime-компонентов. + """ + self._components: dict[str, Any] = {} + + def register(self, key: str, component: Any) -> None: + """ + Регистрирует Runtime-компонент под уникальным ключом. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + component: + Экземпляр Runtime-компонента. + + Raises: + InvalidRuntimeComponentError: + Если вместо Runtime-компонента передан None. + + RuntimeAlreadyRegisteredError: + Если указанный ключ уже используется. + """ + if component is None: + raise InvalidRuntimeComponentError( + f"Runtime-компонент для ключа {key!r} не может быть None." + ) + + if key in self._components: + raise RuntimeAlreadyRegisteredError( + f"Runtime-компонент с ключом {key!r} уже зарегистрирован." + ) + + self._components[key] = component + + def unregister(self, key: str) -> None: + """ + Удаляет Runtime-компонент из реестра. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + Raises: + RuntimeNotRegisteredError: + Если компонент с указанным ключом отсутствует. + """ + if key not in self._components: + raise RuntimeNotRegisteredError( + f"Runtime-компонент с ключом {key!r} не зарегистрирован." + ) + + del self._components[key] + + def get(self, key: str) -> Any: + """ + Возвращает зарегистрированный Runtime-компонент. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + Returns: + Зарегистрированный Runtime-компонент. + + Raises: + RuntimeNotRegisteredError: + Если компонент с указанным ключом отсутствует. + """ + try: + return self._components[key] + except KeyError as error: + raise RuntimeNotRegisteredError( + f"Runtime-компонент с ключом {key!r} не зарегистрирован." + ) from error + + def is_registered(self, key: str) -> bool: + """ + Проверяет наличие Runtime-компонента в реестре. + + Args: + key: + Уникальный идентификатор Runtime-компонента. + + Returns: + True, если компонент зарегистрирован, + иначе False. + """ + return key in self._components + + def clear(self) -> None: + """ + Удаляет из реестра все зарегистрированные Runtime-компоненты. + """ + self._components.clear() \ No newline at end of file diff --git a/app/tests/unit/market_data/acquisition/runtime/trade/test_trade_runtime_registry.py b/app/tests/unit/market_data/acquisition/runtime/trade/test_trade_runtime_registry.py new file mode 100644 index 0000000..be3d9a4 --- /dev/null +++ b/app/tests/unit/market_data/acquisition/runtime/trade/test_trade_runtime_registry.py @@ -0,0 +1,126 @@ +# app/tests/unit/market_data/acquisition/runtime/trade/test_trade_runtime_registry.py + +from __future__ import annotations + +import pytest + +from src.market_data.acquisition.runtime.trade.trade_runtime_exceptions import ( + InvalidRuntimeComponentError, + RuntimeAlreadyRegisteredError, + RuntimeNotRegisteredError, +) +from src.market_data.acquisition.runtime.trade.trade_runtime_registry import ( + TradeRuntimeRegistry, +) + + +class DummyComponent: + """Тестовый Runtime-компонент.""" + + +@pytest.fixture +def component() -> DummyComponent: + """Создаёт тестовый Runtime-компонент.""" + return DummyComponent() + + +def test_registry_is_empty_after_creation() -> None: + registry = TradeRuntimeRegistry() + + assert registry.is_registered("component") is False + + +def test_is_registered_returns_false_for_unknown_key() -> None: + registry = TradeRuntimeRegistry() + + assert registry.is_registered("unknown") is False + + +def test_register_component(component: DummyComponent) -> None: + registry = TradeRuntimeRegistry() + + registry.register("component", component) + + assert registry.is_registered("component") is True + + +def test_get_returns_registered_component( + component: DummyComponent, +) -> None: + registry = TradeRuntimeRegistry() + + registry.register("component", component) + + assert registry.get("component") is component + + +def test_is_registered_returns_true_after_registration( + component: DummyComponent, +) -> None: + registry = TradeRuntimeRegistry() + + registry.register("component", component) + + assert registry.is_registered("component") is True + + +def test_register_none_raises_invalid_runtime_component_error() -> None: + registry = TradeRuntimeRegistry() + + with pytest.raises(InvalidRuntimeComponentError): + registry.register("component", None) + + +def test_register_duplicate_key_raises_runtime_already_registered_error( + component: DummyComponent, +) -> None: + registry = TradeRuntimeRegistry() + + registry.register("component", component) + + with pytest.raises(RuntimeAlreadyRegisteredError): + registry.register("component", DummyComponent()) + + +def test_unregister_component( + component: DummyComponent, +) -> None: + registry = TradeRuntimeRegistry() + + registry.register("component", component) + registry.unregister("component") + + assert registry.is_registered("component") is False + + with pytest.raises(RuntimeNotRegisteredError): + registry.get("component") + + +def test_unregister_unknown_component_raises_runtime_not_registered_error() -> None: + registry = TradeRuntimeRegistry() + + with pytest.raises(RuntimeNotRegisteredError): + registry.unregister("unknown") + + +def test_clear_removes_all_components() -> None: + registry = TradeRuntimeRegistry() + + registry.register("component_1", DummyComponent()) + registry.register("component_2", DummyComponent()) + registry.register("component_3", DummyComponent()) + + registry.clear() + + assert registry.is_registered("component_1") is False + assert registry.is_registered("component_2") is False + assert registry.is_registered("component_3") is False + + with pytest.raises(RuntimeNotRegisteredError): + registry.get("component_1") + + with pytest.raises(RuntimeNotRegisteredError): + registry.get("component_2") + + with pytest.raises(RuntimeNotRegisteredError): + registry.get("component_3") \ No newline at end of file diff --git a/docs/migrations/build_060_20_architecture.md b/docs/migrations/build_060_20_architecture.md new file mode 100644 index 0000000..186a420 --- /dev/null +++ b/docs/migrations/build_060_20_architecture.md @@ -0,0 +1,2459 @@ +# Build 060.20 — Trade Runtime Architecture + +**Статус:** Architecture Specification +**Build:** 060.20 +**Ветка:** Trades Feed (Time & Sales) +**Документ:** `build_060_20_architecture.md` +**Предыдущие рабочие названия:** Trade Recovery Registry, Trade Runtime Registry + +**Связанные документы:** + +- `build_060_20.md` — план реализации Build; +- `build_060_19_architecture.md`; +- `build_060_18_architecture.md`. + +--- + +# Назначение документа + +Настоящий документ является официальной архитектурной спецификацией Build **060.20 — Trade Runtime Architecture**. + +Документ определяет: + +- архитектурную модель Trade Runtime; +- границы Trade Runtime; +- состав Runtime-модулей текущего Build; +- ответственность `TradeRuntimeRegistry`; +- модель владения жизненным циклом Runtime-компонентов; +- модель владения бизнес-состоянием Canonical Trade Stream; +- границы интеграции Runtime и Acquisition; +- правила Dependency Injection; +- файловый план; +- стратегию тестирования; +- критерии завершения Build. + +Документ является единственным источником истины для реализации Build 060.20. + +Все решения, зафиксированные настоящей спецификацией, считаются утверждёнными до начала реализации. + +Изменение этих решений во время разработки допускается только после: + +1. остановки реализации; +2. повторного архитектурного анализа; +3. подготовки отдельного ADR; +4. обновления настоящего документа. + +--- + +# Статус Build + +Build 060.20 является непосредственным продолжением серии Build 060, посвящённой построению подсистемы получения и сопровождения потока сделок: + +```text +Trades Feed / Time & Sales +``` + +К началу Build в системе уже существуют: + +- каноническая модель `Trade`; +- Trades Feed; +- Trade Stream Consistency; +- `TradeStreamConsistencyController`; +- Trade Recovery; +- `TradeRecoveryController`; +- REST Recovery Pipeline; +- Runtime Protocol. + +Система уже умеет: + +- получать документы сделок из REST; +- преобразовывать транспортные документы в каноническую модель `Trade`; +- контролировать порядок канонического потока; +- исключать повторную публикацию сделок; +- обнаруживать конфликтующие повторы; +- обнаруживать нарушения непрерывности потока; +- восстанавливать отсутствующие участки посредством Recovery. + +Однако предыдущие Build не определяют единую архитектурную модель существования долгоживущих компонентов Trade Stream. + +В частности, ещё не определено: + +- где создаются stateful-компоненты; +- кто владеет их жизненным циклом; +- как обеспечивается повторное использование экземпляров; +- как разные Runtime-модули получают доступ к общему состоянию потока; +- где проходит граница между Acquisition и Runtime; +- как в дальнейшем подключать новые Runtime-модули без создания отдельных Registry. + +Build 060.20 закрывает именно эту архитектурную задачу. + +--- + +# Предпосылки + +Настоящий Build опирается на архитектурные решения, принятые в предыдущих этапах. + +--- + +## Build 057 + +Определены базовые принципы новой архитектуры Market Data Acquisition. + +Разделены: + +- транспортный уровень; +- слой преобразования; +- каноническая доменная модель; +- интеграционные границы. + +Acquisition построен преимущественно из stateless-компонентов. + +--- + +## Build 060.1 + +Введена единая каноническая модель: + +```text +Trade +``` + +Все источники сделок обязаны приводить входные данные к одной Domain Model. + +Источник данных не должен влиять на последующую обработку сделки. + +--- + +## Build 060.17 + +Построен Trades Feed. + +Получение сделок отделено от конкретной транспортной реализации. + +Feed отвечает за получение и публикацию канонических сделок, но не является владельцем долгоживущего состояния потока. + +--- + +## Build 060.18 + +Введён: + +```text +TradeStreamConsistencyController +``` + +Он стал первым явно stateful-компонентом новой подсистемы Trades Feed. + +Контроллер определён как единственный владелец бизнес-состояния Canonical Trade Stream. + +Он отвечает за: + +- порядок сделок; +- дедупликацию; +- обнаружение конфликтующих повторов; +- обнаружение разрывов; +- сохранение текущего состояния потока. + +Build 060.18 определил владельца бизнес-состояния, но сознательно не определил владельца жизненного цикла самого Controller. + +--- + +## Build 060.19 + +Введён: + +```text +TradeRecoveryController +``` + +Он отвечает за выполнение Recovery Pipeline и восстановление отсутствующих участков потока. + +Recovery использует существующий `TradeStreamConsistencyController`, но не должен владеть его состоянием или жизненным циклом. + +Build 060.19 определил операцию Recovery, но сознательно не формализовал Runtime, внутри которого эта операция выполняется. + +--- + +# Архитектурная проблема + +Предыдущая архитектура Market Data Acquisition основана преимущественно на короткоживущих stateless-компонентах. + +Типичный Acquisition Pipeline выглядит следующим образом. + +```text +Source + +↓ + +Parser + +↓ + +Validation + +↓ + +Mapper + +↓ + +Handler + +↓ + +Canonical Model +``` + +Такие компоненты могут: + +- создаваться перед выполнением операции; +- использоваться для обработки данных; +- уничтожаться после завершения операции. + +Их повторное создание не приводит к потере бизнес-состояния. + +--- + +## Появление stateful-компонентов + +`TradeStreamConsistencyController` не соответствует такой модели жизненного цикла. + +Он хранит накопленное состояние Canonical Trade Stream. + +Например: + +- последнюю принятую сделку; +- уже обработанные идентификаторы; +- сведения, необходимые для контроля порядка; +- сведения, необходимые для обнаружения разрыва; +- другую информацию, определённую Build 060.18. + +Если при каждой операции создаётся новый экземпляр Controller, накопленное состояние теряется. + +В результате: + +- последовательные вызовы перестают работать с единым потоком; +- дедупликация ограничивается одним вызовом; +- контроль порядка начинается заново; +- Recovery не продолжает уже существующий Canonical Trade Stream; +- REST и будущий WebSocket могут получить независимые состояния; +- система перестаёт иметь единую каноническую картину Trade Stream. + +Следовательно, stateful-компонент не может принадлежать жизненному циклу отдельной Acquisition-операции. + +--- + +## Недостаточность модели Recovery Runtime + +Первоначально проблема рассматривалась как необходимость создания: + +```text +Recovery Runtime +``` + +Однако эта модель является слишком узкой. + +`TradeStreamConsistencyController` необходим не только Recovery. + +Он сопровождает Canonical Trade Stream независимо от источника и способа доставки сделок. + +Его потенциальными потребителями являются: + +- REST Recovery; +- WebSocket Trades Feed; +- Gap Detection; +- Stream Monitoring; +- Recovery Scheduler; +- другие будущие Runtime-модули. + +Следовательно, состояние и жизненный цикл принадлежат не операции Recovery. + +Они принадлежат подсистеме сопровождения Trade Stream. + +--- + +# Основная идея Build + +Build 060.20 вводит новый официальный архитектурный уровень: + +```text +Trade Runtime +``` + +Trade Runtime существует независимо от: + +- отдельных REST-запросов; +- отдельных Recovery-вызовов; +- Feed-вызовов; +- транспортного источника; +- конкретной операции Acquisition. + +Trade Runtime объединяет долгоживущие компоненты, необходимые для сопровождения единого Canonical Trade Stream. + +--- + +# Разделение жизненных циклов + +Build формально разделяет два независимых жизненных цикла. + +--- + +## Acquisition Lifetime + +```text +создание операции + +↓ + +получение данных + +↓ + +преобразование данных + +↓ + +публикация результата + +↓ + +завершение операции +``` + +Acquisition-компоненты могут быть короткоживущими. + +Они не должны владеть состоянием непрерывного Trade Stream. + +--- + +## Trade Runtime Lifetime + +```text +создание Runtime + +↓ + +регистрация Runtime-компонентов + +↓ + +многократное использование + +↓ + +обслуживание Trade Stream + +↓ + +завершение процесса +``` + +Trade Runtime существует дольше любой отдельной Acquisition-операции. + +Количество вызовов Feed или Recovery не должно влиять на количество экземпляров Runtime-компонентов. + +--- + +# Trade Runtime Model + +## Определение + +**Trade Runtime** — это инфраструктурная подсистема долгоживущих компонентов, обеспечивающих сопровождение единого Canonical Trade Stream в течение жизни процесса. + +Trade Runtime отвечает за существование и доступность Runtime-компонентов. + +Он не является: + +- источником данных; +- Feed; +- Handler; +- транспортным адаптером; +- Recovery-операцией; +- владельцем канонической бизнес-модели `Trade`; +- хранилищем бизнес-логики Acquisition. + +--- + +## Состав Trade Runtime в текущем Build + +В рамках Build 060.20 Trade Runtime включает два существующих функциональных модуля. + +```text +Trade Runtime +│ +├── Stream Consistency Module +│ └── TradeStreamConsistencyController +│ +└── Recovery Module + └── TradeRecoveryController +``` + +Build не переносит бизнес-логику этих компонентов. + +Build изменяет только модель их создания, хранения, получения и повторного использования. + +--- + +## Stream Consistency Module + +Stream Consistency Module отвечает за согласованность Canonical Trade Stream. + +Его основным компонентом является: + +```text +TradeStreamConsistencyController +``` + +Он остаётся единственным владельцем бизнес-состояния потока. + +Build 060.20 не изменяет его бизнес-ответственность. + +--- + +## Recovery Module + +Recovery Module отвечает за восстановление отсутствующих участков Trade Stream. + +Его основным компонентом является: + +```text +TradeRecoveryController +``` + +Recovery является одним из модулей Trade Runtime. + +Он не является: + +- самим Runtime; +- владельцем Runtime; +- владельцем состояния потока; +- владельцем жизненного цикла Consistency Controller. + +--- + +## Возможное дальнейшее расширение + +В последующих Build Trade Runtime может быть расширен новыми модулями. + +```text +Trade Runtime +│ +├── Stream Consistency Module +├── Recovery Module +├── Gap Detection Module +├── Recovery Scheduling Module +├── Stream Monitoring Module +└── другие Runtime-модули +``` + +Build 060.20 не реализует эти будущие модули. + +Их перечисление определяет только допустимое направление архитектурного расширения. + +--- + +# TradeRuntimeRegistry + +Для управления доступом к долгоживущим компонентам Build вводит: + +```text +TradeRuntimeRegistry +``` + +`TradeRuntimeRegistry` является инфраструктурным реестром компонентов Trade Runtime. + +Он становится единой точкой доступа к зарегистрированным Runtime-компонентам. + +Концептуально: + +```text +TradeRuntimeRegistry +│ +├── TradeStreamConsistencyController +└── TradeRecoveryController +``` + +Registry не превращает компоненты в единый объект с общей бизнес-логикой. + +Каждый Runtime-компонент сохраняет: + +- собственную ответственность; +- собственный публичный контракт; +- собственные зависимости; +- собственную область состояния. + +Registry обеспечивает только их корректное существование и предоставление. + +--- + +# Цель Build + +Цель Build 060.20 — сформировать архитектуру Trade Runtime как самостоятельной инфраструктурной подсистемы Dzentra. + +В рамках данного Build определяется: + +* архитектура Runtime; +* Runtime Layer; +* Runtime-модули; +* TradeRuntimeRegistry; +* модель владения жизненным циклом; +* модель владения бизнес-состоянием; +* архитектурные инварианты Runtime. + +--- + +# Что НЕ входит в Scope Build + +Настоящий Build сознательно НЕ реализует: + +- окончательный Composition Root; +- объединение существующих `service.py`; +- изменение архитектуры Acquisition; +- изменение архитектуры Feed; +- изменение архитектуры Handler; +- изменение Runtime Protocol; +- сохранение Runtime между перезапусками процесса; +- распределённый Runtime; +- поддержку нескольких процессов; +- Runtime для других доменных подсистем; +- интеграцию WebSocket Trades Feed; +- Gap Detection; +- Runtime Scheduler; +- Stream Monitoring; +- Runtime Metrics; +- Runtime Health Check; +- Runtime Lifecycle Manager. + +Все перечисленные задачи относятся к следующим Build серии 060. + +Build 060.20 отвечает исключительно за формирование архитектуры локального Trade Runtime. + +--- + +# Архитектурные принципы + +Настоящий Build вводит фундаментальные принципы построения Runtime-подсистем Dzentra. + +Все последующие Runtime должны соответствовать данным правилам. + +--- + +## 1. Runtime Before Acquisition + +Любой долгоживущий компонент принадлежит Runtime. + +Acquisition никогда не создаёт Runtime самостоятельно. + +Acquisition только использует уже существующие Runtime-компоненты. + +--- + +## 2. Stateless Acquisition + +Подсистема Acquisition полностью сохраняет архитектуру предыдущих Build. + +Stateless остаются: + +- Source; +- Parser; +- Validation; +- Mapper; +- Handler; +- Feed. + +Build 060.20 не изменяет данное правило. + +--- + +## 3. Runtime Owns Lifetime + +Trade Runtime является единственным владельцем жизненного цикла Runtime-компонентов. + +Никакой Runtime-компонент не имеет права самостоятельно определять собственное существование. + +--- + +## 4. Business State Owns Itself + +Жизненный цикл компонентов и бизнес-состояние являются независимыми понятиями. + +Trade Runtime отвечает за существование компонентов. + +Каждый Runtime-модуль самостоятельно отвечает за собственное бизнес-состояние. + +--- + +## 5. Single Runtime Instance + +Каждый Runtime-компонент существует в единственном экземпляре. + +Количество Recovery-вызовов, + +Feed-вызовов, + +или других Runtime-операций + +не влияет на количество Runtime-компонентов. + +--- + +## 6. Runtime Reuse + +Все Runtime-модули используют один и тот же экземпляр зарегистрированных компонентов. + +Runtime никогда не создаётся повторно во время обычной работы процесса. + +--- + +## 7. Composition Outside Runtime + +Trade Runtime никогда самостоятельно не создаёт собственные зависимости. + +Все зависимости передаются извне. + +Composition остаётся единственной точкой создания компонентов. + +--- + +## 8. Runtime Is Infrastructure + +Trade Runtime является инфраструктурным уровнем. + +Runtime не содержит: + +- транспортной логики; +- доменной логики; +- логики получения данных; +- логики публикации данных. + +Он обеспечивает исключительно существование Runtime-компонентов. + +--- + +## 9. Runtime Is Extensible + +Добавление нового Runtime-модуля не должно требовать изменения архитектуры Runtime. + +Новый модуль регистрируется внутри существующего Runtime. + +Создание отдельных Registry для каждого нового модуля запрещается. + +--- + +## 10. Unique File Naming + +Во всём проекте Dzentra запрещено существование нескольких файлов с одинаковыми именами независимо от каталогов. + +Единственным исключением остаётся: + +```text +__init__.py +``` + +Каждый Runtime-компонент получает уникальное имя файла. + +Например: + +```text +trade_runtime_registry.py + +trade_runtime_protocol.py + +trade_runtime_exceptions.py +``` + +--- + +# Модель владения + +Одной из основных целей Build является разделение различных видов ответственности. + +До настоящего Build существовало только понятие владельца состояния. + +Build 060.20 впервые разделяет два независимых уровня владения. + +--- + +## Владение жизненным циклом + +За существование Runtime-компонентов отвечает исключительно: + +```text +TradeRuntimeRegistry +``` + +Он определяет: + +- создание компонентов; +- регистрацию; +- хранение; +- предоставление; +- повторное использование. + +Registry никогда не изменяет внутреннее состояние компонентов. + +--- + +## Владение бизнес-состоянием + +Каждый Runtime-модуль самостоятельно владеет собственным состоянием. + +Например: + +```text +TradeStreamConsistencyController +``` + +остаётся владельцем: + +- порядка сделок; +- дедупликации; +- обнаружения конфликтующих повторов; +- информации о состоянии Canonical Trade Stream. + +Registry не имеет доступа к этому состоянию. + +--- + +## Владение выполнением операций + +За выполнение бизнес-операций отвечает соответствующий Runtime-модуль. + +Например: + +```text +TradeRecoveryController +``` + +отвечает исключительно за выполнение Recovery Pipeline. + +Recovery не отвечает: + +- за создание Runtime; +- за регистрацию Runtime; +- за получение Runtime; +- за уничтожение Runtime. + +--- + +# Архитектурная модель Runtime + +После завершения Build Runtime приобретает следующую структуру. + +```text +Trade Runtime + +│ + +├── Stream Consistency Module + +│ │ + +│ ▼ + +│ TradeStreamConsistencyController + +│ + +└── Recovery Module + + │ + + ▼ + + TradeRecoveryController +``` + +Каждый модуль обладает собственной ответственностью. + +Ни один модуль не становится владельцем другого. + +Runtime лишь объединяет их в одну долгоживущую инфраструктурную подсистему. + +--- + +# TradeRuntimeRegistry + +TradeRuntimeRegistry является владельцем Runtime Layer. + +Концептуально Runtime выглядит следующим образом. + +```text +TradeRuntimeRegistry + +│ + +├── Stream Consistency Module + +│ │ + +│ ▼ + +│ TradeStreamConsistencyController + +│ + +└── Recovery Module + + │ + + ▼ + + TradeRecoveryController +``` + +Registry знает, + +какие Runtime-компоненты существуют. + +Registry не знает, + +как работает каждый Runtime-модуль. + +Это принципиальное архитектурное разделение. + +--- + +# Инварианты Trade Runtime + +Любая реализация Runtime обязана удовлетворять следующим архитектурным инвариантам. + +--- + +## Инвариант №1 + +Trade Runtime существует независимо от любых операций Acquisition. + +--- + +## Инвариант №2 + +Trade Runtime существует независимо от Recovery. + +Recovery является пользователем Runtime. + +--- + +## Инвариант №3 + +TradeRuntimeRegistry является единственным владельцем жизненного цикла Runtime-компонентов. + +--- + +## Инвариант №4 + +TradeStreamConsistencyController остаётся единственным владельцем состояния Canonical Trade Stream. + +--- + +## Инвариант №5 + +TradeRecoveryController никогда не владеет состоянием Stream Consistency. + +--- + +## Инвариант №6 + +Recovery никогда самостоятельно не создаёт Runtime. + +--- + +## Инвариант №7 + +Feed никогда самостоятельно не создаёт Runtime. + +--- + +## Инвариант №8 + +Количество Runtime-компонентов не зависит от количества операций. + +--- + +## Инвариант №9 + +Каждый Runtime-модуль может быть заменён независимо от остальных. + +--- + +## Инвариант №10 + +Trade Runtime полностью независим от источника данных. + +REST, + +WebSocket, + +Replay, + +или любой будущий транспорт + +используют один и тот же Runtime. + +--- + +# Граница Runtime + +Build 060.20 вводит новую официальную архитектурную границу. + +До настоящего Build существовало только Acquisition. + +```text +REST + +↓ + +Recovery + +↓ + +Consistency +``` + +После завершения Build появляется самостоятельный Runtime Layer. + +```text +Trade Runtime + +│ + +├── Stream Consistency + +└── Recovery + +↓ + +Acquisition Operations + +↓ + +REST + +WebSocket + +Replay + +... +``` + +Таким образом Runtime становится постоянной инфраструктурной подсистемой, + +а Acquisition превращается лишь в набор операций, использующих Runtime. + +--- + +# Архитектура компонентов + +После определения модели Trade Runtime необходимо определить архитектурные компоненты, обеспечивающие его существование. + +Build 060.20 не вводит новую бизнес-логику. + +Все существующие алгоритмы Recovery и Stream Consistency сохраняются без изменений. + +Build вводит новую инфраструктурную модель их существования. + +--- + +# Архитектура Runtime + +После завершения Build Runtime состоит из трёх независимых компонентов. + +```text + Trade Runtime + + │ + + ▼ + + TradeRuntimeRegistry + + ┌─────────┴─────────┐ + + ▼ ▼ + + Stream Consistency Module Recovery Module + + │ │ + + ▼ ▼ + +TradeStreamConsistencyController TradeRecoveryController +``` + +TradeRuntimeRegistry не является владельцем бизнес-логики. + +Он обеспечивает только существование Runtime-модулей. + +Каждый Runtime-модуль сохраняет собственную ответственность. + +--- + +# TradeRuntimeRegistry + +## Назначение + +TradeRuntimeRegistry является единственной точкой доступа ко всем Runtime-компонентам Trade Runtime. + +Registry предоставляет зарегистрированные Runtime-компоненты другим частям системы. + +Registry не принимает участия в обработке сделок. + +Registry не выполняет Recovery. + +Registry не контролирует поток сделок. + +--- + +# Ответственность Registry + +Registry отвечает исключительно за: + +- регистрацию Runtime-компонентов; +- поиск Runtime-компонентов; +- предоставление Runtime-компонентов; +- контроль корректности регистрации; +- обеспечение повторного использования Runtime-компонентов. + +--- + +# Registry НЕ отвечает + +TradeRuntimeRegistry сознательно не отвечает за: + +- создание бизнес-состояния; +- выполнение Recovery; +- получение данных; +- работу Feed; +- работу Parser; +- работу Handler; +- согласование Trade Stream; +- транспортную интеграцию; +- управление жизненным циклом процесса. + +Registry остаётся исключительно инфраструктурным компонентом Runtime. + +--- + +# Главный принцип Registry + +Registry знает, + +какие Runtime-компоненты существуют. + +Registry не знает, + +что делает каждый Runtime-модуль. + +Именно это позволяет независимо развивать Runtime без изменения Registry. + +--- + +# Stream Consistency Module + +## Назначение + +Stream Consistency Module отвечает за сопровождение непрерывного Canonical Trade Stream. + +Его центральным компонентом является: + +```text +TradeStreamConsistencyController +``` + +Build 060.20 не изменяет его алгоритмы. + +Изменяется исключительно модель его существования. + +До настоящего Build экземпляр Controller создавался как обычный объект. + +После завершения Build Controller становится долгоживущим Runtime-компонентом. + +--- + +# Ответственность Stream Consistency + +Контроллер продолжает отвечать исключительно за: + +- контроль порядка сделок; +- дедупликацию; +- обнаружение конфликтующих повторов; +- обнаружение нарушений последовательности; +- поддержку состояния Canonical Trade Stream. + +Никаких новых бизнес-обязанностей Build не вводит. + +--- + +# Почему состояние принадлежит именно этому модулю + +Во время проектирования рассматривались различные варианты. + +--- + +## Вариант 1 + +Передать состояние Recovery Module. + +Отклонён. + +Recovery выполняет операции. + +Recovery не сопровождает поток. + +--- + +## Вариант 2 + +Передать состояние Registry. + +Отклонён. + +Registry становится владельцем бизнес-состояния. + +Это нарушает принцип разделения ответственности. + +--- + +## Вариант 3 + +Оставить состояние внутри Stream Consistency Module. + +Принят. + +Именно данный модуль сопровождает Canonical Trade Stream. + +Следовательно именно он обязан владеть его состоянием. + +--- + +# Recovery Module + +## Назначение + +Recovery Module отвечает исключительно за выполнение операций восстановления Trade Stream. + +Его центральным компонентом является: + +```text +TradeRecoveryController +``` + +Recovery Module не является владельцем Runtime. + +Recovery Module не является владельцем Stream Consistency. + +Recovery Module является пользователем Runtime. + +--- + +# Ответственность Recovery Module + +Recovery Module отвечает исключительно за: + +- выполнение Recovery Pipeline; +- получение Recovery Source; +- передачу сделок в Stream Consistency; +- возврат результата Recovery. + +--- + +# Recovery Module НЕ отвечает + +Recovery Module сознательно не отвечает за: + +- создание Runtime; +- регистрацию Runtime; +- создание Stream Consistency; +- управление жизненным циклом Runtime; +- хранение состояния Canonical Trade Stream. + +Все перечисленные обязанности принадлежат другим компонентам Runtime. + +--- + +# Главный принцип Recovery Module + +Recovery выполняет операции. + +Stream Consistency сопровождает поток. + +Registry обеспечивает существование компонентов. + +Каждый Runtime-модуль отвечает только за собственную область ответственности. + +--- + +# Взаимодействие Runtime-модулей + +Runtime-модули взаимодействуют исключительно через публичные контракты. + +Концептуально схема выглядит следующим образом. + +```text + Trade Runtime + + │ + + ▼ + + TradeRuntimeRegistry + + ┌─────────┴─────────┐ + + ▼ ▼ + +TradeStreamConsistencyController TradeRecoveryController + + ▲ │ + + └───────────────────┘ + + Recovery использует + + Stream Consistency +``` + +Из данной схемы следует: + +- Registry ничего не знает о внутреннем устройстве Recovery; +- Registry ничего не знает о внутреннем устройстве Stream Consistency; +- Recovery знает только публичный контракт Stream Consistency; +- Stream Consistency ничего не знает о Recovery. + +Таким образом Runtime состоит из независимых модулей с минимально необходимыми зависимостями. + +--- + +# Главная архитектурная граница + +После завершения Build официальная архитектурная модель становится следующей. + +```text + Runtime Layer + + TradeRuntimeRegistry + + │ + + ┌──────────┴──────────┐ + + ▼ ▼ + +Stream Consistency Recovery Module + +──────────────────────────────────────────── + + Acquisition Layer + +──────────────────────────────────────────── + +REST + +WebSocket + +Replay + +Recovery Requests + +... + +──────────────────────────────────────────── + + Domain Layer + +──────────────────────────────────────────── + +Canonical Trade Stream +``` + +Trade Runtime становится самостоятельным инфраструктурным слоем системы. + +Acquisition использует Runtime. + +Domain получает уже согласованный поток сделок. + +Ни один слой не нарушает ответственность другого. + +--- + +# Архитектура Runtime Pipeline + +## Основной принцип + +Build 060.20 не изменяет существующий Pipeline получения сделок. + +Изменяется исключительно способ получения Runtime-компонентов. + +До настоящего Build Runtime-компоненты создавались непосредственно внутри Recovery. + +После завершения Build Recovery использует уже существующий Trade Runtime. + +Таким образом Runtime становится независимой инфраструктурной подсистемой, существующей до начала любой операции Acquisition. + +--- + +# Recovery Pipeline + +После завершения Build выполнение Recovery выглядит следующим образом. + +```text + Composition Root + + │ + + ▼ + + TradeRuntimeRegistry + + │ + + ▼ + + TradeRecoveryController + + │ + + ▼ + + TradeDocumentSource + + │ + + ▼ + + TradeDocumentHandler + + │ + + ▼ + + Trade + + │ + + ▼ + + TradeStreamConsistencyController + + │ + + ▼ + + Canonical Trade Stream +``` + +Runtime существует независимо от данного Pipeline. + +Pipeline использует Runtime, + +но не определяет его жизненный цикл. + +--- + +# Integration Boundary + +Build 060.20 вводит официальную границу интеграции Runtime. + +До настоящего Build существовала следующая схема. + +```text +Recovery + +↓ + +TradeStreamConsistencyController +``` + +После завершения Build архитектура становится следующей. + +```text +Recovery Module + +↓ + +Trade Runtime + +↓ + +TradeStreamConsistencyController +``` + +Recovery больше не знает: + +- где создаётся Controller; +- сколько экземпляров существует; +- каким образом обеспечивается повторное использование. + +Recovery использует исключительно публичный контракт Runtime. + +--- + +# Dependency Injection + +Ни один Runtime-модуль не создаёт собственные зависимости. + +Все Runtime-компоненты создаются вне Runtime. + +Концептуально архитектура выглядит следующим образом. + +```text +Composition Root + + │ + + ▼ + +TradeRuntimeRegistry + + │ + + ├──────────────┐ + + ▼ ▼ + +TradeRecoveryController + +TradeStreamConsistencyController +``` + +Таким образом: + +- Composition знает способ создания компонентов; +- Registry знает способ предоставления компонентов; +- Runtime-модули знают только собственные публичные зависимости. + +--- + +# Последовательность выполнения Recovery + +После определения архитектуры Runtime необходимо определить официальный алгоритм выполнения Recovery. + +Recovery рассматривается как последовательность операций над уже существующим Runtime. + +--- + +## Шаг 1 + +Получение Runtime-компонентов. + +```text +TradeRuntimeRegistry + +↓ + +TradeRecoveryController + +↓ + +TradeStreamConsistencyController +``` + +Recovery получает уже зарегистрированные Runtime-компоненты. + +Если Runtime отсутствует, + +Recovery не начинается. + +Создание Runtime не относится к ответственности Recovery. + +--- + +## Шаг 2 + +Получение транспортных документов. + +```text +TradeDocumentSource + +↓ + +TradeDocumentHandler +``` + +Все транспортные документы преобразуются в каноническую модель: + +```text +Trade +``` + +--- + +## Шаг 3 + +Передача сделок в Stream Consistency. + +Каждая полученная сделка передаётся существующему экземпляру: + +```text +TradeStreamConsistencyController +``` + +Именно этот Runtime-модуль принимает решение: + +- принять сделку; +- отбросить повтор; +- определить нарушение порядка; +- зафиксировать Gap. + +--- + +## Шаг 4 + +Обновление состояния Runtime. + +После успешной обработки внутреннее состояние Runtime обновляется. + +Изменение состояния производится исключительно средствами: + +```text +TradeStreamConsistencyController +``` + +TradeRuntimeRegistry участия в изменении состояния не принимает. + +Recovery также не изменяет состояние напрямую. + +--- + +## Шаг 5 + +Возврат результата. + +После завершения обработки Recovery возвращает результат вызывающей стороне. + +Trade Runtime продолжает существовать. + +Никакие Runtime-компоненты после завершения Recovery не уничтожаются. + +--- + +# Почему Runtime существует раньше Recovery + +Во время проектирования рассматривалась альтернативная архитектура. + +```text +Recovery + +↓ + +Создать Runtime + +↓ + +Выполнить Recovery +``` + +Данная модель была отклонена. + +Причины: + +- Recovery начинает управлять Runtime; +- Runtime становится зависимым от Recovery; +- невозможно совместное использование Runtime другими модулями; +- WebSocket впоследствии потребует собственный Runtime; +- появляются различные механизмы создания Runtime. + +Такая архитектура нарушает принцип единственного владельца жизненного цикла. + +--- + +# Официальная последовательность + +Build закрепляет следующий порядок выполнения. + +```text +Получить Runtime + +↓ + +Получить документы + +↓ + +Преобразовать документы + +↓ + +Передать сделки в Stream Consistency + +↓ + +Обновить Runtime State + +↓ + +Вернуть результат +``` + +Изменение данной последовательности запрещается. + +--- + +# Атомарность Runtime + +Любая ошибка Recovery не должна нарушать внутреннюю согласованность Runtime. + +Если Recovery завершается ошибкой, + +Trade Runtime обязан оставаться в корректном состоянии. + +Это означает: + +- Registry продолжает существовать; +- Runtime-модули продолжают существовать; +- внутреннее состояние изменяется только успешно обработанными сделками; +- незавершённая Recovery не разрушает Runtime. + +--- + +# Архитектурная диаграмма + +Полная архитектура подсистемы после завершения Build выглядит следующим образом. + +```text +──────────────────────────────────────────────────────── + + Trade Runtime Layer + +──────────────────────────────────────────────────────── + + TradeRuntimeRegistry + + │ + + ┌───────────────┴───────────────┐ + + ▼ ▼ + +TradeStreamConsistencyController TradeRecoveryController + +──────────────────────────────────────────────────────── + + Acquisition Layer + +──────────────────────────────────────────────────────── + +TradeDocumentSource + + │ + + ▼ + +TradeDocumentHandler + + │ + + ▼ + +Trade + +──────────────────────────────────────────────────────── + + Domain Layer + +──────────────────────────────────────────────────────── + +Canonical Trade Stream +``` + +Trade Runtime становится постоянной инфраструктурной подсистемой. + +Acquisition получает доступ к Runtime исключительно через Registry. + +--- + +# Интеграция с Acquisition + +Build 060.20 не изменяет существующий Acquisition Pipeline. + +Он изменяет только модель владения Runtime. + +До настоящего Build существовала следующая схема. + +```text +Recovery + +↓ + +Consistency +``` + +После завершения Build. + +```text +Trade Runtime + +│ + +├── Stream Consistency + +└── Recovery + +↓ + +Acquisition +``` + +Никакой существующий алгоритм обработки сделок не изменяется. + +Изменяется исключительно модель существования Runtime-компонентов. + +--- + +# Расширяемость Runtime + +Одной из целей новой архитектуры является возможность постепенного расширения Runtime без изменения уже существующей структуры. + +Например: + +```text +Trade Runtime + +│ + +├── Stream Consistency Module + +├── Recovery Module + +├── Gap Detection Module + +├── Replay Module + +├── Stream Monitoring Module + +└── Runtime Scheduler Module +``` + +Добавление нового Runtime-модуля не требует: + +- создания нового Registry; +- изменения существующих Runtime-модулей; +- изменения Acquisition Pipeline. + +Достаточно зарегистрировать новый Runtime-компонент в существующем `TradeRuntimeRegistry`. + +Именно эта модель рассматривается как целевая архитектура дальнейшего развития Runtime Layer. + +--- + +# План изменений файлов + +Build 060.20 вводит первую инфраструктурную подсистему Trade Runtime. + +Предлагаемая структура. + +```text +market_data/acquisition/ + +└── runtime/ + + └── trade/ + + ├── __init__.py + + ├── trade_runtime_registry.py + + ├── trade_runtime_protocol.py + + ├── trade_runtime_exceptions.py +``` + +Build не изменяет структуру существующих компонентов Acquisition. + +Trade Runtime добавляется как самостоятельный инфраструктурный слой. + +--- + +# trade_runtime_registry.py + +Содержит: + +```text +TradeRuntimeRegistry +``` + +Отвечает исключительно за: + +- регистрацию Runtime-компонентов; +- получение Runtime-компонентов; +- повторное использование Runtime-компонентов. + +Registry не содержит бизнес-логики. + +--- + +# trade_runtime_protocol.py + +Содержит публичный контракт Runtime. + +Все внешние компоненты должны зависеть исключительно от Protocol. + +Конкретная реализация Registry остаётся внутренней деталью Runtime. + +--- + +# trade_runtime_exceptions.py + +Содержит инфраструктурные исключения Runtime. + +Например: + +- RuntimeNotRegisteredError; +- RuntimeAlreadyRegisteredError; +- InvalidRuntimeComponentError. + +Build не вводит новые исключения Recovery Pipeline. + +Исключения относятся исключительно к инфраструктуре Runtime. + +--- + +# Изменяемые файлы + +Build должен минимально затронуть существующий код. + +Изменения предполагаются только в следующих точках интеграции. + +- Composition Root; +- временная точка композиции (`service.py`); +- Recovery Pipeline. + +Все остальные изменения должны быть локализованы внутри новой подсистемы Trade Runtime. + +--- + +# Architectural Decision Records + +## ADR-060.20-01 — Trade Runtime + +### Решение + +Trade Runtime является самостоятельной инфраструктурной подсистемой. + +Runtime существует независимо от любых Acquisition-операций. + +Recovery является одним из Runtime-модулей. + +--- + +## ADR-060.20-02 — Runtime Lifecycle Ownership + +### Решение + +Жизненным циклом Runtime-компонентов владеет исключительно: + +```text +TradeRuntimeRegistry +``` + +Никакой другой компонент системы не имеет права создавать альтернативные экземпляры Runtime. + +--- + +## ADR-060.20-03 — Business State Ownership + +### Решение + +Жизненный цикл компонентов и бизнес-состояние разделены. + +TradeRuntimeRegistry владеет существованием Runtime-компонентов. + +TradeStreamConsistencyController остаётся единственным владельцем состояния Canonical Trade Stream. + +--- + +## ADR-060.20-04 — Recovery Independence + +### Решение + +TradeRecoveryController никогда самостоятельно не создаёт Runtime. + +Recovery получает Runtime исключительно через публичный контракт Registry. + +--- + +## ADR-060.20-05 — Runtime Boundary + +### Решение + +Trade Runtime становится официальной инфраструктурной границей между Runtime Layer и Acquisition Layer. + +Все последующие Build обязаны использовать данную модель. + +--- + +## ADR-060.20-06 — Registry Responsibility + +### Решение + +TradeRuntimeRegistry является исключительно инфраструктурным каталогом Runtime-компонентов. + +Registry: + +- не содержит бизнес-логики; +- не выполняет Recovery; +- не управляет состоянием Canonical Trade Stream; +- не знает внутреннего устройства Runtime-модулей. + +--- + +## ADR-060.20-07 — Runtime Extensibility + +### Решение + +Trade Runtime должен расширяться посредством регистрации новых Runtime-модулей. + +Создание отдельных специализированных Registry запрещается. + +Все Runtime-компоненты регистрируются внутри единого: + +```text +TradeRuntimeRegistry +``` + +--- + +# Стратегия тестирования + +Build 060.20 вводит первую инфраструктурную подсистему Runtime. + +Поэтому объектом тестирования становится не только Registry как класс. + +Необходимо подтвердить корректность всей архитектурной модели Runtime. + +--- + +# Основные принципы тестирования + +## Проверяются архитектурные инварианты + +Главным объектом тестирования являются инварианты Trade Runtime. + +Проверяется не реализация конкретного метода, + +а соблюдение архитектурных правил. + +--- + +## Проверяются публичные контракты + +Тесты взаимодействуют исключительно через публичный Runtime Protocol. + +Внутренняя реализация Registry не должна влиять на тесты. + +--- + +## Один тест — один инвариант + +Каждый негативный сценарий проверяет нарушение только одного архитектурного правила. + +Это обеспечивает понятную диагностику ошибок. + +--- + +## Детерминированность + +Все тесты должны быть полностью воспроизводимыми. + +Никакие случайные значения, + +внешние сервисы, + +или сетевые события + +не должны влиять на результат. + +--- + +# Уровни тестирования + +Build вводит три уровня проверки. + +--- + +## Unit Tests + +Изолированно проверяются: + +- TradeRuntimeRegistry; +- Runtime Protocol. + +Все зависимости заменяются тестовыми объектами. + +--- + +## Integration Tests + +Проверяется полный путь получения Runtime. + +```text +Composition Root + +↓ + +TradeRuntimeRegistry + +↓ + +TradeRecoveryController + +↓ + +TradeStreamConsistencyController +``` + +Основная задача — + +доказать, + +что Runtime корректно интегрирован в существующую архитектуру. + +--- + +## Regression Tests + +Подтверждается, + +что Build 060.20 + +не изменил поведение предыдущих Build. + +В частности: + +- Feed; +- Handler; +- Stream Consistency; +- Recovery Pipeline. + +--- + +# Матрица тестирования + +## 1. Регистрация Runtime-компонента + +Проверяется успешная регистрация Runtime-модуля. + +Ожидаемый результат: + +зарегистрированный компонент становится доступным через Registry. + +--- + +## 2. Повторное получение Runtime + +Проверяется повторный запрос зарегистрированного компонента. + +Ожидаемый результат: + +возвращается тот же экземпляр. + +--- + +## 3. Несколько Runtime-модулей + +Проверяется регистрация нескольких независимых Runtime-компонентов. + +Ожидаемый результат: + +каждый Runtime-модуль доступен независимо. + +--- + +## 4. Повторное использование Runtime + +Несколько Recovery используют один и тот же Runtime. + +Ожидаемый результат: + +новые экземпляры не создаются. + +--- + +## 5. Сохранение состояния + +Несколько последовательных Recovery. + +Ожидаемый результат: + +TradeStreamConsistencyController сохраняет своё состояние. + +--- + +## 6. Ошибка отсутствующего Runtime + +Попытка получения незарегистрированного Runtime. + +Ожидаемый результат: + +генерируется инфраструктурное исключение Runtime. + +--- + +## 7. Независимость Recovery + +Recovery не создаёт Runtime самостоятельно. + +Ожидаемый результат: + +используются только зарегистрированные Runtime-компоненты. + +--- + +## 8. Совместимость + +Проверяется существующий Acquisition Pipeline. + +Ожидаемый результат: + +поведение системы не изменилось. + +--- + +# Матрица архитектурных инвариантов + +| Инвариант | Проверка | +|-----------|----------| +| Runtime Lifetime | Unit | +| Runtime Ownership | Unit | +| Business State Ownership | Unit | +| Registry Responsibility | Unit | +| Runtime Boundary | Integration | +| Runtime Reuse | Integration | +| Stateless Acquisition | Regression | +| Recovery Independence | Regression | +| Existing Feed Behaviour | Regression | + +--- + +# Definition of Done + +Build считается завершённым только после выполнения всех перечисленных условий. + +--- + +## Архитектура + +- Все ADR реализованы без отклонений. +- Runtime Layer полностью соответствует настоящей спецификации. +- Registry остаётся инфраструктурным компонентом. +- Runtime отделён от Acquisition. +- Runtime допускает дальнейшее расширение новыми модулями. + +--- + +## Код + +- Не изменена бизнес-логика Recovery. +- Не изменена бизнес-логика Stream Consistency. +- Не изменён Acquisition Pipeline. +- Не нарушены существующие публичные API. + +--- + +## Функциональность + +Поддерживаются: + +- единый Trade Runtime; +- единый экземпляр Runtime-компонентов; +- повторное использование Runtime; +- сохранение состояния между Recovery; +- независимость Runtime от Acquisition; +- готовность к регистрации новых Runtime-модулей. + +--- + +## Тестирование + +Все Unit Tests проходят. + +Все Integration Tests проходят. + +Все Regression Tests проходят без изменений. + +--- + +## Документация + +Обновлены: + +- Architecture Specification; +- ADR; +- Architecture Diagram; +- File Plan. + +--- + +# План реализации + +Настоящий Build должен реализовываться строго поэтапно. + +Переход к следующему этапу допускается только после полного завершения предыдущего. + +--- + +## Этап 1 — Создание Runtime Layer + +Создаётся новая инфраструктурная подсистема: + +```text +market_data/acquisition/ +└── runtime/ + └── trade/ +``` + +Добавляются: + +- `trade_runtime_registry.py`; +- `trade_runtime_protocol.py`; +- `trade_runtime_exceptions.py`. + +На данном этапе существующая бизнес-логика системы не изменяется. + +--- + +## Этап 2 — Реализация TradeRuntimeRegistry + +Реализуется инфраструктурный компонент: + +```text +TradeRuntimeRegistry +``` + +Registry должен обеспечивать: + +- регистрацию Runtime-компонентов; +- получение Runtime-компонентов; +- повторное использование зарегистрированных экземпляров; +- инфраструктурные проверки корректности регистрации. + +Никакие бизнес-алгоритмы на данном этапе не изменяются. + +--- + +## Этап 3 — Интеграция Stream Consistency + +Существующий: + +```text +TradeStreamConsistencyController +``` + +становится Runtime-компонентом. + +Изменяется только способ его получения. + +Алгоритмы: + +- дедупликации; +- проверки последовательности; +- сопровождения Canonical Trade Stream + +остаются полностью неизменными. + +--- + +## Этап 4 — Интеграция Recovery + +Существующий: + +```text +TradeRecoveryController +``` + +переводится на использование Runtime. + +Recovery получает Runtime-компоненты исключительно через публичный Runtime Protocol. + +Создание Runtime внутри Recovery полностью исключается. + +--- + +## Этап 5 — Интеграция Composition Root + +Текущая временная композиция (`service.py`) обновляется таким образом, чтобы: + +- создать Runtime-компоненты; +- зарегистрировать их в `TradeRuntimeRegistry`; +- передать Registry остальным компонентам системы. + +В дальнейшем данная логика будет перенесена в постоянный Composition Root. + +--- + +## Этап 6 — Архитектурная проверка + +После завершения интеграции необходимо подтвердить выполнение всех архитектурных инвариантов Build. + +Проверяется: + +- независимость Runtime; +- единственность Runtime-компонентов; +- отсутствие изменений бизнес-логики; +- корректность повторного использования Runtime; +- отсутствие прямого создания Runtime внутри Recovery. + +--- + +# Build Boundary + +Настоящий Build имеет строго ограниченную область ответственности. + +--- + +## Build отвечает за + +- построение архитектуры Trade Runtime; +- введение Runtime Layer; +- введение TradeRuntimeRegistry; +- определение Runtime Protocol; +- разделение жизненного цикла и бизнес-состояния; +- определение архитектурных инвариантов; +- подготовку Runtime к дальнейшему расширению. + +--- + +## Build сознательно НЕ отвечает за + +- изменение алгоритмов Recovery; +- изменение алгоритмов Stream Consistency; +- реализацию WebSocket Feed; +- реализацию Gap Detection; +- реализацию Runtime Scheduler; +- реализацию Monitoring; +- реализацию Runtime Metrics; +- реализацию Runtime Health Check; +- реализацию Runtime Persistence; +- построение окончательного Composition Root. + +Все перечисленные задачи относятся к последующим Build серии 060. + +--- + +# Архитектурный результат + +После завершения Build система впервые получает полноценную архитектуру Trade Runtime как самостоятельного инфраструктурного слоя. + +До Build 060.20: + +```text +Recovery + +↓ + +TradeStreamConsistencyController +``` + +После Build 060.20: + +```text + Trade Runtime + + │ + + ┌───────────────┴───────────────┐ + + ▼ ▼ + +TradeRuntimeRegistry + + │ + + ├──────────────────────────┐ + + ▼ ▼ + +TradeStreamConsistencyController + +TradeRecoveryController + +──────────────────────────────────────── + + Acquisition + +──────────────────────────────────────── + +REST + +WebSocket + +Replay + +Future Sources + +──────────────────────────────────────── + + Canonical Trade Stream +``` + +Trade Runtime становится самостоятельным инфраструктурным слоем системы. + +Recovery становится Runtime-модулем. + +Stream Consistency становится Runtime-модулем. + +Registry становится единственным владельцем жизненного цикла Runtime-компонентов. + +--- + +# Архитектурные последствия + +После завершения Build становятся возможны последующие этапы развития без изменения базовой архитектуры. + +Например: + +```text +Trade Runtime + +│ + +├── Infrastructure +│ └── TradeRuntimeRegistry + +├── Stream Consistency Module +│ └── TradeStreamConsistencyController + +├── Recovery Module +│ └── TradeRecoveryController + +├── Gap Detection Module + +├── Stream Monitoring Module + +├── Replay Module + +├── Runtime Scheduler Module + +├── Runtime Metrics Module + +└── ... +``` + +Добавление нового Runtime-модуля требует только: + +1. создания нового компонента; +2. регистрации его в `TradeRuntimeRegistry`; +3. использования публичного Runtime Protocol. + +Существующая архитектура при этом не изменяется. + +--- + +# Заключение + +Build 060.20 впервые формализует Trade Runtime как самостоятельную архитектурную подсистему Dzentra. + +TradeRuntimeRegistry является инфраструктурным ядром этой подсистемы, но не исчерпывает её. Runtime включает также Runtime-модули, архитектурные инварианты, модель владения и правила расширения, определённые настоящей спецификацией. + +В результате выполнения Build достигаются следующие цели: + +- Runtime становится самостоятельной инфраструктурной подсистемой; +- жизненный цикл Runtime-компонентов отделяется от бизнес-состояния; +- Recovery перестаёт быть владельцем Runtime; +- Stream Consistency сохраняет полное владение состоянием Canonical Trade Stream; +- все Runtime-компоненты получают единый механизм регистрации и повторного использования; +- Runtime становится независимым от конкретного транспорта получения данных. + +Данная архитектура является базовой для всех последующих Build серии 060 и определяет единый подход к построению долгоживущих Runtime-подсистем Dzentra. + +Любые последующие расширения Runtime должны соответствовать архитектурным принципам, инвариантам и ADR, закреплённым настоящей спецификацией. \ No newline at end of file