Перейти к основному содержимому

ADR-008: Абстракция channel service

  • Статус: Принято
  • Дата: 16 апреля 2026
  • Автор: 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 маршрутизацию и тестирование.

Сразу перейти на DB-backed каналы

Пока отклонено. Продукту сначала нужна абстракция; DB-модель и management UI можно добавить позже без смены plugin-контрактов.

Свой service у каждого плагина

Отклонено. У Telegram и WhatsApp должна быть одна и та же форма: type, id, department, namespace, enabled flag и config map.

Последствия

Плюсы

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

Минусы

  • Env-backed реализация пока отдаёт только один канал каждого типа.
  • Секреты живут в process-конфиге, пока не добавится DB-backed хранилище секретов.
  • findByRoutingKey() остаётся опциональным до момента, когда понадобится для inbound-маршрутизации.

Заметки по реализации

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