Base Orchestrator — Фундамент бизнес-логики
BaseBotOrchestrator — это "сердце" каждой фичи. Если Директор управляет навигацией между комнатами, то Оркестратор — это хозяин комнаты, который решает, что пользователь увидит внутри.
🏛 Архитектура без состояния (Stateless Singletons)
Во многих фреймворках объекты логики создаются под каждого пользователя. В codex-bot всё иначе:
1. Синглон: Оркестратор создается в единственном экземпляре при старте бота.
2. Stateless: Он не имеет права хранить self.user_data. Если вы запишете что-то в self одного оркестратора, это увидят все пользователи бота.
3. Context-Driven: Все данные, специфичные для запроса, приходят «снаружи» через объект Director.
Это делает систему экстремально производительной и легкой в отладке.
✍️ Анатомия методов (Сигнатуры)
Мы придерживаемся строгого стандарта: Контекст всегда важнее Данных. Поэтому во всех методах Director идет первым аргументом.
1. render_content (Абстрактный)
Это единственное место, где вы пишете код формирования сообщения. * director: Доступ к БД, API, ID пользователя. * payload: Очищенные данные (уже без метаданных навигации).
2. handle_entry
Точка входа, которую вызывает Директор. По умолчанию она просто вызывает render, но вы можете переопределить её, если при первом заходе в фичу нужно сделать что-то особенное (например, отправить аналитику или обнулить какой-то стейт).
🏗 Жизненный цикл данных
Процесс превращения "сырых данных" в сообщение в Telegram выглядит так:
- Оркестратор выполняет бизнес-логику (запросы к БД, расчеты).
- Оркестратор возвращает
ViewResultDTO(только текст и кнопки). - Базовый класс ловит этот DTO и оборачивает его в
UnifiedViewDTO(добавляет пустые поля для меню). - Director подхватывает этот конверт и заполняет технические поля:
chat_id,session_key. - ViewSender получает финальный, полностью укомплектованный
UnifiedViewDTOи выполняет отрисовку.
💡 Типизация и Безопасность
Фреймворк использует Python Generics для защиты ваших данных. При создании оркестратора всегда указывайте тип данных, которые он ожидает:
class ProfileOrchestrator(BaseBotOrchestrator[UserProfileDTO]):
async def render_content(self, director: Director, payload: UserProfileDTO | None = None):
# IDE теперь знает, что у payload есть поля name, balance и т.д.
return self.ui.show_card(payload)
Использование | None обязательно, так как фича может быть вызвана из главного меню вообще без данных. Фреймворк гарантирует, что вы об этом не забудете на этапе написания кода.