Как реализовать чистую архитектуру на Android?

Автор оригинала: Manuel Mato
  • Перевод

Как реализовать чистую архитектуру на Android?


Что вы найдёте в этой статье?


В 2016 году я начал изучать Java, а в начале 2017 года — Android. С самого начала я уже знал, что существует понятие архитектуры приложений, но не знал, как это применить в своём коде. Я находил много разных гайдов, но понятнее от этого мне не становилось.


Эта статья — именно та, которую мне хотелось бы прочитать в начале своего пути.


Важность архитектуры приложений


Многие компании проводят технические тесты для кандидатов, которые проходят отбор. Тесты могут отличаться, но есть то, что их объединяет. Все они требуют понимания и умения применения хорошей программной архитектуры.


Хорошая программная архитектура позволяет легко понимать, разрабатывать, поддерживать и внедрять систему [Книга «Чистая архитектура», глава 15]

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


Пример


Элементы в RecyclerView:


Элементы в RecyclerView


  1. Мы будем получать данные из API и показывать результаты пользователю.
  2. Результатом будет список пива с названием, описанием, изображением и содержанием алкоголя для каждого.
  3. Пиво должно быть упорядочено по градусу крепости.

Для решения этой задачи:


  1. Мы должны получить данные из API.
  2. Упорядочить элементы от самого низкого до самого высокого градуса крепости.
  3. Если содержание алкоголя меньше 5%, будет нарисован зелёный кружок, если оно находится между 5% и 8% — кружок будет оранжевым, а выше 8% — красный кружок.
  4. Наконец, мы должны показать список элементов.

Какое наименее гибкое решение?


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


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


Как понять архитектуру приложений в Android?


Я приведу очень простой пример. Представьте себе автомобильный завод с пятью зонами:


  1. Первая зона создает шасси.
  2. Вторая зона соединяет механические части.
  3. Третья зона собирает электронную схему.
  4. Четвертая область — покрасочная.
  5. И последняя область добавляет эстетические детали.

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


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


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


Кроме того, если со временем мы захотим открыть вторую фабрику для создания новой модели автомобиля, будет легче разделить рабочие зоны.


Применение архитектуры в Android


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


Что это за слои?


Слои архитектуры Android


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


  1. Уровень представления.
  2. Уровень бизнес-логики.
  3. И уровень данных.

1. Уровень представления


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


В нашем примере эти операции разделены между уровнем пользовательского интерфейса и уровнем ViewModel:


  • Уровень пользовательского интерфейса содержит Activity и фрагменты, фиксирующие пользовательские события и отображающие данные.
  • Уровень ViewModel форматирует данные так, что пользовательский интерфейс показывает их определённым образом.

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


В нашем примере слой пользовательского интерфейса отображает список пива, а слой ViewModel сообщает цвет, который вы должны использовать в зависимости от алкогольного диапазона.


2. Уровень бизнес-логики


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


3. Уровень данных


На этом уровне находятся данные и способ доступа к ним.


Эти операции разделены между уровнем репозитория и уровнем источника данных:


  • Уровень репозитория реализует логику доступа к данным. Его ответственность заключается в том, чтобы получить данные. Необходимо проверить, где искать их в определённый момент. Например, вы можете сначала проверить локальную базу данных и, если там данных нет, сделать запрос к API и сохранить данные в базу данных. То есть он определяет способ доступа к данным. В нашем примере он запрашивает данные о пиве непосредственно у уровня, который взаимодействует с API.
  • Уровень источника данных отвечает непосредственно за получение данных. В нашем примере он реализует логику доступа к API для получения данных о пиве.

Как слои взаимодействуют?


Давайте посмотрим на теоретический и практический подходы взаимодействия.


В теории:


Взаимодействие между слоями


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


  • Пользовательский интерфейс может общаться только с ViewModel.
  • ViewModel может общаться только с уровнем бизнес-логики.
  • Уровень бизнес-логики может общаться только с уровнем репозитория.
  • И репозиторий может общаться только с источником данных.

На практике:


Структура пакетов в IDE Android Studio при чистой архитектуре:


Структура пакетов


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


Заключительные замечания по архитектуре приложений


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


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


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


Я делюсь репозиторием, где вы можете увидеть:


  • Пример чистой архитектуры на Android с Kotlin.
  • Использование LiveData для связи пользовательского интерфейса с ViewModel.
  • Использование корутин.
  • Kodein для внедрения зависимостей.
  • JUnit и Mockito для тестирования бизнес-логики.
  • +16
  • 9,8k
  • 5
Поделиться публикацией

Комментарии 5

    +2
    ИМХО, такой подход примирителен к любому приложению с пользовательским интерфейсом.
    Не понимаю каким боком тут андроид указан…
      0
      Почему нет? В конкретно этой статье разбирается реализация чистой архитектуры в контексте Android-приложения
      0
      А ради чего ошибки преобразуются в магические константы? Можно ведь более внятные решения использовать, вроде Throwable наследований или Sealed классов.
      Это же любой разработчик забодается определять, что же там внури за ошибка, если проект хоть немного разрастется.
        0
        В целом получилось интересно, но создалось ощущение, что чистая архитектура это только про слои. А пример пакетов вообще вводит в заблуждение, ведь даже сам Мартин говорил, что в чистой архитектуре по структуре проекта должно быть понятно, что он делает и какие фичи имеет, а не какие паттерны и архитектуры были применены. Такая структура пакетов должна быть внутри пакета конкретной фичи, как ты смотришь на такой вариант?
          0
          Спасибо за статью на интересную тему.
          [далее замечания, но не для критики статьи]
          Зона ответственности, как термин постоянно требует уточнения. [Не знаю почему. ] Обычно ее понимают как, «одна сущность делает, что-то одно», но это понятие связано сильно с вектором изменением. Есть даже определение у каждого класса/компонента и т.д. только один владелец. Или каждый класс должен иметь один вектор изменения.
          Так вот. Самое интересное. Разработчик разбил на слои и компоненты свой проект и получает глубокое удовлетворение и счастье когда ему приходят требования тз совпадающие с вектором изменения, который заложен в проекте (т.е. с архитектурой). Но в реальности действует закон «Вектор изменения предполагаемый разработчиком строго перпендикулярный вектору изменения требуемого заказчиком». И здесь наша архитектура плывет.
          Для примера.
          Аналогия со сборкой машины. Там где делают колеса. Заказчик прислал ТЗ «сделать диаметр колес в два раза больше». В идеале, это изменение никак не должно затронуть другие цеха. Мы сделали новые колеса и их просто поставили. Это хорошая архитектура. Если потребовалось изменение в других компонентах (например кузова) — похуже.
          Еще пример.
          Заказчик прислал тз. наше приложение вышло на рынок под маркой «PoBuhay» и требуется подключить еще несовершеннолетних клиентов. С показом газировки (с указанием размера пузырьков). И если мы тапаем на газировку, открывается вьюха с Пяточком с подгрузкой данных по количество калорий и газа. Т.е новый элемент (прям очень) отличается от тех которых мы задумали первоначально. Изменения пройдут по всем слоям. И желательно сделать такие изменения, что при добавление других новых элементов это затронет как можно меньше слоев и классов. Улучшить архитектуру, сделать ее гибче.
          Делая архитектуру гибче, мы ее делаем менее понятной. И все выливается в поиск баланса и вопросу «серебряная пуля vs незатратный проект»

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

          Самое читаемое