воскресенье, 24 мая 2026 г.

EventHub, WebAppEventHub02, MicroProcessArch, BusLibrary

- взаимодействие BackGroundServices через GS.EventHub02

- EventHub: GS.EventHub02

EventHub, WebAppEventHub02, MicroProcessArch, BusLibrary

--------------------------------------------------------------------------

EventHub Old version

D:\Projects\VS\GS.EvenHub\GS.EvenHub.slnx

D:\Projects\VS\GS.EvenHub\GS.EventHub02\GS.EventHub02.csproj

-----------------------------------------------------------------------------------------------

Interaction beatween BackgroundServices with GS.EventHub02

D:\Projects\VS\GS.EvenHub\GS.EvenHub.slnx

D:\Projects\VS\GS.EvenHub\WebAppEventHub02\WebAppEventHub02.csproj

----------------------------------------------------------------------- --------------------

EventHub New version 26.05.24 

D:\Projects\VS\2605\Bus\BusLibrary\BusLibrary.slnx

EventHub New version 26.05.24

D:\Projects\VS\2605\Bus\BusLibrary\BusLibrary03\BusLibrary03.csproj

D:\Projects\VS\2605\Bus\BusLibrary\BusLibrary.Test02\BusLibrary.Test02.csproj

EventHub Old version EventHub

D:\Projects\VS\2605\Bus\BusLibrary02.Core\EventHub New version.csproj


Subscribes, Handlers

Статические подписки (атрибутные)

Это обработчики, помеченные атрибутом [Handles("key")] и зарегистрированные в DI через сканирование сборок.

✅ Когда использовать

  • Известный набор событий на этапе компиляции
    Все ключи и обработчики определены заранее, их количество не меняется во время работы приложения.

  • Жёсткая типизация и проверка на этапе сборки
    Если обработчик забыт или неправильно указан ключ, компилятор не заметит, но тесты или запуск сразу покажут отсутствие подписчика.

  • Инфраструктурные или системные события
    Например, события жизненного цикла приложения (ApplicationStarted, RequestHandled), логирование, аудит. Это вещи, которые всегда должны обрабатываться и не зависят от пользовательского ввода.

  • Модульная архитектура с явными зависимостями
    Каждый модуль (IEventHubModule) может объявить свои ассембли с обработчиками. Статические обработчики чётко показывают, какие события модуль обрабатывает.

  • Производительность
    Нет накладных расходов на динамическую подписку/отписку в рантайме. Обработчики создаются один раз как singleton.

❌ Когда не стоит

  • События, которые появляются или исчезают в зависимости от состояния приложения или пользовательских действий (например, подписка на уведомления конкретного пользователя).

  • Необходимость подписаться на событие из кода, который не должен зависеть от атрибутов (например, скрипты, плагины).


Динамические подписки (Subscribe)

Обработчики регистрируются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler).

✅ Когда использовать

  • Временные или условные подписки
    Пример: пользователь открыл страницу — подписались на обновления; закрыл — отписались.

  • Подписки с ограниченным временем жизни
    Например, запрос-ответ, ожидание конкретного события в течение таймаута.

  • Расширяемость без перекомпиляции
    Плагины или скрипты, загружаемые динамически, могут подписываться на события, не имея доступа к атрибутам или не имея возможности быть просканированными.

  • Код, где обработчик не может быть оформлен как класс с атрибутом
    Лямбды, методы экземпляра, замыкания. Удобно, когда логика обработки проста и не хочется создавать отдельный класс.

  • Тестирование
    В тестах легко подписать лямбду, собирающую сообщения, и проверить их получение.

❌ Когда не стоит

  • Обработчики, которые должны быть постоянными и предсказуемыми. Динамическая подписка может быть случайно удалена (dispose токена), и событие перестанет обрабатываться.

  • Если нужна строгая типизация и гарантия, что обработчик существует на момент старта приложения.


Сравнительная таблица

