From dbf59c544a72f2109db4bf5bb45b8dcfe8b8c990 Mon Sep 17 00:00:00 2001 From: Toutsu Date: Wed, 6 May 2026 12:07:10 +0300 Subject: [PATCH] =?UTF-8?q?docs(adr):=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20ADR=20002=20=E2=80=94=20platform-neutral?= =?UTF-8?q?=20batch=20rendering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../002-platform-neutral-batch-rendering.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/adr/002-platform-neutral-batch-rendering.md diff --git a/docs/adr/002-platform-neutral-batch-rendering.md b/docs/adr/002-platform-neutral-batch-rendering.md new file mode 100644 index 0000000..eb901f4 --- /dev/null +++ b/docs/adr/002-platform-neutral-batch-rendering.md @@ -0,0 +1,65 @@ +# ADR 002: Platform-Neutral Batch Rendering + +## Status + +**Accepted** — implemented in v1.10.0 (PR #42). + +## Context + +`SessionBatchRenderer` жил в `GmRelay.Shared` и напрямую зависел от `Telegram.Bot` (`InlineKeyboardMarkup`, `ParseMode.Html`). Это создавало проблемы: + +1. **Shared не был platform-neutral.** Любой платформенный проект (Discord, Slack, WebSocket) тащил Telegram-зависимость. +2. **Дублирование логики.** `GmRelay.Web` использовал тот же рендерер через прямую зависимость от `Shared`, но Web — это не Telegram-клиент. +3. **Невозможно написать unit-тесты без Telegram-объектов.** Smoke-тесты создавали InlineKeyboardMarkup даже для проверки чисто доменной логики. + +## Decision + +Разделить рендеринг на две стадии: + +1. **View Builder (platform-neutral)** — собирает view model из доменных DTO. +2. **Platform Renderer (platform-specific)** — превращает view model в платформенное представление. + +``` +Domain DTOs + │ + ▼ +SessionBatchViewBuilder (Shared) + │ + ▼ +SessionBatchViewModel (platform-neutral) + │ + ├──► TelegramSessionBatchRenderer ──► HTML + InlineKeyboardMarkup + │ + └──► DiscordSessionBatchRenderer ──► (issue #26) +``` + +### Изменённые компоненты + +| Компонент | Было | Стало | +|---|---|---| +| `SessionBatchRenderer` | `GmRelay.Shared.Rendering` | Удалён | +| `SessionBatchViewBuilder` | — | `GmRelay.Shared.Rendering` | +| `SessionBatchViewModel` | — | `GmRelay.Shared.Rendering` | +| `TelegramSessionBatchRenderer` | — | `GmRelay.Bot` + `GmRelay.Web` | +| `DiscordSessionBatchRenderer` | — | `GmRelay.Shared.Rendering` (stub) | +| `BatchMessageEditor` | `GmRelay.Shared.Rendering` | `GmRelay.Bot` + `GmRelay.Web` | + +## Consequences + +### Positive + +- `GmRelay.Shared` больше не зависит от `Telegram.Bot`. Чистый platform-agnostic проект. +- Можно добавить `DiscordSessionBatchRenderer` без изменений в `Shared`. +- Unit-тесты ViewBuilder не создают `InlineKeyboardMarkup`. +- Логика подсчёта игроков, сортировки сессий и генерации действий — в одном месте (ViewBuilder). + +### Negative + +- **Временное дублирование.** `TelegramSessionBatchRenderer` и `BatchMessageEditor` скопированы в `Bot` и `Web`. Планируется вынести в `GmRelay.Shared.Telegram` при появлении третьего Telegram-потребителя. +- **Дополнительная стадия.** Теперь два вызова вместо одного: `Build` + `Render`. Этоtrade-off за чистоту абстракции. + +## Related + +- Issue #22 — этот рефакторинг. +- Issue #26 — Discord Bot MVP (потребитель новой архитектуры). +- ADR 001 — vertical slice, native AOT, Aspire (`docs/adr/0001-use-vertical-slice-native-aot-and-aspire.md`).