Как Мы Выбрали Архитектуру Презентационного Слоя На Новом Проекте И Не Прогадали



О проекте Всем привет! Меня зовут Даниил Климчук.

Год назад я приехал в яркие.

деньги третий разработчик Android. Несмотря на это, кода в проекте практически не было, а первые возможности только начинали разрабатываться.

Нам нужно было запустить новое банковское приложение в Европе, где нам пришлось бы конкурировать с такими компаниями, как Revolut. Уже тогда было ясно, что команда очень быстро значительно вырастет. Конечно, стоило сразу задуматься о том, как будет развиваться архитектура проекта.

Через год, когда проект стартует, времени на это уже не останется, и повода для внесения существенных изменений просто не будет. Одним из ключевых решений на раннем этапе был выбор архитектуры уровня представления.

В этой статье я расскажу, как мы его получили.



Оглавление

  • Первая часть.

    Как мы выбрали архитектуру презентационного слоя на новом проекте и не прогадали.

  • Часть вторая.

    Давайте проанализируем архитектуру ELM в мобильном приложении.

  • Часть третья.

    Пишем для Android с Элмсли.



О выборе

Возможные для нас подходы четко разделились на две группы: проверенные временем и надежные MVP, MVVM и MVC, а также новые архитектуры с использованием Однонаправленный поток данных: Redux, MVI, Elm (он же MVU) и т. д. Я не хотел сравнивать каждый по отдельности, а чтобы упростить задачу, решите, в каком направлении смотреть в первую очередь.

Поэтому мы быстро набросали список требований.

Я хотел бы:

  • Код был поддержан Лучше всего помнить, что со временем код все равно придется понимать и изменять.

  • Новые люди могут быстро влиться В данном случае время – деньги, а также комфорт работы.

    После прихода в компанию всегда появляется что-то новое и сил и времени вникать в детали архитектуры может не хватить.

  • Уменьшите шаблонность Вводить одно и то же на каждом экране утомительно и может привести к ошибкам.

  • Был единый подход к архитектуре В среднем разные части приложения лучше писать в одном стиле, чтобы разработчики могли переключаться между задачами и тратить меньше времени на понимание того, как именно работает экран.

  • Проще было покрыть тестами Мы сразу подумали, что всю логику покроем модульными тестами, и хотели максимально упростить нашу работу.

Исходя из этого, сразу принять решение было довольно сложно.

Пришлось обратиться к проверенному методу и описать плюсы и минусы обоих подходов:

Для старого доброго

  • Нет шаблона Достаточно реализовать базовые MVP-классы, после чего вам останется только создать Presenter/ViewModel/Controller для каждого экрана.

    В отличие от архитектур UDF, в которых даже для каждого события требуется свой класс.

  • Это известные архитектуры.

    Возможно, чтобы найти Android-разработчика, который не работал с MVP, нужно постараться.

    И для нас важен порог входа, потому что нам нужно будет собрать большую команду.

  • Упрощенный анализ кода При смене экрана меняются только Presenter и View. В UDF-архитектуре логика Presenter разделена на несколько классов, каждый из которых необходимо просматривать отдельно.

  • Нет проблем Проблема описана в проблема для компонентов архитектуры Android. В MVP его практически нет, но в MVVM с LiveData можно использовать сам класс SingleLiveEvent. Для архитектур UDF не существует устоявшегося подхода к решению этой проблемы; для этого вам придется придумать что-то свое.

  • Легко понять Если рассматривать саму архитектуру, то MVP и MVVM определяют лишь наличие двух классов View и Presenter (или ViewModel соответственно).

    В UDF-архитектурах структура более сложная и их компоненты имеют более узкую зону ответственности.