КритерийСтатические (атрибуты)Динамические (Subscribe)
Когда создаютсяНа старте приложения (сканирование сборок)В любой момент во время выполнения
Время жизниВсё время работы приложенияМожет быть конечным (через Dispose)
Способ регистрацииАтрибут [Handles] + реализация IMessageHandler<T>Вызов Subscribe<T>(key, handler)
ТипизацияЖёсткая: класс должен реализовать интерфейсГибкая: лямбда с сигнатурой Func<T, CancellationToken, ValueTask>
ПроизводительностьЧуть выше (нет поиска по списку динамических подписок)Незначительные накладные расходы на Dictionary lookup
ВидимостьПолный список можно получить через IEventHandlerRegistryТолько через GetSubscribedKeys()
Использование в тестахТребует настройки DI и сканирования сборокБыстро и изолированно: подписал лямбду и готово
Управление памятьюОбработчики живут пока жив контейнерМожно отписаться, освободив ресурсы

Рекомендация

  • Используйте статические обработчики для основной бизнес-логики и системных событий. Это даёт предсказуемость, строгую структуру и хорошую производительность.

  • Используйте динамические подписки для UI-логики, временных реакций, плагинов и тестов. Они дают гибкость и изоляцию.

  • Режим Both — отличное решение, когда в одном приложении сочетаются оба вида событий. Например, ядро обрабатывает аудит через статику, а клиентский код подписывается динамически на уведомления для конкретного пользователя.

Вы можете комбинировать оба подхода, не боясь конфликтов — они работают независимо и в одной очереди, сохраняя порядок сообщений.

----------------------------------------------------------------------

В вашей шине статические обработчики и динамические подписки — это два разных механизма реагирования на события, которые отличаются способом регистрации, временем жизни, производительностью и сценариями использования.

🔍 Основные различияиизадаются на этапе компиляции: класс помечается атрибутом [Handles("ключ")] и реализует IMessageHandler<T>. Они автоматически находятся при сканировании сборок и регистрируются в DI как singleton.

---------------------------------------

Основные различия

1. Способ регистрации

  • Статические обработчики задаются на этапе компиляции: класс помечается атрибутом [Handles("ключ")] и реализует IMessageHandler<T>. Они автоматически находятся при сканировании сборок и регистрируются в DI как singleton.

  • Динамические подписки создаются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler). Можно подписаться лямбдой, методом, делегатом — не требуется отдельный класс или атрибут.

2. Время жизни

  • Статические существуют всё время работы приложения (пока жив DI-контейнер). Их нельзя убрать выборочно — только если полностью остановить хаб.

  • Динамические могут быть временными: подписка живёт, пока не будет вызван Dispose() возвращаемого токена. Можно подписаться на несколько секунд (например, ожидая ответ) и отписаться.

3. Порядок вызова

В режиме Both сначала всегда вызываются все статические обработчики (в порядке, определённом сканированием), а затем — все динамические (в порядке регистрации). Это происходит синхронно внутри одного цикла обработки сообщения.

4. Гибкость и предсказуемость

  • Статические дают жёсткую структуру: вы точно знаете, какие события кем обрабатываются, всё проверяется при старте. Подходит для обязательной бизнес-логики, аудита, инфраструктурных событий.

  • Динамические дают гибкость: можно подписываться и отписываться в зависимости от состояния приложения, добавлять обработчики без перекомпиляции (плагины, скрипты). Но есть риск случайно отписаться или пропустить событие.

5. Производительность

  • Статические чуть быстрее: реестр EventHandlerRegistry отдаёт готовый список типов, экземпляры обработчиков уже созданы в DI.

  • Динамические требуют поиска по ConcurrentDictionary и, возможно, упаковки делегатов, но накладные расходы минимальны.

6. Тестирование

  • Статические требуют настройки DI и сканирования сборок, но дают полную уверенность в работе всех штатных обработчиков.

  • Динамические позволяют в тесте быстро подписать лямбду, собрать данные и проверить их — меньше инфраструктуры.

📊 Сравнительная таблица

ХарактеристикаСтатические обработчикиДинамические подписки
Когда определяютсяНа этапе компиляции (атрибуты)В рантайме (вызов метода)
Время жизниПостоянное (пока жив DI)Может быть временным (через Dispose)
РегистрацияАвтоматическая (сканирование сборок)Явная (Subscribe<T>())
Тип обработчикаКласс, реализующий IMessageHandler<T>Делегат (лямбда/метод)
ГибкостьНизкая (нельзя убрать один обработчик)Высокая (можно подписать/отписать в любой момент)
ПредсказуемостьВысокая (всё видно при старте)Ниже (легко забыть или случайно отписать)
ПроизводительностьНемного выше (готовый реестр)Незначительно ниже (словарь + делегат)
ИспользованиеБизнес-логика, аудит, обязательные событияUI, плагины, временные реакции, тесты

🤝 Можно ли их совмещать?

Да, в режиме RoutingMode.Both оба типа работают одновременно. Сообщения обрабатываются в одной очереди строго по порядку: сначала отрабатывают статические обработчики, потом динамические. Это позволяет отделить постоянную инфраструктуру от гибкой функциональности без конфликтов.

Таким образом, выбор зависит от того, нужна ли вам неизменная и проверяемая структура (статика) или лёгкость добавления/удаления обработчиков в процессе работы (динамика).

Динамические подписки создаются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler). Можно подписаться лямбдой, методом, делегатом — не требуется отдельный класс или атрибут.

-------

В вашей шине статические обработчики и динамические подписки — это два разных механизма реагирования на события, которые отличаются способом регистрации, временем жизни, производительностью и сценариями использования.


🔍 Основные различия

1. Способ регистрации

Статические обработчики задаются на этапе компиляции: класс помечается атрибутом [Handles("ключ")] и реализует IMessageHandler<T>. Они автоматически находятся при сканировании сборок и регистрируются в DI как singleton.


Динамические подписки создаются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler). Можно подписаться лямбдой, методом, делегатом — не требуется отдельный класс или атрибут.

2. Время жизни

Статические существуют всё время работы приложения (пока жив DI-контейнер). Их нельзя убрать выборочно — только если полностью остановить хаб.

Динамические могут быть временными: подписка живёт, пока не будет вызван Dispose() возвращаемого токена. Можно подписаться на несколько секунд (например, ожидая ответ) и отписаться.

3. Порядок вызова

В режиме Both сначала всегда вызываются все статические обработчики (в порядке, определённом сканированием), а затем — все динамические (в порядке регистрации). Это происходит синхронно внутри одного цикла обработки сообщения.

4. Гибкость и предсказуемость

Статические дают жёсткую структуру: вы точно знаете, какие события кем обрабатываются, всё проверяется при старте. Подходит для обязательной бизнес-логики, аудита, инфраструктурных событий.

Динамические дают гибкость: можно подписываться и отписываться в зависимости от состояния приложения, добавлять обработчики без перекомпиляции (плагины, скрипты). Но есть риск случайно отписаться или пропустить событие.

5. Производительность

Статические чуть быстрее: реестр EventHandlerRegistry отдаёт готовый список типов, экземпляры обработчиков уже созданы в DI.

Динамические требуют поиска по ConcurrentDictionary и, возможно, упаковки делегатов, но накладные расходы минимальны.

6. Тестирование

Статические требуют настройки DI и сканирования сборок, но дают полную уверенность в работе всех штатных обработчиков.

Динамические позволяют в тесте быстро подписать лямбду, собрать данные и проверить их — меньше инфраструктуры.

📊 Сравнительная таблица

Характеристика Статические обработчики Динамические подписки

Когда определяются На этапе компиляции (атрибуты) В рантайме (вызов метода)

Время жизни Постоянное (пока жив DI) Может быть временным (через Dispose)

Регистрация Автоматическая (сканирование сборок) Явная (Subscribe<T>())

Тип обработчика Класс, реализующий IMessageHandler<T> Делегат (лямбда/метод)

Гибкость Низкая (нельзя убрать один обработчик) Высокая (можно подписать/отписать в любой момент)

Предсказуемость Высокая (всё видно при старте) Ниже (легко забыть или случайно отписать)

Производительность Немного выше (готовый реестр) Незначительно ниже (словарь + делегат)

Использование Бизнес-логика, аудит, обязательные события UI, плагины, временные реакции, тесты

🤝 Можно ли их совмещать?

