Skip to content

Архитектурный аудит: неконсистентности, дублирование и потенциальные проблемы подсистем #459

@shikhalev

Description

@shikhalev

Архитектурный аудит подсистем

Комплексный анализ связей между подсистемами проекта. Выявлены проблемы неконсистентности, дублирования кода, потенциальной несовместимости и неясной логики взаимодействия.


1. Бэкенд: двойные пути доступа к сервисам

1.1 Глобалы + ServiceContainer одновременно

orchestrator.py инициализирует сервисы двумя способами: сначала как глобальные переменные (строки 401–476), затем копирует их в ServiceContainer (строки 1024–1034). Разные роутеры используют разные пути доступа:

Сервис Глобал Container Кто использует
llm_service orchestrator.py:463 container.llm_service health_check (оба!), llm.py (service_manager)
voice_service orchestrator.py:463 container.voice_service tts.py (container), health_check (глобал)
piper_service orchestrator.py:466 container.piper_service tts.py (container)
stt_service orchestrator.py:468 Только глобал

Проблема: Health-check демонстрирует недоверие к обоим путям (orchestrator.py:1172):

current_llm_service = container.llm_service if container.llm_service else llm_service

Нет механизма синхронизации при горячей замене сервиса (например, смена LLM-бэкенда через API) — обновление одного пути не гарантирует обновления другого.

1.2 Три слоя сервисов без чёткого разделения

Слой Расположение Примеры Принцип?
AI/Runtime Корень проекта cloud_llm_service.py, voice_clone_service.py, multi_bot_manager.py, bridge_manager.py Нет
Domain app/services/ amocrm_service.py, wiki_rag_service.py, gdpr_service.py, sales_funnel.py Нет
Data db/integration.py 28 Async*Manager классов Обёртки над репозиториями

Неконсистентность: orchestrator.py импортирует GSMService из app/services/ (строка 1039), но CloudLLMService — из корня. Нет документированного принципа, определяющего в каком слое размещать новый сервис.

1.3 Два фреймворка Telegram в одном проекте

Фреймворк Где используется Для чего
python-telegram-bot telegram_bot_service.py, app/services/sales_funnel.py Legacy single-bot сервис
aiogram 3.x telegram_bot/handlers/, telegram_bot/sales/ Новый multi-instance бот

Дублирование: Sales-воронка реализована дважды:

  • Legacy: app/services/sales_funnel.py (imports from telegram.ext import ContextTypes)
  • Новый: telegram_bot/handlers/sales/ (imports from aiogram)

Оба пакета экспортируют типы с одинаковыми именами (Update, Message, InlineKeyboardButton), что потенциально создаёт конфликт импортов.


2. База данных: безопасность workspace-изоляции

2.1 Обход workspace-фильтра в FAQ

db/repositories/faq.py:

  • find_answer() (строка 61) — нет параметра workspace_id → возвращает ответы из всех workspace
  • get_by_question() (строка 81) — аналогично, без фильтра
  • delete_by_question() (строка 158) — workspace_id=None удаляет FAQ из всех workspace

Риск: пользователь Workspace 1 может получить FAQ-ответ из Workspace 2.

2.2 Глобальные роли без workspace-привязки

db/repositories/role.py — ни один метод не принимает workspace_id. Если роли должны быть workspace-scoped, это дыра в изоляции.

2.3 Redis-кеш без workspace в ключах

db/redis_client.py — ключи кеша глобальные:

key = f"{CacheKey.CHAT_SESSION}:{session_id}"   # Нет workspace_id
key = f"{CacheKey.FAQ}:{...}"                     # Нет workspace_id

FAQ кешируется только при workspace_id=None (faq.py:36-59), но если два workspace запросят FAQ без фильтра — получат общий кеш.

2.4 redis.keys() в production

db/redis_client.py:159cache_delete_pattern() использует await redis_client.keys(pattern), что блокирует весь Redis на больших БД. Нужен .scan().

2.5 SQLite: нет WAL-режима

db/database.py:27-33 — отсутствуют PRAGMA:

PRAGMA journal_mode=WAL;     -- для параллельных чтения/записи
PRAGMA busy_timeout=5000;    -- для избежания SQLITE_BUSY
PRAGMA synchronous=NORMAL;   -- производительность без потери надёжности

При 10+ одновременных пользователях — риск SQLITE_BUSY ошибок.


3. Монолитные god-файлы