Для нового хайпа

  • Собственно сама UDF В таких архитектурах существует только один фиксированный путь, по которому данные передаются внутри приложения.

    В отличие, например, от MVP, где в Presenter со временем может накапливаться огромное количество спагетти-кода, который со временем становится трудным для понимания.

  • Единое неизменяемое состояние Состояние экрана выделено в отдельный класс под названием State. Если такого явного ограничения нет, состояние может описываться множеством флагов, иногда частично расположенных где-то в представлении или дублированных в нескольких местах.

    Такой подход позволяет иметь единый источник достоверной информации о текущем состоянии экрана.

    Важным преимуществом такого подхода является возможность получить доступ к State в каждый момент времени и понять, например, загружаются ли в данный момент данные.

  • Обработка изменений конфигурации и восстановление процессов Гораздо проще, потому что есть одно государство.

    Достаточно просто нарисовать его еще раз на экране, чтобы полностью восстановить предыдущее состояние.

    При обработке смерти процесса необходимо сохранить только один класс.

    Честно говоря, например, использование LiveData позволит вам обрабатывать изменения конфигурации.

    Однако это дополнительная зависимость, которую придется добавить в проект. Кроме того, стандартный механизм обработки смерти процесса для ViewModel Дескриптор сохраненного состояния гораздо сложнее реализовать и усложняет логику ViewModel.

  • Разделение интересов Логика уровня представления разделена на несколько классов, каждый из которых выполняет свою функцию.

    В отличие, например, от MVP, в котором вся логика находится в Presenter. Получается, что он отвечает за обработку изменений состояния, загрузку данных, изменение модели и т. д. Явного разделения на зоны ответственности нет и зачастую все это находится в одном классе.

  • Безопасность резьбы Нет необходимости думать о потокобезопасности; вся синхронизация происходит на уровне реализации архитектуры.

    Из-за разделения задач и неизменяемого состояния разные части кода не должны иметь доступ к одним и тем же изменяемым данным.

    Например, в MVP внутри Presenter гораздо проще выстрелить себе в ногу, случайно изменив какой-нибудь флаг в состоянии, отличном от основного потока.

  • Проблема с раздутым ведущим Со временем Presenter или ViewModel могут вырасти до нескольких тысяч строк кода.

    На этом этапе вам придется подумать о том, как разделить логику, что вполне может привести к тому, что решение будет менее гибким, чем изначально разработанная архитектура UDF.

  • Горизонтальное масштабирование Некоторые архитектуры UDF имеют возможность составлять экран из нескольких частей вместо одного большого презентатора.

    Например, в MviCore есть разделение на Features, а в ELM — на компоненты.

    Каждый из них написан в одном стиле и вместе составляет логику экрана.

    Кроме того, эти детали можно использовать повторно, в отличие от MVP и MVVM, где приходится придумывать собственное нестандартное решение этой проблемы.

  • Простота тестирования Логика разделена на компоненты, которые можно тестировать индивидуально.

    А Редюсер или его аналог — это вообще чистая функция и к тому же не содержит работы с многопоточностью.

  • Заставляет вас заранее планировать логику экрана Прежде чем писать экран, изначально нужно подумать о том, каким может быть состояние экрана, какие события будут происходить и как все это связать воедино.

    В MVP и других архитектурах нет четкого подхода к написанию экрана, поэтому планирование остается за разработчиком.

  • Возможность реализовать отладку путешествий во времени.

    Позволяет записывать последовательность состояний экрана и затем воспроизводить их.

    Это позволяет разработчику воспроизвести последовательность действий, приводящую к ошибке.

  • Реактивный ранец Архитектуры UDF лучше подходят для работы с Jetpack Compose, для которого недавно была выпущена альфа-версия.

    Архитектуры UDF имеют единственный метод для отображения состояния, который можно сразу преобразовать в иерархию представлений.

  • Хайп Больше шансов, что девелоперов заинтересует вакансия с современной архитектурой, которая даст возможность разработать или попробовать что-то новое.



Как было принято решение

Архитектуры UDF имеют множество преимуществ, которыми мы не хотели жертвовать ради простоты.

Помимо ажиотажа и интересных функций, подкупала возможность справляться с запутанной логикой в Presenter. Я не хотел возвращаться к той же проблеме через несколько лет или получить кучу неподдерживаемого кода.

В конце концов мы решили использовать UDF.

МВИ против ELM

