Приложение разделено на слои:
- data
- models
- presentation
Data слой отвечает за получение / изменение / удаление данных. Основные его классы - репозитории. Задача репозитория состоит в том, чтобы взять данные из доступного источника. Например, если у нас есть сервер и локальная база данных, то репозиторий сначала обратится к классу, отвечающему за соединение с интернетом, и если интернет есть - сходит в сеть. Если интернета нет - возьмёт данные из локальной базы.
В ситуации, когда нам нужно сгенерировать рандомные / тестовые данные, мы создаем их в репозитории. Это удобно тем, что в любой момент можно изменить источник данных, и остальные компоненты приложения не пострадают от этого.
Репозиторий может
- брать данные
- сравнивать их (когда мы передаем объект на апдейт, репозиторий может сравнить текущий объект и новый, и если они одинаковые, лишний раз не перезаписывать данные)
- принимать решение о том, откуда взять данные (локальная бд, сеть, преференсы)
- обращаться к другим репозиториям за данными ( в такой ситуации можно создать медиатор - класс, который будет вызывать сначала один, потом второй репозиторий. )
Domain слой - хоть его и нет в примере, о нем стоит помнить. В этом слое хранится бизнес-логика приложения. Это оптимально в том случае, если наша вьюмодель слишком большая, и мы хотим разгрузить её, вынеся логику в несколько классов-юзкейсов.
Presentation слой - здесь хранятся все классы, отвечающие за пользовательский интерфейс. Активити, фрагменты, диалоги, и так далее. Задачи этого слоя:
- получить данные (неважно откуда, вьюмодель, репозиторий, или ещё что-то)
- показать их
- получить данные о вьюшках и изменить их вид (например из вьюмодели может придти сообщение (в виде livedata) о том, что юзер не заполнил поле "имя", и пытается сохранить данные). В таком случае наша Активити или Фрагмент должны принять эти данные и показать, например, Snackbar или Toast, уведомить юзера о том, что нужно заполнить это поле.
Почему бы просто не проверить данные на месте, во фрагменте? Потому что со временем количество кода во фрагменте будет расти, разные методы будут отвечать за разные вещи, и будет непросто разобраться, что вообще происходит. А если логика проверки данных изменится, будет намного сложнее найти тот кусок кода, который отвечает за проверку.
Вьюмодель - это паттерн для связи вьюшек и логики / данных, который в андроиде реализован классом ViewModel()
, от которого должны наследоваться все наши вьюмодели.
Задача вьюмодели - получить данные из фрамента / активити, проверить их, если будет найдена ошибка - уведомить фрагмент / активити через livedata.
Также вьюмодель ответственна за получение данных из репозиториев. Ей неважно, откуда приходят данные - из сети, из БД, или это просто руками сгенерированные рандомные данные. Важно получить их, и передать во фрагмент / активити через livedata.
LiveData
- этот класс реализует паттерн Observer. Создается во вьюмодели, а дальше на неё могут подписаться активити / фрагменты, и они будут уведомлены, когда данные изменятся. Хорошая практика: оставлять livedata закрытой везде кроме вьюмодели.
Например, фрагмент не должен делать этого:
someLiveData.value = 123
Вместо этого, стоит создать отдельный метод во вьюмодели, который будет отвечать за изменение livedata:
fun changeLiveData(value: Int) { someLiveData.value = value }
Почему? Потому что первый вариант выходит за пределы зоны ответственности фрагмента. Он не должен менять данные и иметь логику, не относящуюся к вьюшкам. А если у нас есть метод, который принимает значение, и ставит его во вьюмодели - мы можем в будущем написать какую-нибудь проверку или другую логику, а во фрагменте ничего не изменится - он как отдавал данные во вьюмодель, так и отдает.
Вообще, главный смысл чистой архитектуры - удобство. Плохо когда добавляешь фичу, а приложение ломается в десяти местах. Приходится тратить много времени на фикс багов, значительно больше, чем изначально ушло бы на "скелет" чистой архитектуры.
В основе такой архитектуры лежат принципы, но не правила. Нет единственно верного или в корне неверного решения (если оно не нарушает общие принципы).