Files
clientsflow/docs/adr/0001-chat-platform-service-boundaries.md
2026-03-08 19:15:30 +07:00

110 lines
5.0 KiB
Markdown

# 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`.