From 70f0c65bff9a450b4f54920716da991ac21b7aa5 Mon Sep 17 00:00:00 2001 From: Toutsu Date: Fri, 24 Apr 2026 10:57:47 +0300 Subject: [PATCH] Create wiki page 'Architecture' --- Architecture.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Architecture.md diff --git a/Architecture.md b/Architecture.md new file mode 100644 index 0000000..69cbfb2 --- /dev/null +++ b/Architecture.md @@ -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:`; +- callback `cancel_session:`; +- callback `delete_session:`; +- callback `reschedule_session:`; +- callback `reschedule_vote::`; +- callback `rsvp::`; +- обычные текстовые сообщения ГМа как ввод нового времени для активного переноса. + +Маршрутизация не использует 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`. \ No newline at end of file