# Ресёрч: aiogram 3.x Architecture Review > **Дата:** 2026-03-30 > **Вердикт:** APPROVED с двумя уточнениями --- ## 1. Структура проекта **Официальный пример multi_file_bot:** ``` multi_file_bot/ bot.py handlers/ common.py ... ``` **Best practice для средних проектов (наш случай):** ``` adapter/telegram/ bot.py ← Dispatcher + include_routers + polling/webhook converter.py ← граница aiogram ↔ core/ states.py ← все StatesGroup handlers/ ← по одному Router на модуль keyboards/ ← InlineKeyboardBuilder фабрики middleware.py ← DI + logging + rate limit ``` **Оценка:** наша структура соответствует стандарту. ✓ --- ## 2. Middleware vs Converter В aiogram 3.x эти два паттерна решают **разные задачи** и должны использоваться вместе. | | Middleware | Converter | |---|---|---| | Назначение | Infrastructure | Бизнес-логика | | Что делает | Логирование, DI, rate limit, сессия БД | aiogram Event → IncomingEvent | | Когда вызывается | До и после хендлера | Внутри хендлера | **Правильная комбинация:** ```python # middleware.py — только infrastructure class DependencyMiddleware(BaseMiddleware): def __init__(self, platform, store): self.platform = platform self.store = store async def __call__(self, handler, event, data): data["platform"] = self.platform data["store"] = self.store return await handler(event, data) # handler — converter вызывается внутри async def handle_message(message: Message, platform, store): event = to_incoming_message(message) # converter results = await dispatcher.dispatch(event, platform, store) await send_results(message, results) # converter обратно ``` **Оценка:** наш converter.py — правильный паттерн. Добавить `middleware.py` для DI. ✓+ --- ## 3. Dependency Injection Стандарт aiogram 3.x — **через middleware + data dict**: ```python # Регистрация в bot.py dp.message.middleware(DependencyMiddleware(platform=platform_client, store=store)) # Получение в handler (через type hint на имя ключа) async def handle_message(message: Message, platform: PlatformClient, store: StateStore): ... ``` Альтернатива — через `dp["key"] = value` (Dispatcher workflow data): ```python dp["platform"] = platform_client # в bot.py async def handler(message: Message, platform: PlatformClient): # aiogram сам находит по типу ... ``` **Оценка:** нужно явно добавить один из этих механизмов, иначе хендлеры не получат platform/store. ⚠️ --- ## 4. InlineKeyboardBuilder `InlineKeyboardBuilder` — рекомендуемый подход в aiogram 3.x. `InlineKeyboardMarkup` с вложенными списками считается устаревшим стилем. ```python # keyboards/chat.py from aiogram.utils.keyboard import InlineKeyboardBuilder def chats_keyboard(chats: list[ChatContext]) -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() for chat in chats: builder.button(text=f"💬 {chat.name}", callback_data=f"chat:{chat.chat_id}") builder.button(text="➕ Новый чат", callback_data="new_chat") builder.adjust(1) # одна кнопка в строку return builder.as_markup() ``` **Оценка:** использовать `InlineKeyboardBuilder` везде. ✓ --- ## 5. F-фильтры (MagicFilter) aiogram 3.x MagicFilter (`F`) — стандарт вместо ручных проверок в хендлерах: ```python from aiogram import F # Вместо if message.text == "/start" внутри хендлера router.message.register(start_handler, Command("start")) # Фильтр по типу вложения router.message.register(voice_handler, F.voice) router.message.register(photo_handler, F.photo) # Фильтр по состоянию router.message.register(handle_name_input, OnboardingState.waiting_for_name) # Callback фильтр router.callback_query.register(confirm_handler, F.data.startswith("confirm:")) ``` **Оценка:** использовать F-фильтры при регистрации роутеров — чище, чем if/else в хендлерах. ✓ --- ## 6. Сцены (Scenes) — новинка aiogram 3.x aiogram 3.4+ ввёл `Scene` как улучшенный FSM для сложных диалогов: ```python from aiogram.fsm.scene import Scene, on class OnboardingScene(Scene, state="onboarding"): @on.message.enter() async def on_enter(self, message: Message): await message.answer("Как зовут твоего агента?") @on.message() async def on_name(self, message: Message, state: FSMContext): await state.update_data(agent_name=message.text) await self.wizard.goto(OnboardingScene2) ``` **Оценка:** Scenes — опциональное улучшение для онбординга. Классический FSM через StatesGroup тоже корректен и проще для понимания. Использовать StatesGroup для прототипа, Scenes — в будущем. ✓ --- ## Итог | Решение | Статус | |---|---| | Router-based архитектура, один Router на модуль | ✅ Стандарт | | converter.py как граница aiogram ↔ core/ | ✅ Правильный паттерн | | InlineKeyboardBuilder в keyboards/ | ✅ Рекомендуется | | SQLiteStorage для FSM | ✅ Стандарт для MVP | | **Нужно добавить: DependencyMiddleware** | ⚠️ DI без него не работает | | **Нужно добавить: F-фильтры при регистрации** | ⚠️ Иначе проверки в хендлерах | **Архитектура одобрена.** Два уточнения (middleware.py и F-фильтры) небольшие и органично вписываются в текущую структуру.