Многие реализации архитектур UDF очень похожи, поэтому мы выделили главное отличие: в MVI логика экрана разделена между Редюсером и Intent, тогда как в ELM она полностью находится в Update.

Как мы выбрали архитектуру презентационного слоя на новом проекте и не прогадали

МВИ против ELM Например, когда вы нажимаете кнопку загрузки, намерение MVI знает, что данные необходимо получить, а редуктор отвечает за отображение статуса загрузки.

В Elm за все это отвечает один класс Update, а в рамках Side Effect происходит только само получение данных.



Почему стоит выбрать ЭЛМ

Мы решили руководствоваться уже существующими недостатками UDF-архитектур, в которых были различия.

Элм однозначно выиграл:

  • Тестовое покрытие Elm позволяет покрыть тестами всю логику экрана, написав тесты всего для одного класса.

    Более того, этот класс не содержит асинхронного кода и писать тесты гораздо проще.

    Более сложные сценарии будут охватываться UI-тестами, а работа по написанию модульных тестов будет значительно сокращена.

  • Понимание новых членов команды Человеку, только что пришедшему работать с Elm, проще объяснить: «вот логика, а вот асинхронные операции».

    В отличие от МВИ, в котором надо представлять, как всё работает в целом.

  • Обзор кода Обновление от Elm можно рассматривать отдельно, так как оно содержит всю логику.

    При просмотре кода, написанного на mvi, приходится больше переключаться между Intent и Редюсером, потому что логика между ними разделена.

В настоящее время уже существует несколько реализаций архитектуры Elm с открытым исходным кодом, например Заварочный чайник , Пуэр И Элмо Однако мы решили сделать свой собственный.



Как решить проблемы с UDF

Еще два пункта остались нерешенными; для них нужно было искать решения.



Как мы выбрали архитектуру презентационного слоя на новом проекте и не прогадали

Наша реализация с окончательным наименованием

шаблон

Головная боль таких подходов — создание большого количества классов на этапе создания экрана.

Например, в нашей реализации это Actor, Редюсер, State, Event, Effect, Command и StoreFactory. Простой экран с одним запросом превращается в долгий набор давно заученного кода.

Для решения этой проблемы был реализован плагин для Android Studio. Весь повторяющийся код можно сгенерировать и добавить новый экран становится не сложнее, чем в обычном MVP.

SingleLiveEvent

Мы поддержали решение этой проблемы на уровнях нашей реализации Elm. Мы выделили отдельную сущность для сообщений, которые нужно отправить во View только один раз.

Для них была выделена отдельная очередь сообщений, на изменения в которой подписывается View. На схеме эта сущность обозначена как Эффект.

Восстановление состояния

Эту проблему можно разделить на две части: восстановление состояния при изменении конфигурации и при восстановлении процесса.

Для решения первой проблемы достаточно хранения компонента Elm внутри Dagger Scope. Новый экземпляр фрагмента подключится к компоненту и получит последнее состояние при инициализации.

Обработка смерти процесса оказалась немного сложнее.

Так как есть состояние, выделенное в отдельный класс, то достаточно сохранить его в onSaveInstanceState.

Что дальше

Мы постарались подойти к решению о выборе архитектуры с должной тщательностью, так как считали, что это окажет существенное влияние на развитие проекта в будущем.

Спустя год можно сказать, что наше решение пережило расширение команды до 12 человек, но потребовало совместных усилий в своей разработке.

Помимо первоначальной реализации самой архитектуры, нам также пришлось исправлять в ней ошибки, писать руководства для новичков, разрабатывать общий подход к написанию тестов и многое другое.

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

Теперь он доступен как библиотека с открытым исходным кодом.

Элмсли .

Подробнее о нашей реализации мы расскажем в следующая часть .

Теги: #Android #Разработка Android #Разработка мобильных приложений #архитектура #разработка мобильных устройств #Kotlin #архитектура приложений #дизайн и рефакторинг #mvi #mvi #elm #choice #vivid.money #udf

Вместе с данным постом часто просматривают: