MBL
Go / Проекты / SmartParking / ParkTrack (Smart Parking) — обзор и архитектура
Go средний

ParkTrack (Smart Parking) — обзор и архитектура

projectsmobileflutter

Архитектурная схема (archify) →

ParkTrack — мобильное приложение на Flutter для поиска парковки: показывает свободные зоны на карте, прогнозирует занятость и строит маршрут до выбранного места. Пакет называется mobile, реальное имя продукта — ParkTrack (см. pubspec.yaml).

Clean Architecture на Flutter

Четыре слоя, разделённые по папкам lib/:

  • core — инфраструктура без бизнес-смысла: network (Dio-клиент, интерцепторы), router (go_router), storage (токены, флаг истёкшей сессии), theme.
  • data — реализация доступа к данным: api (Retrofit-подобные Dio-вызовы для auth/zones/occupancy/routing/forecasts), models (DTO с freezed+json_serializable), repositories (фасад над api для presentation-слоя).
  • domain — модели предметной области без привязки к сети: Zone, User, RouteResult. Здесь нет @JsonKey-разметки — это чистые бизнес-объекты.
  • presentation — Riverpod-провайдеры (StateNotifierProvider) и экраны (auth/map/profile/search/splash).

Зависимости идут внутрь: presentation знает про domain и data, domain не знает ни про кого.

Зачем нужен MockInterceptor

lib/core/network/mock_interceptor.dart — Dio-интерцептор, который перехватывает запрос на этапе onRequest и сразу отдаёт handler.resolve(...) с готовым JSON, не давая запросу уйти в сеть вообще. Флаг kUseMocks (сейчас false в коде) включает его глобально в dio_client.dart.

Это не заглушка "на всякий случай" — подделанные ответы точно повторяют реальный контракт бэкенда: GeoJSON-полигоны зон (Polygon с кольцом координат), уровень доверия прогноза (confidence), и даже реалистичный deeplink_url вида yandexnavi://build_route_on_map?lat_to=...&appmetrica_tracking_id=... для передачи построения маршрута во внешний Яндекс.Навигатор. Такой мок позволяет верстать и отлаживать весь UI (карту, список кандидатов, экран маршрута) до того, как бэкенд вообще готов — и потом выключается одной константой.

Модель зоны и почему в ней есть прогноз

domain/models/zone.dart — Zone хранит не только текущее состояние (freeCount, confidence, pay), но и опциональные поля прогноза: hasForecast, forecastFor, forecastGeneratedAt. Это значит, что не у каждой зоны обязан быть прогноз занятости — карта корректно работает и с зонами, где есть только текущие данные, и точно так же рисует зоны с добавленным поверх прогнозом на будущее время.

ZoneType — parallel (парковка параллельно бордюру) или standard; LocationType — street/yard/openLot/underground/multilevel. Оба поля не для отображения ради галочки — потенциально влияют на то, как приложение должно ранжировать кандидатов (подземная парковка vs открытая площадка — разный сценарий использования).

Поток "поиск → кандидаты → маршрут"

По мок-данным routing/search виден реальный контракт: сервер возвращает не одну зону, а ранжированный список кандидатов (rank, current_free_count, current_confidence, predicted_free_count, distance_to_destination_meters, duration_from_origin_seconds) — то есть решение "куда ехать" явно не принимается только по расстоянию, в формуле участвует и прогноз занятости. Дальше routing/new фиксирует выбранную зону и возвращает deeplink_url — финальная навигация делегируется внешнему навигатору, приложение не рисует собственный turn-by-turn маршрут.

Самопроверка 0 / 2
Могу назвать все 4 слоя Clean Architecture в проекте и что живёт в каждом
Понимаю, зачем нужен MockInterceptor и почему он подделывает ответ ДО сетевого запроса, а не после
Как усвоено?