Files
Deal/docs/architecture/2026-09-05-deal-architecture-design.md
T
Rustam Khalimov 27c7831910
ci / build-test (push) Canceled after 0s
Deal — единая кодовая база
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/
Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue,
контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер),
Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог).

Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0,
тесты 1340/130/52/38/9 зелёные.
2026-09-11 23:56:47 +03:00

273 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Дейл (Deal) — архитектурный дизайн-док
> Исторический документ (архитектурный дизайн-черновик, 2026-09-05). Актуальное состояние — `docs/superpowers/STATUS.md` и `docs/technical/Техническая-документация-Дейл.md`.
> Версия: 0.1 (черновик для согласования)
> Дата: 2026-09-05
> Статус: фиксирует согласованные решения по переписыванию LeadRadar в новый продукт «Дейл»
---
## 1. Контекст и цели
**LeadRadar** — рабочий прототип (Python/FastAPI/DuckDB/Vue), проверенный на тестовых данных.
**«Дейл»** — новая реализация: SaaS-продукт, который мониторит Telegram-каналы и группы клиентов,
отсеивает рекламу/скам/дубликаты и показывает **реальные заказы и клиентов**, совпадающих с
профилем пользователя (сфера, стек, бюджет). Клиенты подключают свои Telegram-аккаунты.
### Цели переписывания
1. Код, который владелец продукта может поддерживать сам (типизированный .NET вместо Python).
2. Стабильность и строгость типов, интерфейсов, слоёв — «как сеньор-архитектор».
3. Мультитенантный SaaS (схема на тенанта) — фундамент для роста до сотен/тысяч клиентов.
4. Безопасность «с первого дня» (публичный продукт).
5. Полная документация: ТЗ, инструкция пользователя, техдок — параллельно с кодом.
### Не-цели (сейчас)
- Переписывание фронтенда (Vue остаётся как есть).
- Kafka/кубер (отложены до реального масштаба; архитектура готова к ним).
- Биллинг-провайдер (лимиты в ядре, биллинг — позже).
- Саморегистрация тенантов (только инвайты).
---
## 2. Решения верхнего уровня (зафиксированы)
| # | Решение | Выбор |
|---|---|---|
| 1 | Стратегия | Big Bang: пишем новый бэкенд целиком; старые данные не мигрируем (тестовые) |
| 2 | Фронтенд | Vue не трогаем; HTTP-контракт `/api/...` — замороженная спецификация миграции |
| 3 | Архитектура | Модульный монолит в `core` (один процесс, одно sln); сервисы — отдельные процессы/sln |
| 4 | Стек | .NET (актуальная LTS), C# современный, Postgres |
| 5 | Мультитенантность | Одна Postgres-БД, **схема на тенанта** (`tenant_<id>.*`), системное в `public` |
| 6 | Владение данными | Каждый модуль владеет своими таблицами; межмодульно — интерфейсы/доменные события |
| 7 | Межпроцессно | gRPC + mTLS; шина событий за портом `IEventBus` (outbox → Kafka позже) |
| 8 | Telegram | Отдельный `telegram-service`: ферма сессий, 1 аккаунт/тенант, анти-бан; исполняет команды ядра, ничего не знает о бизнес-логике |
| 9 | ML | Отдельный `ml-service`: .NET + ONNX, пул моделей per-tenant, обучение на действиях |
| 10 | AI (LLM) | Отдельный `ai-service`: фасад провайдеров, промпты, учёт токенов |
| 11 | Клиенты SaaS | Подключают свои Telegram-аккаунты и настраивают обработку под свою сферу |
| 12 | Доступ тенантов | Инвайты: тенанта создаёт оператор, клиент по ссылке задаёт пароль |
| 13 | Аутентификация | Логин = email + пароль (email уникален глобально); `tenantId` в сессии/JWT |
| 14 | Название | «Дейл» (бренд), namespace `Deal` |
| 15 | Код-стайл | Документ пользователя + 5 адаптаций; 1 тип = 1 файл; `.editorconfig` + анализаторы |
| 16 | Наблюдаемость | Serilog + OpenTelemetry → Grafana + Loki + Promtail |
| 17 | Админка | Операторская (A): тенанты, лимиты, health, impersonation, аудит |
| 18 | Бэкапы | Ежедневные: Postgres + minio + сессии |
| 19 | Лимиты | Бюджет токенов на тенанта (LLM); fallback на ML/локальную обработку |
| 20 | Деплой | docker compose на своём VPS; Cloudflare перед origin; k8s позже |
---
## 3. Структура репозитория
```
src/
core/ # МОДУЛЬНЫЙ МОНОЛИТ — один процесс, один sln
Deal.sln
Deal.Api/ # host: Web API (/api-контракт), gRPC-сервер, SSE, DI
Deal.Modules.Pipeline/ # очередь → стоп-лист → дедуп → ML/ИИ → карточка
Deal.Modules.Kanban/ # карточки, колонки, правила, архив/корзина
Deal.Modules.Projects/ # «Выбранные» (проектный канбан)
Deal.Modules.Discovery/ # поиск каналов, вступление, чёрный список
Deal.Modules.Settings/ # настройки тенанта, промпты, валюты
Deal.Modules.Tenants/ # тенанты, инвайты, лимиты, админка
Deal.SharedKernel/ # Result, доменные события, время, tenant-контекст
Deal.Infrastructure/ # Postgres, миграции, outbox, IEventBus, файлы (MinIO)
Deal.Contracts/ # DTO для /api + gRPC-контракты наружу
tests/ # Deal.Tests.* (unit/integration модулей)
ml-service/ # Deal.Ml.sln — .NET + ONNX, обучение/предсказание per-tenant
ai-service/ # Deal.Ai.sln — LLM-фасад, промпты, учёт токенов
telegram-service/ # Deal.Telegram.sln — ферма сессий, анти-бан
contracts/ # общие .proto (gRPC): ml.proto, ai.proto, telegram.proto
frontend/ # Vue — переезжает как есть
docker-compose.yml # dev-подъём всех процессов
```
Правила:
- `core` — единственное место с бизнес-логикой и БД.
- Каждый сервис самодостаточен: свой sln, свой контейнер.
- `.proto` — единственный общий «язык» между процессами, лежит в `contracts/`.
---
## 4. Мультитенантность
### Модель БД
- Одна Postgres-БД, **схема на тенанта**: `tenant_<id>.*`.
- Системные таблицы (реестр тенантов, пользователи, инвайты, глобальные настройки,
ключи приложения Telegram) — в схеме `public`.
- DAL получает схему из tenant-контекста (claim в JWT / gRPC-метаданные);
пул соединений переключает `search_path`.
- Миграции применяются ко всем схемам тенантов (специальный механизм, см. §10).
- «Золотым» клиентам позже — выделенный инстанс: стратегия выбора схемы/БД в одном месте.
### Изоляция (критично)
- `tenantId` **только из сессии/JWT**, никогда из тела запроса.
- Каждый SQL-запрос исполняется в контексте схемы тенанта; модуль проверяет
принадлежность объекта тенанту (IDOR-защита).
- Интеграционные тесты на перекрёстный доступ тенантов — обязательны.
### Обработка per-tenant
- Настройки обработки (стоп-фразы, промпты, колонки/правила, ключи) — per-tenant.
- **ML-модель — per-tenant** (модель дизайнера не учится на действиях кровельщика):
`ml-service` держит пул моделей, core передаёт `tenantId` в каждом вызове.
---
## 5. Модули core и их границы
Модули заводятся сразу как отдельные проекты; **внутренние интерфейсы между ними
не выдумываются заранее** — появляются в момент реальной зависимости.
| Модуль | Ответственность | Владеет таблицами (в схеме тенанта) |
|---|---|---|
| Pipeline | очередь входящих → стоп-лист → дедуп → ML/ИИ → карточка; отсев; обработка | очередь, отсев, dedup |
| Kanban | карточки, колонки, правила, архив/корзина, комментарии, файлы | карточки, колонки |
| Projects | «Выбранные»: свой канбан, стадии, история, напоминания | проекты |
| Discovery | задачи поиска каналов, кандидаты, чёрный список, квоты | discovery-таблицы |
| Settings | настройки тенанта, промпты, валюты | настройки |
| Tenants | тенанты, пользователи, инвайты, лимиты, аудит, операторская админка | tenant-реестр (в `public`) |
Общие справочники (например, «колонки» нужны и Pipeline при создании карточки, и Kanban
при отрисовке) живут в модуле-владельце (Kanban); доступ — через его публичный интерфейс.
---
## 6. Контракты
### 6.1 `/api` — замороженный контракт миграции
- Фронтенд Vue продолжает ходить в `/api/...` без изменений.
- Снимаем точную карту с работающего LeadRadar (эндпоинты + формы ответов, которые
реально потребляет фронт) → фиксируем как OpenAPI-спецификацию.
- Новый `Deal.Api` обязан воспроизводить её 1:1.
- Ведём реестр «кривых мест»: если правка фронта на 1 строку убирает слой костылей —
выносим на решение владельца по одному (не молча).
### 6.2 gRPC-контракты (`contracts/`)
- `telegram.proto`: команды ядра (подключить аккаунт, слушать канал, перечитать,
вступить/выйти) + поток сырых сообщений → ядро.
- `ml.proto`: predict (текст → решение), train (действие → обучение), health.
- `ai.proto`: classify/filter/generate (текст → структура), учёт токенов.
- Каждый вызов несёт `tenantId`; сервисы проверяют принадлежность по своей модели
(сессии/модели), не доверяя полю на слово.
### 6.3 Шина событий
- Порт `IEventBus` в SharedKernel.
- Реализация сейчас: outbox в Postgres (транзакционно событие + эффект, фоновый диспетчер).
- Kafka — позже, сменой реализации без правки бизнес-логики.
---
## 7. Сервисы
### 7.1 telegram-service
- Отдельный процесс, свой sln. Ничего не знает о данных и бизнес-логике.
- **Сессии привязаны к тенанту** (`tenantId → session`, 1:1): команды исполняются только
на сессии своего тенанта; нет сессии для tenantId → отказ.
- Проверка принадлежности диалога: read/subscribe только для диалогов аккаунта тенанта.
- Join — только от имени тенанта, под его квотами и анти-баном.
- Исходящий поток сообщений помечен `tenantId` (источник определён на входе, в сервисе).
- Сервисная аутентификация (mTLS) + аудит команд `(tenantId, действие, диалог, результат)`.
- Один аккаунт на тенанта на старте (связь тенант→аккаунты уже таблицей — расширение позже).
### 7.2 ml-service
- .NET + ONNX (не ML.NET для онлайн-обучения): пул моделей по тенантам, обучение на
реальных действиях пользователя и результатах ИИ.
- Ничего не знает о домене: получает текст, отдаёт решение; обучение — по контракту.
- Экспорт/импорт моделей — по контракту (для переноса между инстансами).
### 7.3 ai-service
- Фасад LLM-провайдеров (DeepSeek и др., включая локальные OpenAI-совместимые),
библиотека промптов, классификация, генерация.
- **Учёт токенов**: каждый вызов оценивается в токенах и списывается с бюджета тенанта.
- При исчерпании бюджета — fallback на ML/локальную обработку + уведомление
(приём сообщений не блокируется).
---
## 8. Безопасность
### Слой приложения (core)
- SQL-инъекции: запрет конкатенации SQL; только параметризация (EF Core/Dapper);
анализаторы; Postgres-роль без DDL.
- Tenant-изоляция (IDOR): tenantId из сессии; проверка принадлежности; тесты.
- Аутентификация: Argon2id, лимит попыток, одноразовые инвайты с expiry.
- Сессии: httpOnly cookie + CSRF (не localStorage).
- XSS: экранирование на фронте (renderSourceMessage), CSP, запрет v-html без санитайзера.
- SSRF: ai/telegram не тянут произвольные URL от имени тенанта (allowlist).
- Валидация входа: DTO + FluentValidation, лимиты размеров.
- Аудит: входы, инвайты, impersonation, действия оператора — неизменяемый поток.
### Транспорт/сервисы
- TLS везде; mTLS между сервисами; service-token второй фактор.
### Инфраструктура
- Cloudflare (DDoS/WAF) → reverse proxy (TLS, rate limit по IP, security-заголовки).
- Rate limiting в приложении по тенанту (защита от «шумного соседа»).
- Docker: сервисы в изолированной сети, наружу — только прокси; non-root, read-only FS.
- Секреты: env/secret-хранилище; шифрование (enc); ничего в коде/репозитории.
### Процессы
- CI: сканирование зависимостей (NuGet/npm), SAST, trivy-скан образов.
- Обновления и алерты на CVE.
- Postgres: бэкапы ежедневные, тест восстановления.
---
## 9. Наблюдаемость, админка, бэкапы
### Observability
- Serilog (структурированные логи) + OpenTelemetry (метрики/трейсы) → Promtail → **Grafana + Loki**.
- Дашборды: health сервисов, pipeline, ML-качество, расход токенов по тенантам.
- За абстракцией экспорта — смена стека без правки кода.
### Операторская админка (только оператору)
- Создание тенантов и инвайтов, лимиты, health, impersonation (с полным аудитом),
подозрительная активность. Отдельный защищённый вход (оператор ≠ тенант).
### Бэкапы
- Ежедневно: Postgres (pg_dump), файлы MinIO, сессии telegram.
- Retention и внешняя выгрузка — уточнить на этапе деплоя.
---
## 10. Деплой
- docker compose на одном VPS: core, ml-service, ai-service, telegram-service,
postgres, minio, grafana/loki/promtail, reverse proxy.
- Сервисы compose = будущие k8s-деплойменты (никаких завязок на compose в коде).
- Миграции схем тенантов: механизм «миграция ко всем схемам» (список схем в `public`,
применение по очереди, версия миграции на схему) — детализировать в плане реализации.
---
## 11. Стандарты кода
- Код-стайл: `C:\telbase\Стиль_кода.docx` + согласованные адаптации
(без snake_case-хелперов и регионов, public-поля → свойства, XML-doc для public-контрактов,
настройки через `IOptions<T>`, комментарии на русском).
- 1 тип = 1 файл (класс/record/struct/enum/interface — отдельный файл).
- `.editorconfig` + Roslyn-анализаторы с ошибками на нарушения.
- Второй слой правил: скилы `agent-rules-books` (Clean Code, DDD, DDIA).
- .NET-эталоны: скил `dotnet-clean-architecture-skills` (адаптировать под проект).
---
## 12. Открытые вопросы / следующие шаги
1. **Карта `/api`**: снять точную спецификацию с работающего LeadRadar (отдельная задача).
2. **Детали лимитов**: механика «бюджет токенов» (период, пороги, уведомления) — спроектировать.
3. **Бэкапы**: точная схема retention/внешнего хранилища.
4. **Миграции на 1000 схем**: детальный механизм.
5. Порядок реализации: этап 0 (каркас) → Pipeline+Kanban → ai/ml/telegram → Projects/Discovery.
---
## Приложение: глоссарий
- **Тенант** — клиент SaaS (одна организация/пользователь), владеет схемой БД и настройками.
- **Канал/источник** — Telegram-канал/группа, который слушает аккаунт тенанта.
- **Карточка** — структурированная заявка (заказ/вакансия), созданная пайплайном.
- **Outbox** — паттерн надёжной доставки событий через таблицу в той же транзакции.