# ADR-0001: Chat Platform Boundaries (GraphQL + Hatchet) Дата: 2026-03-08 Статус: accepted ## Контекст Нужна минимальная и предсказуемая схема из 5 сервисов: - `frontend` - `backend` - `telegram_backend` - `telegram_worker` - `hatchet` Ключевые ограничения: - основная Prisma/доменная БД только в `backend`; - `telegram_backend` и `telegram_worker` не содержат CRM-домен и не пишут в основную БД; - взаимодействие между сервисами только через GraphQL; - асинхронность и ретраи централизованы в Hatchet. ## Решение Принимаем архитектуру: 1. `backend` - владеет доменной моделью чатов и единственной основной Prisma-базой; - принимает inbound события от `telegram_worker` через GraphQL (`ingestTelegramInbound`); - создает outbound задачи в `telegram_backend` через GraphQL (`requestTelegramOutbound`); - принимает delivery-отчеты от `telegram_worker` через GraphQL (`reportTelegramOutbound`). 2. `telegram_backend` - принимает webhook Telegram; - нормализует payload в `OmniInboundEnvelopeV1`; - ставит задачи в Hatchet (`process-telegram-inbound`, `process-telegram-outbound`); - предоставляет GraphQL API для enqueue и отправки в Telegram API. 3. `telegram_worker` - исполняет задачи Hatchet; - для inbound вызывает `backend /graphql`; - для outbound вызывает `telegram_backend /graphql` (`sendTelegramMessage`), затем `backend /graphql` (`reportTelegramOutbound`); - не имеет собственной Prisma-базы. 4. `hatchet` - единый оркестратор задач, ретраев и backoff-политик. ## Потоки ### Inbound (Telegram -> CRM) 1. Telegram webhook приходит в `telegram_backend`. 2. `telegram_backend` нормализует событие и enqueue в Hatchet `process-telegram-inbound`. 3. `telegram_worker` исполняет задачу и вызывает `backend.ingestTelegramInbound`. 4. `backend` сохраняет доменные изменения в своей БД. ### Outbound (CRM -> Telegram) 1. `backend` инициирует отправку (`requestTelegramOutbound`) в `telegram_backend`. 2. `telegram_backend` enqueue в Hatchet `process-telegram-outbound`. 3. `telegram_worker` вызывает `telegram_backend.sendTelegramMessage`. 4. `telegram_worker` репортит итог в `backend.reportTelegramOutbound`. ## Границы ответственности `backend`: - можно: вся бизнес-логика и состояние; - нельзя: прямой вызов Telegram API. `telegram_backend`: - можно: webhook ingress, нормализация, enqueue, адаптер Telegram API; - нельзя: доменные записи CRM. `telegram_worker`: - можно: исполнение задач, ретраи, orchestration шагов; - нельзя: хранение CRM-состояния и прямой доступ к основной БД. ## Надежность - webhook отвечает `200` только после успешной постановки задачи в Hatchet; - при недоступности сервисов задача ретраится Hatchet; - inbound обработка идемпотентна через `idempotencyKey` и provider identifiers в `backend`. ## Последствия Плюсы: - меньше сервисов и меньше скрытых связей; - изоляция доменной БД в `backend`; - единая точка ретраев/оркестрации (Hatchet). Минусы: - выше требования к стабильности GraphQL-контрактов между сервисами; - нужна наблюдаемость по цепочке `telegram_backend -> hatchet -> telegram_worker -> backend`.