# ADR-0001: Chat Platform Boundaries (GraphQL + Hatchet) Дата: 2026-03-08 Статус: accepted ## Контекст Нужна минимальная и предсказуемая схема из 6 сервисов: - `frontend` - `backend` - `backend_worker` - `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. `backend_worker` - исполняет периодические backend workflow в Hatchet; - для cron-задач вызывает `backend /graphql` (без прямого доступа к Prisma). 5. `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`. ### Calendar Predue (Backend cron) 1. Hatchet по cron запускает workflow в `backend_worker`. 2. `backend_worker` вызывает `backend.syncCalendarPredueTimeline`. 3. `backend` делает upsert `ClientTimelineEntry` для `CalendarEvent` в окне `startsAt - preDue`. ## Границы ответственности `backend`: - можно: вся бизнес-логика и состояние; - нельзя: прямой вызов Telegram API. `telegram_backend`: - можно: webhook ingress, нормализация, enqueue, адаптер Telegram API; - нельзя: доменные записи CRM. `telegram_worker`: - можно: исполнение задач, ретраи, orchestration шагов; - нельзя: хранение CRM-состояния и прямой доступ к основной БД. `backend_worker`: - можно: периодические orchestration задачи через Hatchet; - нельзя: прямой доступ к основной БД (только через backend GraphQL). ## Надежность - webhook отвечает `200` только после успешной постановки задачи в Hatchet; - при недоступности сервисов задача ретраится Hatchet; - inbound обработка идемпотентна через `idempotencyKey` и provider identifiers в `backend`. - календарный sync использует advisory-lock в `backend`, поэтому параллельные cron-run безопасны. ## Последствия Плюсы: - меньше скрытых связей; - изоляция доменной БД в `backend`; - единая точка ретраев/оркестрации (Hatchet). Минусы: - выше требования к стабильности GraphQL-контрактов между сервисами; - нужна наблюдаемость по цепочкам `telegram_backend -> hatchet -> telegram_worker -> backend` и `hatchet -> backend_worker -> backend`.