Файл Строк Проблема
db/models.py 3,636 55 моделей в одном файле: auth, chat, bot, CRM, kanban, GSM — всё вместе
db/integration.py 2,422 28 Async*Manager классов — тонкие обёртки без добавленной логики
orchestrator.py ~4,100 Инициализация, 100+ legacy-эндпоинтов, глобалы, startup/shutdown
admin/src/views/ChatView.vue 3,201 Чат, бранчинг, артефакты, стриминг, RAG, TTS, STT, Claude Code
admin/src/views/TelegramView.vue 2,603 9 табов, формы настроек, sales-конфигурация
admin/src/plugins/i18n.ts ~3,400 Все 3 языка в одном файле

3.1 integration.py: бессмысленные обёртки

Каждый Async*Manager создаёт сессию → инстанцирует репозиторий → вызывает тот же метод → возвращает результат. Нулевая добавленная логика:

# db/integration.py:125-140
async def list_sessions(self, owner_id=None, ...):
    async with AsyncSessionLocal() as session:
        repo = ChatRepository(session)
        return await repo.list_sessions(owner_id=owner_id, ...)

При этом ряд роутеров (bot_sales.py) уже обходят менеджеры и работают с репозиториями напрямую — два паттерна в одном проекте.

3.2 Транзакционная целостность нарушена

integration.py создаёт отдельную сессию на каждый вызов метода. Невозможно выполнить несколько операций в одной транзакции:

await chat_mgr.create_session(...)  # Сессия 1, auto-commit
await chat_mgr.add_message(...)     # Сессия 2, отдельная транзакция
# Если add_message упадёт — create_session уже закоммичен

4. Дублирование бот-подсистем (Telegram ↔ WhatsApp)

4.1 Дублирование текстов (98%)

Файл Строк Дублирование
telegram_bot/sales/texts.py 467 Оригинал
whatsapp_bot/sales/texts.py 445 98% копия, разница: **bold***bold*

Каждое исправление текста требует правки двух файлов. Возможна рассинхронизация.

4.2 Дублирование менеджеров (90%)

Файл Строк Структура
multi_bot_manager.py 346 BotProcess + MultiBotManager
whatsapp_manager.py 324 WhatsAppProcess + WhatsAppManager

Классы практически идентичны: единственные отличия — имя модуля (telegram_bot / whatsapp_bot), путь к логам, и способ загрузки конфига.

4.3 Несовместимость паттернов конфигурации

Аспект Telegram WhatsApp
Загрузка конфига Pre-cache в /tmp/bot_config_{id}.json Запрос к БД при каждом старте
Состояние FSM (aiogram) Колонка funnel_state в отдельной SQLite
БД Основная SQLite (SQLAlchemy) Изолированная SQLite (data/wa_sales_{id}.db)
user_id INTEGER (Telegram ID) TEXT (телефон)

Проблемы:

  • WhatsApp бот не может стартовать, если основная БД недоступна (нет кеша)
  • Данные WhatsApp-воронки не видны в общей аналитике (отдельная БД)
  • Telegram имеет 7 таблиц (включая payments, testimonials), WhatsApp — только 3

4.4 Дублирование клавиатур (70%)

telegram_bot/sales/keyboards.py (959 строк) и whatsapp_bot/sales/keyboards.py (922 строки) используют одинаковые callback ID (sales:start_quiz, faq:what_is), но разные UI-примитивы (InlineKeyboard vs quick-reply/list). Изменение ID требует правки в 4 местах (keyboards + handlers × 2 бота).

4.5 Правильное переиспользование (подтверждено)

  • telegram_bot/sales/segments.py — единственный источник сегментации, WhatsApp корректно импортирует
  • telegram_bot/services/llm_router.py — WhatsApp корректно использует через from telegram_bot.services.llm_router import LLMRouter

5. Фронтенд: несогласованность и пробелы

5.1 Прямые fetch() в обход API-клиента

3 места делают fetch() напрямую, минуя api клиент из client.ts:

Файл Строка Эндпоинт
stores/auth.ts 62 GET /admin/auth/permissions
views/AuditView.vue GET /admin/audit/export
views/SettingsView.vue GET /admin/auth/profile, POST /change-password

Последствия: эти вызовы не работают в demo-режиме (не перехватываются setupDemoInterceptor), не участвуют в централизованной обработке ошибок.

5.2 Demo-режим рассинхронизирован с реальным API

  • Demo-типы определены отдельно от основных (demo/store.ts vs api/telegram.ts)
  • Пример: реальный API использует knowledge_collection_ids (массив), demo — knowledge_collection_id (единственное число)
  • Нет автоматической проверки, что mock-данные соответствуют реальным типам
  • Новые поля в API не попадают в demo, пока кто-то не заметит расхождение

5.3 Неполная i18n для казахского языка

Язык Ключей Покрытие
Русский (ru) ~845 100%
English (en) ~844 ~99% (7 строк — кириллица по ошибке)
Қазақша (kk) ~244 29%

