70 KiB
Build 060.20 — Trade Runtime Architecture
Статус: Accepted
Тип документа: Architecture Specification
Build: 060.20
Ветка: Trades Feed (Time & Sales)
Документ: build_060_20_architecture.md
Предыдущие рабочие названия: Trade Recovery Registry, Trade Runtime Registry
Связанные документы:
Назначение документа
Настоящий документ является официальной архитектурной спецификацией 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.
Все решения, зафиксированные настоящей спецификацией, считаются утверждёнными до начала реализации.
Изменение этих решений во время разработки допускается только после:
- остановки реализации;
- повторного архитектурного анализа;
- подготовки отдельного ADR;
- обновления настоящего документа.
Статус Build
Build 060.20 является непосредственным продолжением серии Build 060, посвящённой построению подсистемы получения и сопровождения потока сделок:
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
Введена единая каноническая модель:
Trade
Все источники сделок обязаны приводить входные данные к одной Domain Model.
Источник данных не должен влиять на последующую обработку сделки.
Build 060.17
Построен Trades Feed.
Получение сделок отделено от конкретной транспортной реализации.
Feed отвечает за получение и публикацию канонических сделок, но не является владельцем долгоживущего состояния потока.
Build 060.18
Введён:
TradeStreamConsistencyController
Он стал первым явно stateful-компонентом новой подсистемы Trades Feed.
Контроллер определён как единственный владелец бизнес-состояния Canonical Trade Stream.
Он отвечает за:
- порядок сделок;
- дедупликацию;
- обнаружение конфликтующих повторов;
- обнаружение разрывов;
- сохранение текущего состояния потока.
Build 060.18 определил владельца бизнес-состояния, но сознательно не определил владельца жизненного цикла самого Controller.
Build 060.19
Введён:
TradeRecoveryController
Он отвечает за выполнение Recovery Pipeline и восстановление отсутствующих участков потока.
Recovery использует существующий TradeStreamConsistencyController, но не должен владеть его состоянием или жизненным циклом.
Build 060.19 определил операцию Recovery, но сознательно не формализовал Runtime, внутри которого эта операция выполняется.
Архитектурная проблема
Предыдущая архитектура Market Data Acquisition основана преимущественно на короткоживущих stateless-компонентах.
Типичный Acquisition Pipeline выглядит следующим образом.
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
Первоначально проблема рассматривалась как необходимость создания:
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 вводит новый официальный архитектурный уровень:
Trade Runtime
Trade Runtime существует независимо от:
- отдельных REST-запросов;
- отдельных Recovery-вызовов;
- Feed-вызовов;
- транспортного источника;
- конкретной операции Acquisition.
Trade Runtime объединяет долгоживущие компоненты, необходимые для сопровождения единого Canonical Trade Stream.
Разделение жизненных циклов
Build формально разделяет два независимых жизненных цикла.
Acquisition Lifetime
создание операции
↓
получение данных
↓
преобразование данных
↓
публикация результата
↓
завершение операции
Acquisition-компоненты могут быть короткоживущими.
Они не должны владеть состоянием непрерывного Trade Stream.
Trade Runtime Lifetime
создание 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 включает два существующих функциональных модуля.
Trade Runtime
│
├── Stream Consistency Module
│ └── TradeStreamConsistencyController
│
└── Recovery Module
└── TradeRecoveryController
Build не переносит бизнес-логику этих компонентов.
Build изменяет только модель их создания, хранения, получения и повторного использования.
Stream Consistency Module
Stream Consistency Module отвечает за согласованность Canonical Trade Stream.
Его основным компонентом является:
TradeStreamConsistencyController
Он остаётся единственным владельцем бизнес-состояния потока.
Build 060.20 не изменяет его бизнес-ответственность.
Recovery Module
Recovery Module отвечает за восстановление отсутствующих участков Trade Stream.
Его основным компонентом является:
TradeRecoveryController
Recovery является одним из модулей Trade Runtime.
Он не является:
- самим Runtime;
- владельцем Runtime;
- владельцем состояния потока;
- владельцем жизненного цикла Consistency Controller.
Возможное дальнейшее расширение
В последующих Build Trade Runtime может быть расширен новыми модулями.
Trade Runtime
│
├── Stream Consistency Module
├── Recovery Module
├── Gap Detection Module
├── Recovery Scheduling Module
├── Stream Monitoring Module
└── другие Runtime-модули
Build 060.20 не реализует эти будущие модули.
Их перечисление определяет только допустимое направление архитектурного расширения.
TradeRuntimeRegistry
Для управления доступом к долгоживущим компонентам Build вводит:
TradeRuntimeRegistry
TradeRuntimeRegistry является инфраструктурным реестром компонентов Trade Runtime.
Он становится единой точкой доступа к зарегистрированным Runtime-компонентам.
Концептуально:
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 запрещено существование нескольких файлов с одинаковыми именами независимо от каталогов.
Единственным исключением остаётся:
__init__.py
Каждый Runtime-компонент получает уникальное имя файла.
Например:
trade_runtime_registry.py
trade_runtime_protocol.py
trade_runtime_exceptions.py
Модель владения
Одной из основных целей Build является разделение различных видов ответственности.
До настоящего Build существовало только понятие владельца состояния.
Build 060.20 впервые разделяет два независимых уровня владения.
Владение жизненным циклом
За существование Runtime-компонентов отвечает исключительно:
TradeRuntimeRegistry
Он определяет:
- создание компонентов;
- регистрацию;
- хранение;
- предоставление;
- повторное использование.
Registry никогда не изменяет внутреннее состояние компонентов.
Владение бизнес-состоянием
Каждый Runtime-модуль самостоятельно владеет собственным состоянием.
Например:
TradeStreamConsistencyController
остаётся владельцем:
- порядка сделок;
- дедупликации;
- обнаружения конфликтующих повторов;
- информации о состоянии Canonical Trade Stream.
Registry не имеет доступа к этому состоянию.
Владение выполнением операций
За выполнение бизнес-операций отвечает соответствующий Runtime-модуль.
Например:
TradeRecoveryController
отвечает исключительно за выполнение Recovery Pipeline.
Recovery не отвечает:
- за создание Runtime;
- за регистрацию Runtime;
- за получение Runtime;
- за уничтожение Runtime.
Архитектурная модель Runtime
После завершения Build Runtime приобретает следующую структуру.
Trade Runtime
│
├── Stream Consistency Module
│ │
│ ▼
│ TradeStreamConsistencyController
│
└── Recovery Module
│
▼
TradeRecoveryController
Каждый модуль обладает собственной ответственностью.
Ни один модуль не становится владельцем другого.
Runtime лишь объединяет их в одну долгоживущую инфраструктурную подсистему.
TradeRuntimeRegistry
TradeRuntimeRegistry является владельцем Runtime Layer.
Концептуально Runtime выглядит следующим образом.
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.
REST
↓
Recovery
↓
Consistency
После завершения Build появляется самостоятельный Runtime Layer.
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 состоит из трёх независимых компонентов.
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.
Его центральным компонентом является:
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.
Его центральным компонентом является:
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-модули взаимодействуют исключительно через публичные контракты.
Концептуально схема выглядит следующим образом.
Trade Runtime
│
▼
TradeRuntimeRegistry
┌─────────┴─────────┐
▼ ▼
TradeStreamConsistencyController TradeRecoveryController
▲ │
└───────────────────┘
Recovery использует
Stream Consistency
Из данной схемы следует:
- Registry ничего не знает о внутреннем устройстве Recovery;
- Registry ничего не знает о внутреннем устройстве Stream Consistency;
- Recovery знает только публичный контракт Stream Consistency;
- Stream Consistency ничего не знает о Recovery.
Таким образом Runtime состоит из независимых модулей с минимально необходимыми зависимостями.
Главная архитектурная граница
После завершения Build официальная архитектурная модель становится следующей.
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 выглядит следующим образом.
Composition Root
│
▼
TradeRuntimeRegistry
│
▼
TradeRecoveryController
│
▼
TradeDocumentSource
│
▼
TradeDocumentHandler
│
▼
Trade
│
▼
TradeStreamConsistencyController
│
▼
Canonical Trade Stream
Runtime существует независимо от данного Pipeline.
Pipeline использует Runtime,
но не определяет его жизненный цикл.
Integration Boundary
Build 060.20 вводит официальную границу интеграции Runtime.
До настоящего Build существовала следующая схема.
Recovery
↓
TradeStreamConsistencyController
После завершения Build архитектура становится следующей.
Recovery Module
↓
Trade Runtime
↓
TradeStreamConsistencyController
Recovery больше не знает:
- где создаётся Controller;
- сколько экземпляров существует;
- каким образом обеспечивается повторное использование.
Recovery использует исключительно публичный контракт Runtime.
Dependency Injection
Ни один Runtime-модуль не создаёт собственные зависимости.
Все Runtime-компоненты создаются вне Runtime.
Концептуально архитектура выглядит следующим образом.
Composition Root
│
▼
TradeRuntimeRegistry
│
├──────────────┐
▼ ▼
TradeRecoveryController
TradeStreamConsistencyController
Таким образом:
- Composition знает способ создания компонентов;
- Registry знает способ предоставления компонентов;
- Runtime-модули знают только собственные публичные зависимости.
Последовательность выполнения Recovery
После определения архитектуры Runtime необходимо определить официальный алгоритм выполнения Recovery.
Recovery рассматривается как последовательность операций над уже существующим Runtime.
Шаг 1
Получение Runtime-компонентов.
TradeRuntimeRegistry
↓
TradeRecoveryController
↓
TradeStreamConsistencyController
Recovery получает уже зарегистрированные Runtime-компоненты.
Если Runtime отсутствует,
Recovery не начинается.
Создание Runtime не относится к ответственности Recovery.
Шаг 2
Получение транспортных документов.
TradeDocumentSource
↓
TradeDocumentHandler
Все транспортные документы преобразуются в каноническую модель:
Trade
Шаг 3
Передача сделок в Stream Consistency.
Каждая полученная сделка передаётся существующему экземпляру:
TradeStreamConsistencyController
Именно этот Runtime-модуль принимает решение:
- принять сделку;
- отбросить повтор;
- определить нарушение порядка;
- зафиксировать Gap.
Шаг 4
Обновление состояния Runtime.
После успешной обработки внутреннее состояние Runtime обновляется.
Изменение состояния производится исключительно средствами:
TradeStreamConsistencyController
TradeRuntimeRegistry участия в изменении состояния не принимает.
Recovery также не изменяет состояние напрямую.
Шаг 5
Возврат результата.
После завершения обработки Recovery возвращает результат вызывающей стороне.
Trade Runtime продолжает существовать.
Никакие Runtime-компоненты после завершения Recovery не уничтожаются.
Почему Runtime существует раньше Recovery
Во время проектирования рассматривалась альтернативная архитектура.
Recovery
↓
Создать Runtime
↓
Выполнить Recovery
Данная модель была отклонена.
Причины:
- Recovery начинает управлять Runtime;
- Runtime становится зависимым от Recovery;
- невозможно совместное использование Runtime другими модулями;
- WebSocket впоследствии потребует собственный Runtime;
- появляются различные механизмы создания Runtime.
Такая архитектура нарушает принцип единственного владельца жизненного цикла.
Официальная последовательность
Build закрепляет следующий порядок выполнения.
Получить Runtime
↓
Получить документы
↓
Преобразовать документы
↓
Передать сделки в Stream Consistency
↓
Обновить Runtime State
↓
Вернуть результат
Изменение данной последовательности запрещается.
Атомарность Runtime
Любая ошибка Recovery не должна нарушать внутреннюю согласованность Runtime.
Если Recovery завершается ошибкой,
Trade Runtime обязан оставаться в корректном состоянии.
Это означает:
- Registry продолжает существовать;
- Runtime-модули продолжают существовать;
- внутреннее состояние изменяется только успешно обработанными сделками;
- незавершённая Recovery не разрушает Runtime.
Архитектурная диаграмма
Полная архитектура подсистемы после завершения Build выглядит следующим образом.
────────────────────────────────────────────────────────
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 существовала следующая схема.
Recovery
↓
Consistency
После завершения Build.
Trade Runtime
│
├── Stream Consistency
└── Recovery
↓
Acquisition
Никакой существующий алгоритм обработки сделок не изменяется.
Изменяется исключительно модель существования Runtime-компонентов.
Расширяемость Runtime
Одной из целей новой архитектуры является возможность постепенного расширения Runtime без изменения уже существующей структуры.
Например:
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.
Предлагаемая структура.
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
Содержит:
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-компонентов владеет исключительно:
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-компоненты регистрируются внутри единого:
TradeRuntimeRegistry
Стратегия тестирования
Build 060.20 вводит первую инфраструктурную подсистему Runtime.
Поэтому объектом тестирования становится не только Registry как класс.
Необходимо подтвердить корректность всей архитектурной модели Runtime.
Основные принципы тестирования
Проверяются архитектурные инварианты
Главным объектом тестирования являются инварианты Trade Runtime.
Проверяется не реализация конкретного метода,
а соблюдение архитектурных правил.
Проверяются публичные контракты
Тесты взаимодействуют исключительно через публичный Runtime Protocol.
Внутренняя реализация Registry не должна влиять на тесты.
Один тест — один инвариант
Каждый негативный сценарий проверяет нарушение только одного архитектурного правила.
Это обеспечивает понятную диагностику ошибок.
Детерминированность
Все тесты должны быть полностью воспроизводимыми.
Никакие случайные значения,
внешние сервисы,
или сетевые события
не должны влиять на результат.
Уровни тестирования
Build вводит три уровня проверки.
Unit Tests
Изолированно проверяются:
- TradeRuntimeRegistry;
- Runtime Protocol.
Все зависимости заменяются тестовыми объектами.
Integration Tests
Проверяется полный путь получения Runtime.
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
Создаётся новая инфраструктурная подсистема:
market_data/acquisition/
└── runtime/
└── trade/
Добавляются:
trade_runtime_registry.py;trade_runtime_protocol.py;trade_runtime_exceptions.py.
На данном этапе существующая бизнес-логика системы не изменяется.
Этап 2 — Реализация TradeRuntimeRegistry
Реализуется инфраструктурный компонент:
TradeRuntimeRegistry
Registry должен обеспечивать:
- регистрацию Runtime-компонентов;
- получение Runtime-компонентов;
- повторное использование зарегистрированных экземпляров;
- инфраструктурные проверки корректности регистрации.
Никакие бизнес-алгоритмы на данном этапе не изменяются.
Этап 3 — Интеграция Stream Consistency
Существующий:
TradeStreamConsistencyController
становится Runtime-компонентом.
Изменяется только способ его получения.
Алгоритмы:
- дедупликации;
- проверки последовательности;
- сопровождения Canonical Trade Stream
остаются полностью неизменными.
Этап 4 — Интеграция Recovery
Существующий:
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:
Recovery
↓
TradeStreamConsistencyController
После Build 060.20:
Trade Runtime
│
┌───────────────┴───────────────┐
▼ ▼
TradeRuntimeRegistry
│
├──────────────────────────┐
▼ ▼
TradeStreamConsistencyController
TradeRecoveryController
────────────────────────────────────────
Acquisition
────────────────────────────────────────
REST
WebSocket
Replay
Future Sources
────────────────────────────────────────
Canonical Trade Stream
Trade Runtime становится самостоятельным инфраструктурным слоем системы.
Recovery становится Runtime-модулем.
Stream Consistency становится Runtime-модулем.
Registry становится единственным владельцем жизненного цикла Runtime-компонентов.
Архитектурные последствия
После завершения Build становятся возможны последующие этапы развития без изменения базовой архитектуры.
Например:
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-модуля требует только:
- создания нового компонента;
- регистрации его в
TradeRuntimeRegistry; - использования публичного 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, закреплённым настоящей спецификацией.