Перейти до основного вмісту

ADR-008: Channel service abstraction

  • Статус: прийнято
  • Дата: 2026-04-16
  • Автор: CTO Agent (KALA-151)

Контекст

Канальні плагіни Telegram і WhatsApp спочатку читали env-змінні напряму. Це працює для одного Telegram-бота та одного WhatsApp-номера, але не масштабується на multi-tenant або multi-namespace-деплої, де різним відділам може бути потрібна різна канальна конфігурація.

Канальний шар також потребує стабільного контракту, щоб майбутні канали (web, Slack, email, db-backed) можна було додавати без переписування кожного плагіна.

Рішення

Впроваджуємо інтерфейс ChannelService:

export interface ChannelService {
getActiveChannel(type: ChannelType): Promise<ChannelConfig | null>;
listActive(type?: ChannelType): Promise<ChannelConfig[]>;
findByRoutingKey?(key: string): Promise<ChannelConfig | null>;
}

Поточний EnvChannelService читає існуючий config-обʼєкт і віддає один активний Telegram-канал і один активний WhatsApp-канал. Непідтримувані типи повертають null або порожній список.

Fastify експонує сервіс як app.channelService, щоб плагіни залежали від інтерфейсу, а не читали env напряму.

Розглянуті альтернативи

Лишити пряме читання env у плагінах

Відхилено. Робить перший деплой простішим, але ускладнює multi-channel routing і тестування.

Одразу перейти на db-backed канали

Відхилено на цій фазі. Продукт спочатку потребує абстракції; db-моделі та management-UI можна додати пізніше без зміни контрактів плагінів.

Plugin-specific config-сервіси

Відхилено. Telegram і WhatsApp потребують однакової форми: type, id, department, namespace, enabled-прапорець і config-мапа.

Наслідки

Позитивні

  • Канальні плагіни працюють через стабільний інтерфейс.
  • Тести можуть конструювати EnvChannelService з невеликим config-стабом.
  • Майбутня db-backed маршрутизація підтримуватиме множинні канали кожного типу.
  • Department і namespace scope — first-class поля на ChannelConfig.

Негативні

  • Env-backed реалізація поки експонує лише один канал кожного типу.
  • Секрети лишаються у process-config, поки не зʼявиться db-backed secret store.
  • findByRoutingKey() лишається опційним, поки не знадобиться для inbound routing.

Нотатки про реалізацію

  • src/lib/channels/channel-service.ts визначає інтерфейс і env-backed реалізацію.
  • Сервіс наразі підтримує telegram і whatsapp.
  • ChannelType уже включає slack, email і web під майбутні реалізації.