Skip to content

Фаза 5: EventBus — асинхронное взаимодействие между модулями #495

@ShaerWare

Description

@ShaerWare

Parent

Часть плана модульной декомпозиции: #489

Цель

Заменить прямые импорты и вызовы между несвязанными доменами на события через EventBus. Модули публикуют события, другие модули подписываются. Нет прямых зависимостей между доменами одного уровня.

События для реализации

Каждое событие — отдельный PR:

Событие Публикует Подписчик Что заменяет
KnowledgeUpdated knowledge llm (reload FAQ cache) Прямой вызов из wiki_rag роутера
WidgetSessionCreated channels/widget crm (create amoCRM lead) create_task(_widget_create_amocrm_lead) в orchestrator.py
WidgetMessageSent channels/widget crm (append note to lead) Прямой вызов в widget endpoint
WidgetContactSubmitted channels/widget crm (link contact to lead) Прямой вызов в widget endpoint
UserRoleChanged core/auth core/cache (invalidation) Ручной cache.invalidate()
SessionRevoked core/auth core/cache (invalidation) Ручной _session_cache.remove()
DatasetSynced crm, ecommerce knowledge (reindex collection) Прямой вызов reindex в amocrm/woocommerce роутерах
ConfigChanged core/config affected modules (reload) Нет (сейчас требует рестарт сервиса)
BotProcessDied channels/telegram, /whatsapp monitoring (alert), self (auto-restart) Нет (сейчас бот падает молча)

Паттерн

# modules/channels/widget/events.py
@dataclass
class WidgetSessionCreated(BaseEvent):
    session_id: str
    widget_instance_id: str
    visitor_metadata: dict

# modules/channels/widget/router.py — публикация
await bus.publish(WidgetSessionCreated(
    session_id=session.id,
    widget_instance_id=widget_id,
    visitor_metadata=metadata,
))

# modules/crm/service.py — подписка
class CRMService:
    def setup_events(self, bus: EventBus):
        bus.subscribe(WidgetSessionCreated, self._on_widget_session)

    async def _on_widget_session(self, event: WidgetSessionCreated):
        """Создать lead в amoCRM при создании виджет-сессии."""
        ...

Порядок работы

Начать с самых простых и изолированных событий:

  1. UserRoleChanged, SessionRevoked — внутри core, самый безопасный
  2. KnowledgeUpdated — простая связь knowledge → llm
  3. DatasetSynced — crm/ecommerce → knowledge
  4. WidgetSessionCreated/MessageSent/ContactSubmitted — widget → crm
  5. BotProcessDied — channels → monitoring (новая функциональность)
  6. ConfigChanged — последним (широкий blast radius)

Критерии готовности

  • Все перечисленные прямые зависимости между доменами заменены на события
  • Каждый модуль определяет свои события в events.py
  • Подписки регистрируются в setup_events() при инициализации модуля
  • Ошибки в обработчиках логируются, не роняют publisher
  • Граф зависимостей соответствует схеме из Целевая архитектура: модульная декомпозиция #481
  • Все тесты проходят
  • CI зелёный

Риск: низкий-средний

Каждое событие меняет ровно один поток данных. Если обработчик упал — publisher продолжает работать (fire-and-forget с логированием).

Зависимости

  • Фаза 0 (EventBus должен существовать)
  • Фаза 4 (EventBus должен быть интегрирован в startup — модули регистрируют подписки)

Оценка: M (5-6 PR, по одному событию)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low priority / Backlogphase:5-techdebtPhase 5: Technical DebtrefactoringArchitectural refactoring

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions