Статические подписки (атрибутные)
Это обработчики, помеченные атрибутом [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.
---------------------------------------
Комментариев нет:
Отправить комментарий