Чистая архитектура в Kotlin и Jetpack Compose
Используя чистую архитектуру, вы можете разрабатывать приложения с очень низкой степенью связанности и независимыми от деталей технической реализации, таких как базы данных и фреймворки. Таким образом, приложение становится простым в обслуживании и гибким для изменений. Это также становится внутренне проверяемым. Здесь я покажу, как я структурирую свои проекты чистой архитектуры. На этот раз мы собираемся создать Android-приложение todo с помощью Jetpack Compose. Мы проиллюстрируем только один пример использования списка задач, полученных из API. Давайте начнем.
Структура пакета проекта принимает следующий вид:
├── Core ├── Data ├── Domain └── Presentation
Начнем со слоя Domain.
Этот уровень описывает ЧТО делает ваш проект/приложение. Позвольте мне объяснить, многие приложения построены и структурированы таким образом, что вы не можете понять, что делает приложение, просто взглянув на структуру папок. Используя аналогию здания с домом, вы можете быстро определить, как будет выглядеть здание и его функциональность, просмотрев план этажа и высоту здания.
Точно так же уровень предметной области нашего проекта должен указывать и описывать ЧТО делает наше приложение. В этой папке мы будем хранить модели использования, интерфейсы репозитория и варианты использования.
├── Core ├── Data ├── Presentation └── Domain ├── Model │ ├── Todo.kt │ └── User.kt ├── Repository │ ├── TodoRepository.kt │ └── UserRepository.kt └── UseCase ├── Todo │ ├── GetTodos.kt │ ├── GetTodo.kt │ ├── DeleteTodo.kt │ ├── UpdateTodo.kt │ └── CreateTodo.kt └── User ├── GetUsers.kt ├── GetUser.kt ├── DeleteUser.kt ├── UpdateUser.kt └── CreateUser.kt
- Модель. Модель обычно представляет реальный объект, связанный с проблемой. В этой папке мы обычно храним классы для представления объектов. например Todo, пользователь и т. д.
- Репозиторий: контейнер для всех интерфейсов репозитория. Репозиторий — это центральное место для хранения всех операций, связанных с моделью. В этом случае интерфейс репозитория Todo будет описывать методы репозитория. Фактическая реализация репозитория будет храниться на уровне Data.
- UseCases:контейнер для перечисления всех функций нашего приложения. например Получить, Удалить, Создать, Обновить
Уровень ПРЕЗЕНТАЦИЯ будет содержать весь код, связанный с потребителем, относительно КАК приложение будет взаимодействовать с внешним миром. Уровень представления может быть веб-формами, интерфейсом командной строки, конечными точками API и т. д. В этом случае это будут экраны для списка задач и сопровождающей его модели представления.
├── Core ├── Data ├── Domain └── Presentation └── Todo └── TodoList ├── TodoListViewModel.kt └── TodoListView.kt
Уровень DATA будет хранить весь код, связанный с внешними зависимостями, в том виде, в котором он КАК реализован:
├── Core ├── Domain ├── Presentation ├── Data ├── Repository │ ├── TodoRepositoryImpl.kt │ ├── TodoAPIDataSourceImpl.kt │ └── TodoDBDataSourceImpl.kt └── DataSource ├── API │ ├── TodoAPIDataSource.kt. │ └── Entity │ ├── TodoAPIEntity.kt │ └── UserAPIEntity.kt └── DB ├── TodoDBDataSource.kt. └── Entity ├── TodoDBEntity.kt └── UserDBEntity.kt
- Репозиторий: реализации репозитория
- Источник данных: все интерфейсы источника данных и сущности. Сущность представляет собой отдельный экземпляр объекта домена, сохраненный в базе данных в виде записи. У него есть некоторые атрибуты, которые мы представляем в виде столбцов в наших таблицах БД или конечных точках API. Мы не можем контролировать, как данные моделируются во внешнем источнике данных, поэтому эти сущности необходимо отображать из сущностей в модели предметной области в реализациях.
и, наконец, уровень CORE содержит все компоненты, которые являются общими для всех уровней, такие как константы, конфигурации или внедрение зависимостей (которые мы не будем рассматривать).
Нашей первой задачей всегда будет начинать с моделей предметной области и объектов данных.
Давайте теперь напишем интерфейс для TodoDatasource. Нам нужен один, чтобы обеспечить поведение любого источника данных (API, БД и т. д.).
interface TodoDataSource { suspend fun getTodos(): List<Todo> }
У нас достаточно, чтобы написать реализацию этого интерфейса, и мы назовем ее TodoAPIImpl:
Примечание. функция getTodos этого репозитория возвращает список задач. Итак, нам нужно сопоставить TodoAPIEntity -> Todo:
Прежде чем мы напишем наш TodoRepositoryImpl, давайте напишем интерфейс для него на уровне домена.
Теперь мы видим, что TodoRepositoryImpl может принимать любой источник данных в качестве зависимости, что отлично подходит для замены источников данных.
Теперь, когда у нас есть наш репозиторий todo, мы можем написать сценарий использования GetTodos.
а затем, в свою очередь, мы можем написать модель представления нашей презентации и представление
Итак, резюмируя: