Flutter-приложение с feature-first архитектурой, где каждый endpoint добавляется в новую/существующую feature без изменений в core.
lib/
core/
auth/
bootstrap/
config/
error/
network/
routing/
utils/
shared/
constants/
extensions/
ui-kit/
widgets/
features/
auth|catalog|pricing|cart|orders|admin|reports/
data/
domain/
presentation/
- Flutter SDK (stable channel).
- Dart SDK (входит в Flutter SDK).
- Browser toolchain для web (
flutter config --enable-webпри необходимости). - Доступ к backend окружениям (dev/stage/prod) и валидные токены для защищённых сценариев.
Конфигурация окружения загружается из compile-time переменных в EnvConfig.load():
APP_ENV—dev | stage | prod.API_BASE_URL— базовый URL backend.
Примеры запуска:
# dev
flutter run -d chrome \
--dart-define=APP_ENV=dev \
--dart-define=API_BASE_URL=https://dev-api.example.com/api/v1
# stage
flutter run -d chrome \
--dart-define=APP_ENV=stage \
--dart-define=API_BASE_URL=https://stage-api.example.com/api/v1
# prod
flutter run -d chrome \
--dart-define=APP_ENV=prod \
--dart-define=API_BASE_URL=https://api.example.com/api/v1Если в вашей инфраструктуре переменная называется
BACKEND_BASE_URL, прокиньте её вAPI_BASE_URLна этапе запуска/CI (например, через shell-export или секреты pipeline).
Сборка web:
flutter pub get
flutter build web \
--release \
--dart-define=APP_ENV=prod \
--dart-define=API_BASE_URL=https://api.example.com/api/v1Артефакты будут в build/web.
Базовый процесс деплоя:
- Выполнить
flutter build web ...с нужным окружением. - Опубликовать содержимое
build/webв статический хостинг (Nginx, S3+CloudFront, Firebase Hosting, etc.). - Настроить fallback для SPA-роутов на
index.html. - Проверить, что backend разрешает CORS для origin вашего frontend-домена.
- В коде приложение читает именно
API_BASE_URLвlib/core/config/env_config.dart. BACKEND_BASE_URLможно использовать как внешнее имя переменной в CI/CD, но перед запуском Flutter её нужно маппить вAPI_BASE_URL.- Префикс
/api/v1централизованно задаётся в значенииAPI_BASE_URL(например,https://host/api/v1), а feature-API используют относительные пути (/catalog,/orders,/auth/refreshи т.д.).
main.dart содержит только:
- Инициализацию env-конфига.
- Подготовку DI/провайдеров.
- Подключение роутера.
Ниже базовый шаблон для добавления новой feature <feature> с новым endpoint:
lib/features/<feature>/
data/
<feature>_api.dart
<feature>_repository_impl.dart
<feature>_dto.dart
domain/
<feature>_entity.dart
<feature>_repository.dart
get_<feature>_items_use_case.dart
presentation/
<feature>_screen.dart
<feature>_controller.dart # StateNotifier/AsyncNotifier
<feature>_widgets.dart
- DTO: описать request/response модели в
data/models/*илиdata/*_dto.dart. - Repository (data layer): добавить вызов endpoint в
<feature>_api.dartи маппинг DTO -> Entity в<feature>_repository_impl.dart. - Use case (domain layer): расширить контракт
domain/<feature>_repository.dartи создать/обновить use case (get_<feature>_items_use_case.dartили отдельный сценарий). - UI (presentation layer): подключить новый use case в controller/provider и отобразить состояние на экране.
- Route: зарегистрировать маршрут в
lib/core/routing/routes.dartи подключить экран вlib/core/routing/app_router.dart. - Test:
- unit-тесты use case/repository;
- тесты маппинга DTO;
- widget/integration-тест экрана (happy path + ошибки 401/429/5xx).
data/<feature>_api.dart— вызовы endpoint и парсинг DTO.data/<feature>_repository_impl.dart— маппинг DTO -> entity и реализация domain-контрактов.domain/<feature>_repository.dart— интерфейс репозитория.domain/get_<feature>_items_use_case.dart— бизнес-сценарий.presentation/*— экран, state/controller/provider, UI-виджеты.
- API:
lib/features/reports/data/reports_api.dart - Repository impl:
lib/features/reports/data/reports_repository_impl.dart - Domain contract:
lib/features/reports/domain/reports_repository.dart - Use case:
lib/features/reports/domain/get_reports_items_use_case.dart - Presentation:
lib/features/reports/presentation/reports_screen.dart
Новый endpoint добавляется в соответствующую feature через
data -> domain -> presentation, не требуя изменений вlib/core/*.
- Все провайдеры размещаются в
lib/core/di/providers.dart. - Именование провайдеров фиксируется суффиксом
Provider:- инфраструктура:
<service>Provider(dioProvider,apiClientProvider,tokenStorageProvider,authSessionProvider); - data/domain:
<feature>ApiProvider,<feature>RepositoryProvider,get<Feature>ItemsUseCaseProvider; - экранные состояния:
<feature>StateProvider(черезStateNotifierProvider/AsyncNotifierProvider).
- инфраструктура:
- Для одного типа зависимости используем один источник истины (single provider per dependency type).
- Хранение токенов:
- текущий
TokenStorage— in-memory storage (токены очищаются при перезапуске вкладки/приложения); - для web fallback в
localStorage/sessionStorageповышает риск кражи токена при XSS, поэтому используйте только как осознанный компромисс и минимизируйте TTL токенов.
- текущий
- XSS mitigation:
- не рендерить недоверенный HTML/JS;
- валидировать и экранировать пользовательские данные;
- держать зависимости Flutter/web-plugins в актуальных версиях.
- CSP (Content Security Policy):
- на уровне web-сервера задавать строгий CSP (ограничение
script-src,connect-src, запрет inline-скриптов, где возможно); - явно разрешать только доверенные backend-origin для API.
- на уровне web-сервера задавать строгий CSP (ограничение
- Token rotation:
- клиент уже использует refresh flow (
POST /auth/refresh) и переоткрывает сессию при успешном ответе; - при ошибке refresh сессия очищается и пользователь переводится в неавторизованное состояние.
- клиент уже использует refresh flow (
Симптомы:
- Частые
401, пользователь выбрасывается на login.
Проверить:
API_BASE_URLуказывает на верный backend и верную версию API.- В refresh-ответе приходит непустой
accessToken. POST /auth/refreshдоступен безAuthorizationheader и принимаетrefreshToken.- Повторяются только idempotent/safe-запросы (или отмеченные idempotent флагом).
Симптомы:
- Web-клиент получает network/CORS ошибки до уровня бизнес-логики.
Проверить:
- Backend возвращает
Access-Control-Allow-Originдля frontend origin. - Разрешены необходимые методы/headers (
Authorization,Content-Type,X-Request-ID). - Preflight
OPTIONSзапросы обрабатываются корректно.
Симптомы:
- Сервер часто отвечает
429 Too Many Requests.
Проверить:
- Учитывается
Retry-After(если есть). - На UI есть backoff/дебаунс и блокировка повторных submit.
- Не происходит циклических рефетчей из-за состояния экрана.
Симптомы:
- Ошибки сети, таймауты, нестабильные ответы.
Проверить:
- Доступность backend (DNS/TLS/firewall/VPN).
- Корректность
API_BASE_URLи сертификатов. - Наличие graceful fallback в UI (retry-кнопка, сохранение предыдущего snapshot для list-экранов).
- Локально:
flutter test --reporter expanded - CI: workflow
.github/workflows/ci.ymlзапускаетflutter pub getиflutter test --reporter expanded. - Тестовые данные и doubles:
- JSON fixtures:
test/fixtures/*.json - Mock repositories/API doubles:
test/doubles/mock_repositories.dart
- JSON fixtures: