Архитектура проекта
Документ описывает, как устроена система (раздел «Архитектура ПО»), и как разложен репозиторий и деплой (последующие разделы). Соглашения для контрибьюторов и проверяемые гейты — в CONTRIBUTING.md.
Архитектура ПО
Заголовок раздела «Архитектура ПО»Рантайм-процессы
Заголовок раздела «Рантайм-процессы»Приложение запускается двумя процессами, разделяющими одну проводку:
- backend (
main_backend.py) — aiohttp: HTTP API Mini App и админки, Telegram-вебхук, вебхуки платёжных провайдеров и панели. Поднимает два aiohttp-приложения на разных портах: вебхуки и Subscription WebApp (Mini App +/api/*). - worker (
main_worker.py) — фоновые задачи: тарифный воркер, синхронизация с панелью, обработчики очереди вебхуков.
Оба процесса вызывают run_setup(ctx) и register_core_reactions(ctx) — то есть оба
слушают шину доменных событий и активируют плагины. Любое изменение событийного слоя должно
одинаково работать в обоих.
Слои и поток запроса
Заголовок раздела «Слои и поток запроса»- Роуты регистрируются явно (
setup_subscription_webapp_routes,setup_admin_routes) и плагинами в рантайме (Plugin.setup_web). - DAL (
db/dal) — единственная точка доступа к БД; сервисы не пишут SQL мимо него. - Сервисы (
bot/services) держат бизнес-логику и публикуют события в ключевых точках.
Четыре типизированных контракта
Заголовок раздела «Четыре типизированных контракта»Архитектура держится на четырёх явных, машинопроверяемых контрактах — это единый источник правды:
- HTTP API — pydantic request/response-модели + реестр
route_contracts, из которого генерируетсяopenapi.json. Подробно: architecture/http-api.md. - Шина доменных событий — одна pydantic-модель на событие (
bot/infra/event_payloads.py), публикация черезemit_model; payload — flat-dict из примитивов. Каталог: architecture/events.md. - Плагины — ABC
Plugin+PluginContext, обнаружение через entry-point группуminishop.plugins. Контракт: development/plugin-contract.md. - Remnawave API — типизированный реестр outbound-операций, webhook-контрактов, поколений API и точных сертифицированных версий. Каталог: architecture/remnawave-api-compatibility.md.
Расширяемость
Заголовок раздела «Расширяемость»Внешний код расширяет приложение через плагины (отдельные пакеты, entry points), не форкая ядро:
HTTP-роуты (setup_web), aiogram-роутеры (setup_bot), фоновые задачи, обработчики очереди,
миграции (неймспейснутые цепочки), локали, провайдер entitlements. Встроенные плагины активны
всегда; внешние гейтятся PLUGINS_ENABLED.
Контракт фронт↔бэк
Заголовок раздела «Контракт фронт↔бэк»OpenAPI-спек (docs/openapi.json) генерируется из живого роутера, из него генерируются
TypeScript-типы фронта (frontend/src/lib/api/openapi.generated.ts), а типизированный клиент
publicApi.ts выводит формы запроса/ответа по пути вызова. Оба артефакта защищены drift-guard
в CI — изменение контракта на бэке, не отражённое во фронте, валит сборку.
Раскладка репозитория
Заголовок раздела «Раскладка репозитория»Репозиторий разделён по зонам ответственности рантайма:
Основной docker-compose.yml находится в корне репозитория, чтобы docker compose up оставался простым продакшен-путем. Он собирает три прикладных образа из deploy/docker/Dockerfile:
backend: aiohttp API и вебхуки.worker: worker тарифов, синхронизация с панелью, обработчики очередей вебхуков.frontend: статические Svelte-ассеты, которые отдает nginx.
Mini App и админка собираются как ES-модули с code splitting: страница подключает
точку входа через <script type="module">, а остальные куски (subscription_webapp.<name>.<hash>.js)
подгружаются по мере надобности. Экраны, кроме главного и настроек, импортируются динамически
(lib/webapp/lazyScreen.svelte.ts), поэтому редактор сообщений с раздела поддержки не попадает
в первую загрузку. Имена кусков content-hashed и раздаются с immutable: и aiohttp-роут, и
nginx-конфиг, и подготовка ассетов совпадают по шаблону имени - расхождение означает 404 на
куске и экран, который не открывается.
Сервис migrate - одноразовый контейнер на базе backend-образа. Он входит в стандартный Compose-граф: Postgres и Redis переходят в healthy-состояние, migrate применяет Base.metadata.create_all и ожидающие schema_migrations, а затем backend и worker стартуют только после успешного завершения migrate. Так миграции остаются автоматическими для docker compose up, но не запускаются внутри каждой backend-реплики.
Python-импорты намеренно остаются в пространствах bot.*, config.* и db.*. Контейнеры рантайма выставляют PYTHONPATH=/app/backend; локальные тесты используют такую же раскладку через pytest.ini.
Основные команды: