Files
elmprodvpn/docs/phase-f/F1_REFACTOR_MODULARITY_PLAN.md

141 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# F1 План рефакторинга и модульности
Дата: 2026-03-07
Статус: planned
Владелец: Engineering
## 1) Цель
- Уменьшить размер и связность крупных файлов без изменения поведения приложения.
- Подготовить кодовую базу к быстрому развитию desktop/web/mobile поверх одного Go API.
## 2) Текущие "горячие" файлы
- `selective-vpn-gui/vpn_dashboard_qt.py``6103` строк (до старта F1.4; после текущего разреза: `116` + `main_window/ui_shell_mixin.py` + `main_window/singbox/*` + `main_window/runtime_actions_mixin.py`).
- `selective-vpn-gui/api_client.py``2220` строк.
- `selective-vpn-gui/dashboard_controller.py``1430` строк.
- `selective-vpn-api/app/transport_handlers.go``1984` строки.
- `selective-vpn-api/app/server.go` (legacy) — центральная связка entrypoints/bootstrap/routes, высокий риск регрессий при точечных изменениях.
## 3) Принципы разрезания
- Поведение не меняем: сначала вынос кода, потом точечные улучшения.
- Модули режем по domain-границам, а не "по 500 строк".
- Сохраняем один публичный entrypoint (facade) для UI и для backend-router.
- После каждого шага прогоняем существующие smoke/e2e (без новых требований к тестам от пользователя).
## 4) План разрезания GUI
### 4.1 `vpn_dashboard_qt.py` -> `gui/main_window/*`
- Цель: вынести табы и event-обвязку в отдельные модули.
- Разделение:
- `main_window.py` — сборка окна и общий lifecycle.
- `tabs/status_tab.py`
- `tabs/vpn_tab.py`
- `tabs/routes_tab.py`
- `tabs/dns_tab.py`
- `tabs/domains_tab.py`
- `tabs/trace_tab.py`
- `services/events_bridge.py` (`EventThread`, dispatch)
- `services/locations_loader.py` (`LocationsThread`, SWR-ui orchestration)
- Критерий: исходный файл `vpn_dashboard_qt.py` становится thin-bootstrap.
- Статус: выполнено (`2026-03-10`).
- Уже сделано:
- добавлены `main_window/constants.py` и `main_window/workers.py` (shared constants + `EventThread`/`LocationsThread`);
- вынесен UI shell (`build tabs + helpers + locations/egress`) в `main_window/ui_shell_mixin.py`;
- `ui_tabs_singbox_mixin.py` дополнительно разрезан на `ui_tabs_singbox_layout_mixin.py` и `ui_tabs_singbox_editor_mixin.py`;
- SingBox-контур дополнительно разрезан на подпакет `main_window/singbox/*` (`editor`, `cards`, `links`, `runtime`) с фасадом `main_window/singbox_mixin.py`;
- вынесен runtime/refresh/actions контур в `main_window/runtime_actions_mixin.py`;
- проведён второй проход декомпозиции: `ui_tabs_*`, `runtime_{state,refresh,auth,ops}`, `singbox/{links_*,runtime_*}`; фасады оставлены для совместимости;
- `MainWindow` переведён на наследование mixin-классов, поведение сохранено; в итоге крупные GUI-модули уложены в читаемые размеры (максимум `~633` строк).
### 4.2 `dashboard_controller.py` -> `controllers/*`
- Разделение:
- `controllers/status_controller.py`
- `controllers/vpn_controller.py`
- `controllers/routes_controller.py`
- `controllers/dns_controller.py`
- `controllers/domains_controller.py`
- `controllers/transport_controller.py`
- `controllers/trace_controller.py`
- Сохраняется фасад `DashboardController` (совместимость с текущим UI-кодом).
- Статус: выполнено (`2026-03-10`).
- Что сделано:
- добавлен пакет `selective-vpn-gui/controllers/` (`core + views + domain mixin-модули`);
- `selective-vpn-gui/dashboard_controller.py` переведён в thin-facade с прежней точкой входа;
- `py_compile` и импорты UI-модулей (`vpn_dashboard_qt.py`, `traffic_mode_dialog.py`, `dns_benchmark_dialog.py`) проходят.
### 4.3 `api_client.py` -> `api/*`
- Разделение:
- `api/client.py` (HTTP/SSE base + shared helpers)
- `api/models.py` (dataclasses)
- `api/status.py`, `api/vpn.py`, `api/routes.py`, `api/traffic.py`, `api/dns.py`, `api/domains.py`, `api/trace.py`
- `api/transport.py` facade + `api/transport_clients.py`, `api/transport_policy.py`, `api/transport_singbox.py`
- Сохраняется фасад `ApiClient` (старая точка импорта для UI).
- Статус: выполнено (`2026-03-10`).
- Что сделано:
- добавлен пакет `selective-vpn-gui/api/` с доменными mixin-модулями;
- `api_client.py` переведён в backward-compatible facade (legacy imports сохранены);
- `api/client.py` сокращён до base-слоя, доменные методы вынесены по подпапкам;
- `api/transport.py` разрезан на subdomain-модули с сохранением класса `TransportApiMixin`.
## 5) План разрезания Go-ядра
### 5.1 `transport_handlers.go` -> `app/transport_*`
- Разделение:
- `transport_handlers_clients.go` (CRUD/lifecycle/health/metrics)
- `transport_handlers_policy.go` (validate/apply/rollback/conflicts)
- `transport_validator.go` (normalize + conflict detection)
- `transport_confirm_token.go` (token lifecycle)
- `transport_state_store.go` (load/save state/snapshots)
- Сохраняем текущие endpoint-path и response-контракт без изменений.
### 5.2 `server.go` -> `app/entrypoints.go + api_bootstrap.go + api_routes.go`
- Цель: развязать entrypoint-логику (`Run*CLI`), bootstrap API-сервера и registry маршрутов.
- Статус: выполнено (`2026-03-10`).
- Состав:
- `entrypoints.go``Run`, `RunAPIServer`, `RunRoutesUpdateCLI`, `RunRoutesClearCLI`, `RunAutoloopCLI`, legacy-dispatch;
- `api_bootstrap.go` — bootstrap и lifecycle HTTP-сервера (`runAPIServerAtAddr`);
- `api_routes.go` — thin-facade `registerAPIRoutes`;
- `api_routes_*.go` — доменные registrars (`core`, `traffic`, `transport`, `trace`, `dns`, `vpn`) без изменения endpoint-path.
### 5.3 `app/*` -> подпакеты runtime (`app/cli`, `app/bootstrap`)
- Цель: убрать "кучу" в корне `app` и перенести исполнимые runner-модули в отдельные папки, сохранив фасады в `app`.
- Статус: выполнено (`2026-03-10`).
- Состав:
- `app/cli/*``routes-update`, `routes-clear`, `autoloop` раннеры с dependency-injection (callback deps);
- `app/bootstrap/server_runner.go` — единый HTTP server runner;
- `app/entrypoints.go` и `app/api_bootstrap.go` оставлены thin-facade слоями.
### 5.4 `transport_handlers.go` -> модульные transport файлы
- Цель: убрать крупнейший монолит transport control-plane без изменения endpoint-контракта.
- Статус: выполнено (`2026-03-10`).
- Состав:
- `transport_shared.go` — state/version constants, mutex, общие типы;
- `transport_handlers_clients.go` — clients CRUD/lifecycle/netns toggle handlers;
- `transport_handlers_policy.go` — policy validate/apply/rollback/conflicts/capabilities handlers;
- `transport_policy_validate.go` — normalize/validate/diff/conflict helpers;
- `transport_client_runtime.go` — runtime/health/lifecycle/allocation helpers;
- `transport_tokens_state.go` — confirm-token и state persistence.
### 5.5 Подпакеты transport (без циклических зависимостей)
- Цель: выносить переиспользуемые части transport-логики в подпапки только там, где не возникает import-cycle с `app`.
- Статус: in_progress (`2026-03-10`).
- Уже вынесено:
- `app/transporttoken/store.go` — confirm-token store (`issue/consume/ttl cleanup`) и token generator.
- Дальше:
- выделить в подпакеты часть stateless helper-функций (при сохранении фасадов в `app`).
## 6) Порядок выполнения (безопасный)
1. `api_client.py` (минимальный риск для UI, проще ревьюить).
2. `dashboard_controller.py`.
3. `vpn_dashboard_qt.py` (таб за табом).
4. `transport_handlers.go` в Go.
## 7) Definition of Done
- Все текущие smoke/e2e проходят:
- `./tests/run_all.sh`
- плюс ручная проверка пользователем в desktop приложении.
- Нет изменений API-контракта (`/api/v1/*`) и event-kind.
- В каждом крупном модуле остаётся читаемый размер (ориентир `< 700` строк; предпочтительно `< 500`).
## 8) Дополнительно (после F1)
- Добавить settings-механику видимости вкладок протоколов (`SingBox`, `DNSTT`, `Phoenix`) через `QSettings`/profile, чтобы включать только нужные модули UI.