Источник: Nuances of Programming
Для лучшего понимания чистой архитектуры давайте создадим примерный проект. Это приложение, на первой странице которого показывается список персонажей из мультсериала «Рик и Морти» с данными. Нажимая на каждого персонаж, на следующей странице можно увидеть серии, в которых эти персонажи появляются.
Поэтому у нас два типа сущностей: персонаж и серия.
Итак, прежде всего разберёмся, почему нам надо использовать чистую архитектуру?
- Разделение обязанностей — разделение кода на части или различные модули, которые имеют определённые обязанности, облегчает его сопровождение и дальнейшие изменения.
- Слабая связанность — в гибкий код можно легко вносить изменения, не меняя всей системы.
- Лёгкость тестирования.
В проекте у нас три слоя: приложение (представление), данные и предметная область.
Данные: в этом слое есть абстрактное определение различных источников данных и как их следует использовать. Мапперы выполняют отображение ответа сервера на модели баз данных. Модели представляют собой модель ответа сервера. Репозиторий существует для реализации вызовов API. Операции с базами данных — для реализации интерфейса «Dao», а пакет API — для определения методов API-вызовов с сервера. В обычном приложении мы, как правило, храним репозиторий и интерфейс репозитория в одном пакете. И можем делать это локально, чтобы везде иметь прямой доступ. Но в этом случае слой данных ни в коем разе не должен знать о других слоях.
Предметная область: этот слой известен как бизнес-логика. Это правила вашего бизнеса. Здесь находится пакет model, содержащий модели баз данных. А также репозиторий, являющийся лишь интерфейсом, и прецеденты. А что такое прецедент? Как известно, прецеденты выполняют единственную задачу. И в случае с персонажами, когда надо получить данные из базы данных, мы пишем прецедент с этой самой задачей получения данных из базы данных.
Приложение: этот слой взаимодействует только с UI (пользовательским интерфейсом) и содержит фрагменты, activity (т. е. визуальный интерфейс с отдельным экраном для одного действия пользователя), ViewModel и Di. Под Di подразумевается модуль, предназначенный для этого фрагмента или activity.
На этом рисунке показано, как слои взаимодействуют друг с другом:
Но закончим уже с текстом и перейдём скорее к коду.
Итак, в проекте мы используем:
Kotlin
Зависимости:
Для этого проекта в базе мы создаём три пакета: Episode («Серия»), Character («Персонаж») и utils.
Что такое utils? Это пакет, содержащий базовые и общие классы, которые используются более чем в двух классах.
Episode («Серия») и Character («Персонаж») содержат данные, предметную область (бизнес-логику) и представление:
Эти пакеты будут выглядеть так:
Пакет API содержит интерфейс «CharacterApi», который является лишь методом взаимодействия с сервером, и «CharacterApiImpl» для реализации этого взаимодействия.
Пакет базы данных содержит интерфейс «Dao»:
В «CharacterRepositoryImpl» вызываем только те методы, которые нам нужны. Здесь нет бизнес-логики.
В предметной области мы определяем модель, которую надо сохранить в базе данных:
Репозиторий — это интерфейс, реализуемый в приведённом выше классе.
Прецедент, подобный упомянутому ранее, выполняет только одну задачу. Например, получение данных с сервера с сохранением их в базе данных.
Мы используем запечатанный класс для передачи данных и наблюдения за ними во ViewModel. И передаём прецедент конструктору для взаимодействия между этими двумя классами.
Для внедрения зависимостей используем hilt, поэтому пакета di нет в Episode («Серии») и Character («Персонаже»), но в util мы определяем «NetworkModule»:
И определяем «AppModule» в utils следующим образом:
А в пакете репозитория в utils определяем модуль репозитория:
Заключение
Итак, мы узнали, как это здорово — разрабатывать свой проект с чистой архитектурой для обеспечения лучшей тестопригодности с возможностью повторного использования. Кроме того, мы теперь можем изменить код, не беспокоясь о том, что станет с остальной частью проекта.
Надеюсь, статья была полезной. Проект с чистой архитектурой загружен на GitHub.
Читайте также:
Перевод статьи Golnaz Torabi: Clean Architecture with MVVM