Да, в режиме RoutingMode.Both оба типа работают одновременно. Сообщения обрабатываются в одной очереди строго по порядку: сначала отрабатывают статические обработчики, потом динамические. Это позволяет отделить постоянную инфраструктуру от гибкой функциональности без конфликтов.

Таким образом, выбор зависит от того, нужна ли вам неизменная и проверяемая структура (статика) или лёгкость добавления/удаления обработчиков в процессе работы (динамика).

суббота, 23 мая 2026 г.

Micro, Process, Architecture

Micro, Process, Architecture

https://chat.qwen.ai/s/485d52f5-88a8-4969-bbca-b00b7bafad4c?fev=0.2.53

https://giga.chat/link/gcscOdEfgM

https://www.perplexity.ai/search/8410ca7b-ed63-4fef-8b0e-06c11b21d2ca#4

https://chat.deepseek.com/share/ol3zcbwy8zwevm07ug

---

Статические подписки (атрибутные)

Это обработчики, помеченные атрибутом [Handles("key")] и зарегистрированные в DI через сканирование сборок.

✅ Когда использовать

  • Известный набор событий на этапе компиляции
    Все ключи и обработчики определены заранее, их количество не меняется во время работы приложения.

  • Жёсткая типизация и проверка на этапе сборки
    Если обработчик забыт или неправильно указан ключ, компилятор не заметит, но тесты или запуск сразу покажут отсутствие подписчика.

  • Инфраструктурные или системные события
    Например, события жизненного цикла приложения (ApplicationStarted, RequestHandled), логирование, аудит. Это вещи, которые всегда должны обрабатываться и не зависят от пользовательского ввода.

  • Модульная архитектура с явными зависимостями
    Каждый модуль (IEventHubModule) может объявить свои ассембли с обработчиками. Статические обработчики чётко показывают, какие события модуль обрабатывает.

  • Производительность
    Нет накладных расходов на динамическую подписку/отписку в рантайме. Обработчики создаются один раз как singleton.

❌ Когда не стоит

  • События, которые появляются или исчезают в зависимости от состояния приложения или пользовательских действий (например, подписка на уведомления конкретного пользователя).

  • Необходимость подписаться на событие из кода, который не должен зависеть от атрибутов (например, скрипты, плагины).


Динамические подписки (Subscribe)

Обработчики регистрируются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler).

✅ Когда использовать

  • Временные или условные подписки
    Пример: пользователь открыл страницу — подписались на обновления; закрыл — отписались.

  • Подписки с ограниченным временем жизни
    Например, запрос-ответ, ожидание конкретного события в течение таймаута.

  • Расширяемость без перекомпиляции
    Плагины или скрипты, загружаемые динамически, могут подписываться на события, не имея доступа к атрибутам или не имея возможности быть просканированными.

  • Код, где обработчик не может быть оформлен как класс с атрибутом
    Лямбды, методы экземпляра, замыкания. Удобно, когда логика обработки проста и не хочется создавать отдельный класс.

  • Тестирование
    В тестах легко подписать лямбду, собирающую сообщения, и проверить их получение.

❌ Когда не стоит

  • Обработчики, которые должны быть постоянными и предсказуемыми. Динамическая подписка может быть случайно удалена (dispose токена), и событие перестанет обрабатываться.

  • Если нужна строгая типизация и гарантия, что обработчик существует на момент старта приложения.


Сравнительная таблица

КритерийСтатические (атрибуты)Динамические (Subscribe)
Когда создаютсяНа старте приложения (сканирование сборок)В любой момент во время выполнения
Время жизниВсё время работы приложенияМожет быть конечным (через Dispose)
Способ регистрацииАтрибут [Handles] + реализация IMessageHandler<T>Вызов Subscribe<T>(key, handler)
ТипизацияЖёсткая: класс должен реализовать интерфейсГибкая: лямбда с сигнатурой Func<T, CancellationToken, ValueTask>
ПроизводительностьЧуть выше (нет поиска по списку динамических подписок)Незначительные накладные расходы на Dictionary lookup
ВидимостьПолный список можно получить через IEventHandlerRegistryТолько через GetSubscribedKeys()
Использование в тестахТребует настройки DI и сканирования сборокБыстро и изолированно: подписал лямбду и готово
Управление памятьюОбработчики живут пока жив контейнерМожно отписаться, освободив ресурсы

Рекомендация

  • Используйте статические обработчики для основной бизнес-логики и системных событий. Это даёт предсказуемость, строгую структуру и хорошую производительность.

  • Используйте динамические подписки для UI-логики, временных реакций, плагинов и тестов. Они дают гибкость и изоляцию.

  • Режим Both — отличное решение, когда в одном приложении сочетаются оба вида событий. Например, ядро обрабатывает аудит через статику, а клиентский код подписывается динамически на уведомления для конкретного пользователя.

Вы можете комбинировать оба подхода, не боясь конфликтов — они работают независимо и в одной очереди, сохраняя порядок сообщений.

----------------------------------------------------------------------

В вашей шине статические обработчики и динамические подписки — это два разных механизма реагирования на события, которые отличаются способом регистрации, временем жизни, производительностью и сценариями использования.

🔍 Основные различияиизадаются на этапе компиляции: класс помечается атрибутом [Handles("ключ")] и реализует IMessageHandler<T>. Они автоматически находятся при сканировании сборок и регистрируются в DI как singleton.

---------------------------------------

Основные различия

1. Способ регистрации

  • Статические обработчики задаются на этапе компиляции: класс помечается атрибутом [Handles("ключ")] и реализует IMessageHandler<T>. Они автоматически находятся при сканировании сборок и регистрируются в DI как singleton.

  • Динамические подписки создаются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler). Можно подписаться лямбдой, методом, делегатом — не требуется отдельный класс или атрибут.

2. Время жизни

  • Статические существуют всё время работы приложения (пока жив DI-контейнер). Их нельзя убрать выборочно — только если полностью остановить хаб.

  • Динамические могут быть временными: подписка живёт, пока не будет вызван Dispose() возвращаемого токена. Можно подписаться на несколько секунд (например, ожидая ответ) и отписаться.

3. Порядок вызова

В режиме Both сначала всегда вызываются все статические обработчики (в порядке, определённом сканированием), а затем — все динамические (в порядке регистрации). Это происходит синхронно внутри одного цикла обработки сообщения.

4. Гибкость и предсказуемость

  • Статические дают жёсткую структуру: вы точно знаете, какие события кем обрабатываются, всё проверяется при старте. Подходит для обязательной бизнес-логики, аудита, инфраструктурных событий.

  • Динамические дают гибкость: можно подписываться и отписываться в зависимости от состояния приложения, добавлять обработчики без перекомпиляции (плагины, скрипты). Но есть риск случайно отписаться или пропустить событие.

5. Производительность

  • Статические чуть быстрее: реестр EventHandlerRegistry отдаёт готовый список типов, экземпляры обработчиков уже созданы в DI.

  • Динамические требуют поиска по ConcurrentDictionary и, возможно, упаковки делегатов, но накладные расходы минимальны.

6. Тестирование

  • Статические требуют настройки DI и сканирования сборок, но дают полную уверенность в работе всех штатных обработчиков.

  • Динамические позволяют в тесте быстро подписать лямбду, собрать данные и проверить их — меньше инфраструктуры.

📊 Сравнительная таблица

ХарактеристикаСтатические обработчикиДинамические подписки
Когда определяютсяНа этапе компиляции (атрибуты)В рантайме (вызов метода)
Время жизниПостоянное (пока жив DI)Может быть временным (через Dispose)
РегистрацияАвтоматическая (сканирование сборок)Явная (Subscribe<T>())
Тип обработчикаКласс, реализующий IMessageHandler<T>Делегат (лямбда/метод)
ГибкостьНизкая (нельзя убрать один обработчик)Высокая (можно подписать/отписать в любой момент)
ПредсказуемостьВысокая (всё видно при старте)Ниже (легко забыть или случайно отписать)
ПроизводительностьНемного выше (готовый реестр)Незначительно ниже (словарь + делегат)
ИспользованиеБизнес-логика, аудит, обязательные событияUI, плагины, временные реакции, тесты

🤝 Можно ли их совмещать?

Да, в режиме RoutingMode.Both оба типа работают одновременно. Сообщения обрабатываются в одной очереди строго по порядку: сначала отрабатывают статические обработчики, потом динамические. Это позволяет отделить постоянную инфраструктуру от гибкой функциональности без конфликтов.

Таким образом, выбор зависит от того, нужна ли вам неизменная и проверяемая структура (статика) или лёгкость добавления/удаления обработчиков в процессе работы (динамика).

Динамические подписки создаются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler). Можно подписаться лямбдой, методом, делегатом — не требуется отдельный класс или атрибут.

-------

В вашей шине статические обработчики и динамические подписки — это два разных механизма реагирования на события, которые отличаются способом регистрации, временем жизни, производительностью и сценариями использования.


🔍 Основные различия

1. Способ регистрации

Статические обработчики задаются на этапе компиляции: класс помечается атрибутом [Handles("ключ")] и реализует IMessageHandler<T>. Они автоматически находятся при сканировании сборок и регистрируются в DI как singleton.


Динамические подписки создаются в рантайме через IDynamicSubscriptionManager.Subscribe<T>(key, handler). Можно подписаться лямбдой, методом, делегатом — не требуется отдельный класс или атрибут.

2. Время жизни

Статические существуют всё время работы приложения (пока жив DI-контейнер). Их нельзя убрать выборочно — только если полностью остановить хаб.

Динамические могут быть временными: подписка живёт, пока не будет вызван Dispose() возвращаемого токена. Можно подписаться на несколько секунд (например, ожидая ответ) и отписаться.

3. Порядок вызова

В режиме Both сначала всегда вызываются все статические обработчики (в порядке, определённом сканированием), а затем — все динамические (в порядке регистрации). Это происходит синхронно внутри одного цикла обработки сообщения.

4. Гибкость и предсказуемость

Статические дают жёсткую структуру: вы точно знаете, какие события кем обрабатываются, всё проверяется при старте. Подходит для обязательной бизнес-логики, аудита, инфраструктурных событий.

Динамические дают гибкость: можно подписываться и отписываться в зависимости от состояния приложения, добавлять обработчики без перекомпиляции (плагины, скрипты). Но есть риск случайно отписаться или пропустить событие.

5. Производительность

Статические чуть быстрее: реестр EventHandlerRegistry отдаёт готовый список типов, экземпляры обработчиков уже созданы в DI.

Динамические требуют поиска по ConcurrentDictionary и, возможно, упаковки делегатов, но накладные расходы минимальны.

6. Тестирование

Статические требуют настройки DI и сканирования сборок, но дают полную уверенность в работе всех штатных обработчиков.

Динамические позволяют в тесте быстро подписать лямбду, собрать данные и проверить их — меньше инфраструктуры.

📊 Сравнительная таблица

Характеристика Статические обработчики Динамические подписки

Когда определяются На этапе компиляции (атрибуты) В рантайме (вызов метода)

Время жизни Постоянное (пока жив DI) Может быть временным (через Dispose)

Регистрация Автоматическая (сканирование сборок) Явная (Subscribe<T>())

Тип обработчика Класс, реализующий IMessageHandler<T> Делегат (лямбда/метод)

Гибкость Низкая (нельзя убрать один обработчик) Высокая (можно подписать/отписать в любой момент)

Предсказуемость Высокая (всё видно при старте) Ниже (легко забыть или случайно отписать)

Производительность Немного выше (готовый реестр) Незначительно ниже (словарь + делегат)

Использование Бизнес-логика, аудит, обязательные события UI, плагины, временные реакции, тесты

🤝 Можно ли их совмещать?

Да, в режиме RoutingMode.Both оба типа работают одновременно. Сообщения обрабатываются в одной очереди строго по порядку: сначала отрабатывают статические обработчики, потом динамические. Это позволяет отделить постоянную инфраструктуру от гибкой функциональности без конфликтов.

Таким образом, выбор зависит от того, нужна ли вам неизменная и проверяемая структура (статика) или лёгкость добавления/удаления обработчиков в процессе работы (динамика).