71% казахских переводов отсутствует. Целые секции (kanban, CRM, WooCommerce) не переведены.

5.4 God-компоненты на фронтенде

ChatView.vue (3,201 строк) совмещает: чат, бранчинг, файлы, артефакты, стриминг, шаринг, Claude Code, RAG, TTS/STT, zen-mode. 60+ реактивных переменных. Должен быть разделён на 5-6 компонентов.

TelegramView.vue (2,603 строки) — 9 табов с формами, нетипизированный salesForm: ref<Record<string, any>>({}).


6. Миграции: две параллельные системы

Текущее состояние

Система Файлов Когда используется
Alembic 16 миграций Новые schema changes (roles, workspaces, kanban, resource_shares)
scripts/migrate_*.py 25 скриптов Старые features + data migrations

Нет документации, определяющей:

  • Когда использовать Alembic vs ручной скрипт
  • Какие таблицы покрыты какой системой
  • Как проверить, что обе системы не конфликтуют

Потенциальный конфликт

Новый разработчик может создать и Alembic-миграцию, и ручной скрипт для одной и той же таблицы. Base.metadata.create_all на старте сервера дополнительно создаёт недостающие таблицы — третий путь, который может маскировать пропущенные миграции.


7. Сводная таблица по приоритетам

Критические (безопасность)

# Проблема Файл
1 FAQ find_answer() без workspace-фильтра db/repositories/faq.py:61
2 FAQ get_by_question() без workspace-фильтра db/repositories/faq.py:81
3 FAQ delete_by_question() может удалить из всех workspace db/repositories/faq.py:158
4 Redis-кеш без workspace в ключах db/redis_client.py

Высокие (архитектура, надёжность)

# Проблема Файл
5 SQLite без WAL и busy_timeout db/database.py:27-33
6 Двойной путь доступа к сервисам (global + container) orchestrator.py
7 Транзакционная целостность нарушена в integration.py db/integration.py
8 Два Telegram-фреймворка с дублированием sales-логики telegram_bot_service.py + telegram_bot/
9 God-файлы: models.py (3.6K), integration.py (2.4K), orchestrator.py (4.1K) db/
10 WhatsApp бот на изолированной БД (data не видна в аналитике) whatsapp_bot/sales/database.py

Средние (качество кода, поддерживаемость)

# Проблема Файл
11 98% дублирование текстов Telegram ↔ WhatsApp */sales/texts.py
12 90% дублирование bot-менеджеров multi_bot_manager.py + whatsapp_manager.py
13 Demo-режим рассинхронизирован с реальным API admin/src/api/demo/
14 Прямые fetch() в обход API-клиента (3 места) auth.ts, AuditView.vue, SettingsView.vue
15 i18n: 71% казахских переводов отсутствует admin/src/plugins/i18n.ts
16 God-компоненты: ChatView (3.2K), TelegramView (2.6K) admin/src/views/
17 redis.keys() вместо .scan() db/redis_client.py:159
18 Две системы миграций без документированного разделения alembic/ + scripts/migrate_*.py
19 Несовместимые паттерны конфигурации ботов multi_bot_manager.py vs whatsapp_manager.py
20 Inconsistent flush/commit в BaseRepository db/repositories/base.py:54-70

Рекомендуемый порядок действий

Немедленно (1-2 дня)

  1. Добавить workspace_id в find_answer() и get_by_question() в FAQ
  2. Включить WAL-режим и busy_timeout для SQLite
  3. Заменить redis.keys() на redis.scan() в cache_delete_pattern()

Короткосрочно (1 спринт)

  1. Удалить legacy telegram_bot_service.py и app/services/sales_funnel.py
  2. Вынести общие тексты ботов в shared/sales/texts.py с форматтером
  3. Создать BaseBotManager для Telegram и WhatsApp менеджеров
  4. Перевести прямые fetch() на централизованный API-клиент
  5. Привести Redis-ключи к workspace-aware формату

Среднесрочно (2-3 спринта)

  1. Разделить db/models.py на доменные файлы
  2. Устранить менеджеры-обёртки из db/integration.py, перейти на прямое использование репозиториев
  3. Консолидировать ServiceContainer как единственный путь доступа к сервисам
  4. Разделить god-компоненты фронтенда
  5. Документировать стратегию миграций (Alembic vs scripts)
  6. Добавить автотесты совместимости demo ↔ real API

Отчёт сгенерирован автоматическим анализом кодовой базы. Все ссылки на строки актуальны на момент создания.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High priorityphase:5-techdebtPhase 5: Technical Debt

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions