Connect with us

Кроссплатформенная разработка

От Android к Multiplatform: дорожная карта

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

Опубликовано

/

     
     

Переход реального приложения на Kotlin Multiplatform, как это сделали в компании Pri-Num для одного из своих финтех-продуктов, приносит как радость, так и проблемы.

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

TL;TR

  • Оцените ресурсы, доступное время и потенциал команды. Разбейте рефакторинг на управляемые задачи с оценкой, и изначально сосредоточьтесь на оценках только для Android, поскольку миграция на iOS может быть незнакома команде.
  • Модулируйте архитектуру. Внесите архитектурные изменения, чтобы разделить код на модули, которые можно рефакторить независимо друг от друга, минимизируя кросс-модульные зависимости.
  • При необходимости планируйте миграцию инъекции зависимостей (DI). Как только модули будут окончательно доработаны, подготовьтесь к миграции DI (с Hilt/Dagger на Koin), так как это охватывает все модули и не может быть выполнено частично. Это может стать самой большой задачей в процессе миграции.
  • Применяйте KMP-плагин постепенно, начиная с уровня данных. Начните с добавления androidTarget(), затем добавьте ios() после завершения миграции Android.
  • Перенесите презентационный слой (UI) в соответствии с потребностями заинтересованных сторон. Если вы используете Android View, подумайте о создании пользовательского интерфейса iOS в SwiftUI. Если же проект более старый, заинтересованные стороны могут поддержать обновление пользовательского интерфейса — в этом случае перейдите на Jetpack Compose, который поддерживает iOS.
  • Пример архитектуры на GitHub в конце статьи.

Оценка

Прежде чем приступить к миграции, необходимо провести тщательную оценку. Эта дорожная карта поможет вам выявить все пробелы в проекте, которые необходимо тщательно оценить вместе с командой. Одним из ключевых решений является определение подхода к миграции: будет ли она проходить параллельно с непрерывной разработкой новых функций, или вы можете выделить все ресурсы, временно приостановив разработку функций? Если заморозка проекта на несколько месяцев невозможна, имейте в виду, что некоторым задачам (например, миграция Dependency Injection), возможно, придется подождать «подходящего времени» в цикле разработки, что увеличит общие расчеты.

Учитывайте также опыт команды. Во время рефакторинга вы, скорее всего, столкнетесь с обходными путями, понятными только владельцу исходного кода, а этого человека может уже не быть в команде. Кроме того, ожидайте, что может потребоваться несколько раундов рефакторинга, а некоторые функции могут быть временно «недоступны» в период миграции.

Подготовка существующего кода. Архитектура

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

1. Модулируйте приложение

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

В Pri-Num мы очень любим модулизацию и можем похвастаться 40 модулями в нашем проекте с планами на будущее расширение :)

Давайте посмотрим немного ближе:

От Android к Multiplatform: дорожная карта

Мы используем классическую архитектуру «данные-домен-представление», высокоуровневая структура которой будет представлена позже. Миграция должна начинаться с уровня данных и заканчиваться уровнем представления. Такой подход необходим, поскольку модули Kotlin Multiplatform (KMP) не могут зависеть от модулей, не относящихся к KMP. Кроме того, слой данных потребует наибольших усилий по интеграции кода, специфичного для iOS.

2. Слой данных

Слой данных структурируется в несколько пар модулей со схемами «API — Реализация».

От Android к Multiplatform: дорожная карта

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

Давайте рассмотрим базу данных. API определяет только интерфейсы и модели:

Мы не выставляем сущности, DAO, аннотации или любые другие изменения инфраструктуры — это гарантирует, что изменения на уровне реализации не повлияют на другие уровни.

Когда мы начали переносить наш проект, Room не поддерживал Kotlin Multiplatform (KMP), поэтому нам пришлось перейти на SQLDelight. Благодаря инкапсуляции мы смогли быстро перенести код. Мы также следим за разработками Room в области KMP и рассматриваем возможность возвращения, как только база станет стабильной. То же самое относится и к нашему сетевому уровню, который изначально работал на Retrofit, но теперь перешли на Ktorfit. Если в будущем мы решим перейти на GraphQL, такая миграция нашего кода будет в безопасности.

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

3. Доменный слой

Доменный слой должен быть самой простой частью приложения для миграции, так как в идеале он не содержит зависимостей от платформы.

В Pri-Num мы спроектировали наш слой домена таким образом, чтобы он полагался на модуль data:api только в случае необходимости и изредка зависел от других сценариев использования. Этот слой получает данные из репозиториев и форматирует их в соответствии с требованиями бизнес-анализа.

Хотя паттерн API <-> Реализация здесь не является строго необходимым, мы следуем ему, чтобы сократить время компиляции для больших проектов. Если в вашем приложении всего несколько десятков use case, может быть достаточно одного доменного модуля.

От Android к Multiplatform: дорожная карта

Хотя теоретически слой домена не должен иметь зависимостей от платформы, в реальности исключения в условиях сжатых сроков могут проскакивать (например, добавление Context «на всякий случай»). Даже при строгой (и нерушимой) политике против таких зависимостей, вы все равно можете обнаружить Java-зависимости, такие как Date или Math. Их следует заменить на KMP-совместимые библиотеки вроде Datetime или BigNum.

Кроме того, наличие доменного слоя с тестами очень полезно, особенно для утилитарных классов, таких как проверка телефона или электронной почты. Это  позволило нам сэкономить значительное время QA при рефакторинге от зависимостей Math и Date.

4. Презентационный (UI) слой

Если вы все еще используете Android View, то ваши возможности ограничены — конечное приложение не будет полностью KMP, так как пользовательский интерфейс придется разрабатывать отдельно для iOS и Android. Однако не стоит отчаиваться: используя Kotlin Multiplatform, вы все равно сможете покрыть около двух третей проекта общим кодом для обеих платформ.

Тем не менее, я рекомендую побудить команду запланировать переход на Compose. Compose улучшает читаемость кода пользовательского интерфейса, ускоряет разработку и прекрасно следует рекомендациям Material Design, придавая приложению отполированный вид. Переход от Compose к Compose Multiplatform также плавный. Если вы не сильно полагаетесь на сторонние библиотеки, изменения будут минимальными.

5. Инъекция зависимостей

На момент написания статьи Hilt не поддерживает Kotlin Multiplatform (хотя, учитывая быстрое развитие KMP, это может скоро измениться!). Нашим решением было перейти на Koin. Хотя было непросто отказаться от compile-time инструмента, сам переход был вполне управляемым.

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

6. KMP-миграция и поддержка iOS

После завершения этих подготовительных работ вы готовы приступить к миграции на KMP. В предыдущей статье я описал шаги по созданию мультиплатформенного проекта Kotlin.

Начните с миграции слоя данных, а затем переходите к слою представления. Если ваша команда все еще наращивает опыт работы с KMP, начните с androidTarget() и добавляйте цель ios() итеративно, по мере приобретения уверенности. Чтобы узнать советы и рекомендации по реализации кода, специфичного для iOS, следите за моими публикациями.

Выводы

Как видите, успех этой миграции во многом зависит от прочного архитектурного фундамента. В Pri-Num уделяют первостепенное внимание разработке архитектур, которые будут развиваться вместе с развитием технологий, и готовы принять будущие вызовы.

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

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

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: