501просмотров
32.7%от подписчиков
16 февраля 2026 г.
📷 ФотоScore: 551
🤖 Архитектура Telegram‑бота: структура проекта, чистый код и тесты (чтобы не превратить бота в “лапшу”) Если бот живёт дольше 2–3 дней, в нём неизбежно появляется: “быстро поправлю тут”, “временно вот так”, “потом разберусь”. Через месяц это уже не бот, а квест.
Ниже - практичный шаблон, который помогает держать код чистым и масштабировать проект без боли. 1️⃣ Главный принцип: разделяй “что бот делает” и “как он общается с Telegram”
❌ Плохой знак: в хендлере 200 строк 0-и там же запросы в БД, логика, тексты, API, платежи.
✅ Хороший знак: хендлер - это тонкий слой: принял апдейт → вызвал сервис → отдал ответ.
Мини‑правило:
- Handlers (Delivery layer) - только роутинг/валидация/ответы
- Services (Use cases) - бизнес‑логика
- Repositories (Data access) - БД/файлы/кэш
- Clients (Integrations) - внешние API
- DTO/Schema - типы входов/выходов между слоями 2️⃣Пример структуры проекта (универсально)
Подойдёт и для aiogram, и для других фреймворков:
bot/ app/ # точка входа, настройка приложения main.py config.py logging.py handlers/ # обработчики команд/сообщений/кнопок start.py orders.py keyboards/ # кнопки/меню main.py texts/ # тексты/шаблоны сообщений ru.py services/ # бизнес-логика (use cases) order_service.py user_service.py repositories/ # доступ к данным user_repo.py order_repo.py clients/ # внешние интеграции payments.py crm.py models/ # модели домена / ORM user.py order.py schemas/ # DTO / валидация (pydantic и т.п.) order.py utils/ # утилиты time.py formatters.py tests/ test_order_service.py 3️⃣Чистые хендлеры: пример “как должно быть”
Хендлер - короткий, читабельный, без “магии”:
# handlers/orders.py
async def create_order(message, order_service): user_id = message.from_user.id text = message.text order = await order_service.create_order(user_id=user_id, raw_text=text) await message.answer(f"Заявка #{order.id} принята") А вся логика - в сервисе:
# services/order_service.py
class OrderService: def init(self, order_repo, anti_spam): self.order_repo = order_repo self.anti_spam = anti_spam async def create_order(self, user_id: int, raw_text: str): self.anti_spam.check(user_id) parsed = self._parse(raw_text) return await self.order_repo.create(user_id=user_id, **parsed) def _parse(self, raw_text: str) -> dict: # парсинг/валидация/нормализация return {"title": raw_text.strip()[:120]} Почему это важно: сервисы проще тестировать, переиспользовать и менять. 4️⃣ Best practices, которые реально экономят время
Конфиг - только через переменные окружения
Токены, ключи, URL - не в коде. Делай config.py, который читает env.
Логи вместо print
Логи должны отвечать на 3 вопроса:
- что произошло?
- кого (user_id/чат)?
- чем закончилось (успех/ошибка)?
Тексты отдельно от логики
Сегодня “Привет”, завтра - “Здравствуйте”, послезавтра - 2 языка. Если тексты смешаны с кодом - боль гарантирована.
Единый стиль ошибок
Определи свои исключения: ValidationError, NotFound, ExternalApiError — и в одном месте решай, как отвечать пользователю. 5️⃣ Тесты: что тестировать в боте в первую очередь
Тестировать Telegram API “в лоб” часто тяжело и не нужно. Тестируй бизнес‑логику (services):
- парсинг и валидация входных данных
- расчёты, условия, ограничения
- работа с репозиториями через моки
- сценарии ошибок (API упало, лимит превышен, пользователь не найден) 6️⃣Чек‑лист “бот готов к росту” Хендлеры тонкие, логика в сервисах Есть единая структура папок Конфиг через env, секретов в репо нет Тексты вынесены отдельно Логи есть, print нет Есть тесты хотя бы на ключевые сервисы Интеграции изолированы в clients/
Хотите бота без боли - купите бота у меня.
Пишите в личку: “Хочу бота” - и коротко опишите задачу (для чего бот и что он должен