Была ли конфликтная ситуация которую решил
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Конфликтная ситуация: Архитектурный спор между MVP и MVVM при миграции legacy-кода
Да, я сталкивался с конфликтной ситуацией, которая была связана с выбором архитектурного подхода при рефакторинге большого legacy-модуля в Android-приложении. Ситуация возникла между мной (техлидом) и senior-разработчиком из другой команды, который был привлечен к проекту как консультант.
Суть конфликта
Мы работали над модулем «Карта и навигация», который был написан в стиле «God Activity» – более 4000 строк кода в одном классе, сильная связность с Android SDK, нулевая тестируемость. Моя позиция была основана на постепенной миграции на MVP (Model-View-Presenter), так как:
- Команда уже имела опыт с MVP в других модулях.
- Это позволяло разбивать монолит постепенно, создавая Presenter для новых функций.
- У нас были tight deadline, и MVP быстрее внедрить в существующий код.
Консультант настаивал на переходе сразу на MVVM (Model-View-ViewModel) с Data Binding и LiveData, аргументируя это:
- MVVM – более современный и «родной» для Android подход (с учетом Android Architecture Components).
- Лучшее разделение ответственности и автоматическая привязка данных.
- Долгосрочная выгода, несмотря на первоначальные затраты.
Решение конфликта
Конфликт был не личным, а техническим. Чтобы его разрешить, я инициировал следующее:
-
Создание Proof of Concept (PoC) для обоих подходов. Мы выделили два дня, чтобы переписать один небольшой, но сложный фрагмент (расчет маршрута) двумя способами.
// Пример фрагмента PoC для MVP class RoutePresenter( private val view: RouteContract.View, private val routeInteractor: RouteInteractor ) { fun calculateRoute(origin: Location, destination: Location) { routeInteractor.calculate(origin, destination, onSuccess = { route -> view.showRoute(route) }, onFailure = { error -> view.showError(error) } ) } }// Пример фрагмента PoC для MVVM class RouteViewModel : ViewModel() { private val _route = MutableLiveData<Route>() val route: LiveData<Route> = _route fun calculateRoute(origin: Location, destination: Location) { viewModelScope.launch { try { val result = routeRepository.calculateRoute(origin, destination) _route.value = result } catch (e: Exception) { // Обработка ошибки } } } } -
Критерии оценки и демонстрация команде. Мы составили таблицу сравнения по ключевым параметрам:
- Время на интеграцию в существующий код (MVP выигрывал).
- Тестируемость (оба подхода давали ~90% coverage).
- Объем нового boilerplate-кода (в MVVM с Data Binding его было меньше).
- Плавность обучения команды (MVP был привычнее).
-
Компромиссное решение. После демонстрации PoC и обсуждения с командой мы пришли к гибридному решению:
- Краткосрочно: Использовать MVP для рефакторинга существующей логики, так как это позволяло немедленно улучшать код, не переучивая всю команду и укладываясь в сроки.
- Долгосрочно: Все новые фичи в модуле писать на MVVM с ViewModel и LiveData, постепенно накапливая экспертизу.
- Создать shared-модуль с Repository-слоем (общим для обеих архитектур), что обеспечило консистентность данных и позволило плавно мигрировать.
Итог и выводы
Конфликт был разрешен через технический анализ, а не через авторитет. В результате:
- Мы уложились в дедлайн, улучшив поддерживаемость кода.
- Команда освоила MVVM на практике без рисков для проекта.
- Кодовая база стала более гибкой: старые части на MVP стабильны, новые – на современном MVVM.
Этот опыт научил меня, что в технических спорах важны не победа одной точки зрения, а нахождение оптимального для проекта и команды решения. Демонстрация конкретных реализаций (PoC) и вовлечение всей команды в обсуждение критериев – ключевой инструмент для разрешения таких конфликтов.