Create wiki page 'Architecture'

2026-04-24 10:57:47 +03:00
parent 1160ecbedc
commit 70f0c65bff
+80
@@ -0,0 +1,80 @@
# Architecture
GM-Relay состоит из нескольких .NET-проектов и общей PostgreSQL БД.
## Проекты
- `src/GmRelay.AppHost` — Aspire AppHost. Поднимает PostgreSQL с PgAdmin и запускает bot/web проекты.
- `src/GmRelay.Bot` — Worker Service с Telegram long polling, миграциями, планировщиком и вертикальными срезами бота.
- `src/GmRelay.Web` — Blazor Server UI для ГМа.
- `src/GmRelay.Shared` — общие доменные значения и rendering-код Telegram-сообщений.
- `src/GmRelay.ServiceDefaults` — общие Aspire defaults: OpenTelemetry, service discovery, resilience, health checks.
## Архитектурное решение
В проекте принят подход Vertical Slice Architecture. Логика бота лежит в `src/GmRelay.Bot/Features/*`, где каждый сценарий содержит свой handler и локальные DTO.
Причины такого выбора зафиксированы в `docs/adr/0001-use-vertical-slice-native-aot-and-aspire.md`:
- сценариев немного, отдельный Clean Architecture слой был бы лишним;
- зависимости Npgsql и Telegram.Bot стабильны;
- Native AOT требует явной регистрации зависимостей и аккуратного SQL-маппинга;
- BackgroundService + PeriodicTimer проще и надёжнее Quartz.NET для двух периодических проверок.
## Поток Telegram updates
`TelegramBotService` получает updates через long polling и передаёт их в `UpdateRouter`.
`UpdateRouter` явно маршрутизирует:
- `/start`, `/help`, `/newsession`, `/listsessions`, `/exportcalendar`;
- callback `join_session:<sessionId>`;
- callback `cancel_session:<sessionId>`;
- callback `delete_session:<sessionId>`;
- callback `reschedule_session:<sessionId>`;
- callback `reschedule_vote:<yes|no>:<proposalId>`;
- callback `rsvp:<confirm|decline>:<sessionId>`;
- обычные текстовые сообщения ГМа как ввод нового времени для активного переноса.
Маршрутизация не использует reflection или assembly scanning.
## Планировщик
`SessionSchedulerService` — stateless background service. Он запускается сразу при старте, затем раз в минуту:
1. Ищет сессии `Planned`, у которых `scheduled_at - 24h <= now()`.
2. Вызывает `SendConfirmationHandler` и переводит сессию в `ConfirmationSent`.
3. Ищет сессии `Confirmed`, у которых `scheduled_at - 5min <= now()` и `link_message_id IS NULL`.
4. Вызывает `SendJoinLinkHandler`.
Состояние хранится в PostgreSQL, поэтому после рестарта сервис продолжает работу без in-memory очередей.
## Web UI
Blazor Server-приложение использует cookie auth и Telegram Login Widget.
Основные сервисы:
- `TelegramAuthService` валидирует HMAC-SHA256 подпись Telegram Login Widget и `auth_date`.
- `SessionService` читает и обновляет группы/сессии через Dapper.
- `AuthorizedSessionService` проверяет, что текущий Telegram ID является ГМом группы.
Основные страницы:
- `/login` — вход через Telegram.
- `/` — список групп ГМа.
- `/group/{GroupId}` — будущие сессии группы.
- `/session/edit/{SessionId}` — редактирование названия, времени и ссылки.
## Shared domain
`GmRelay.Shared` содержит:
- `SessionStatus`: `Planned`, `ConfirmationSent`, `Confirmed`, `Cancelled`.
- `RsvpStatus`: `Pending`, `Confirmed`, `Declined`.
- `MoscowTime`: парсинг и форматирование МСК (`UTC+3`).
- `SessionBatchRenderer`: единый renderer для Telegram-сообщения пачки сессий.
## Диаграммы
C4-диаграммы хранятся в `docs/c4-system-context.md`. Там описаны system context, container view и component view для `GmRelay.Bot`.