Привет! Вы уже прекрасно знакомы с тем, как грамотно реализовать View слой в любой архитектуре. Но как быть со слоем model? Да, мы можем воссоздать кучу утильных классов, сделать data_manager и там описывать всю бизнес-логику, но кому понравится смотреть data_manager, который содержит огромную кучу плохо навигируемого кода. Тут нам на помощь приходит Clean Architecture, который раскладывает всю бизнес-логику и взаимодействия с ней по полочкам. Давайте начнем. В 2012 году Англ Боб, Роберт Мартин, дядюшка Боб, опубликовал статью The Clean Architecture, которая и является основным описанием этого подхода. Разберем все термины, которые используются в данной диаграмме. Entities - сущности, это бизнес-логика приложения. Use Cases - методы использования. Эти методы организуют поток данных в Entities и из них. Так же их называют Interactors - посредники. Interface Adapters - Интерфейс-Адаптеры. Этот набор адаптеров преобразовывает данные в формат удобный для методов использования и сущности. К этим адаптерам принадлежат призенторы и контроллеры. Frameworks and Drivers - место скопления деталей. Это у нас UI, инструменты, фреймворки, базы данных и так далее. Теперь к сути. Clean Architecture - это идеология, такая же как Material Design. Её суть заключается в построении правильных зависимостей между слоями. Это называется Dependency Rule. Каждый слой архитектуры должен иметь только внутренние зависимости и ни одной внешней, это касается функций, классов, переменных и так далее. Это правило позволяет строить системы, которые будет проще поддерживать, потому что изменения во внешних слоях не затронут внутренние слои. На диаграмме изображено четыре круга, т.е. четыре слоя архитектуры. Слоев необязательно должно быть четыре. Вы можете добавлять, либо убавлять слои на свое усмотрение, как вам будет угодно. У каждого слоя должна быть своя конкретная ответственность и он не может делать сразу всю работу приложения. Clean Architecture объединил в себе идеи нескольких других архитектурных подходов, которые сходятся к тому, что архитектура должна быть тестируемой. Бизнес-правила могут быть протестированы без пользовательского интерфейса, базы данных, веб-сервера или любого другого внешнего компонента, не зависеть от UI. Пользовательский интерфейс можно легко изменить не изменяя остальную систему, например, веб интерфейс может быть заменен на консольный, без изменения бизнес-правил. Не зависеть от базы данных. Вы можете поменять Oracle или SQL Server на Mongo DB, Big Table, Cash DB или что там ещё. Ваши бизнес-правила не связаны с базой данных. Не зависеть от внешних фреймворков и библиотек. Архитектура не зависит от существования, какой-либо библиотеки. Это позволяет использовать фреймворк в качестве инструмента, вместо того, чтобы втискивать свою систему в рамки его ограничения и не зависеть от какого-либо внешнего сервиса. По факту, ваше бизнес-правило ничего не знает о внешнем мире. С теорией покончено! Надеюсь суть вы уловили. Теперь перейдем к практике. Clean Architecture получило широкое распространение в Android, после того как парень по имени Фернандо Цехас выпустил в свет статью Architecting Android the clean way. В ней он описывает примеры реализации идеологии Clean Architecture в Android. В этой статье Фернандо приводит такую схему соловьёв. То что на этой схеме другие слои, а в Domain слое лежат ещё какие-то Boudaries, сбивает с толку. Проект разбит на три слоя, это слой данных Data Layer, слой бизнес-логики Domain Layer и слой представления Presentation Layer. Каждый из этих слоев имеет свою цель и может работать независимо от остальных. Давайте разберем их. Тут все просто. Это слой, в котором вы реализуете один из MV паттернов на свой выбор. Что хотите то и используете MVP, MVVM любые другие. Фрагменты и activity это всего лишь view. В них нет никакой логики кроме логики UI и отрисовки этого самого отображения. Этот слой модуль на чистой Java, без каких-либо Android зависимостей. Все внешние компоненты используют интерфейсы для связи с бизнес объектами. Вся логика реализована в этом слое. Тут у нас хранятся классы с которыми работают другие положения, различных интерфейсы Interactions или по другому Use Case нашего приложения. Interaction выражаясь простым языком представляет из себя класс, который выполняет одно конкретное действия над данными. Например, у нас есть объект класса user и нам нужно отредактировать его. Для этого создается Interaction, user interaction и внутри этого класса задается метод edit_user и пишется обработка нашего user. Все данные необходимые для приложения поставляются из этого слоя, через реализацию репозитория, который использует Repository паттерн со стратегией, которая через фабрику выбирает различные источники данных в зависимости от определенных условий. Интерфейс находится в Domain слое. Наверняка многие из вас слышали и даже использовали этот паттерн. На самом деле здесь у вас есть несколько вариантов того как вы реализуете работу с различными хранилищами данных. Вы можете добавить логику работы с разными с разными репозиториями сервисы или Interaction. Если же эта логика останется в репозиториях, тогда вам нужно будет просто обрабатывать данные в интеракторах. Теперь давайте рассмотрим, как эти слои реализуются в Android приложении. Пряморукие и грамотные специалисты делают это вот так. Просто создают зависимости на модули как указано этой схеме. То есть Domain не знает ни об одном модуле. Data знает только о Domain. Presentation знает и о Data, и о Domain. Все просто! Модуль Domain это Java модули. Data и Presentation - это Android модули. Также все эти зависимости между модулями нужно прописать Gradle, как показано на схеме. Такая жесткая установка зависимостей и разделение модулей на Java и Android позволит не ошибиться не опытнымe специалисту. Если вы захотите написать Android код в Domain у вас ничего не выйдет и также ничего не выйдет, если вы из Domain захотите обратиться к Data или Presentation слою. Лично мое мнение, что этот подход красив и безопасен. Теперь давайте посмотрим, как гуляют данные по приложению в Clean Architecture. Допустим, мы нажали на кнопочку или просто зашли на экран и запрашиваем получение данных. Наш запрос из View идет в Presenter. в Presenter мы вызываем необходимый Use Case или Interaction, он выполняет всю работу над данными, идет в Service и опять выполняет работу над данными, если необходимо. Service по своей логике определяет с каким репозиторием или репозиториями он работает. И репозиторий уже предоставляет нам результат запроса и все возвращается обратно. Схема проста, но напомню, что вы можете отказаться от каких-либо слоев и добавлять свои. Эта схема всего лишь классический пример. Давайте посмотрим немного кода. Вот так выглядит интерфейс Interactor. Все просто, есть интерфейс callback, который будет сообщать Presentor о результате, когда вернется ответ от репозитория и сервиса. В данном примере это может быть ошибка или успех. И есть метод Execute, который, соответственно, нужно реализовать. Сейчас посмотрим как это делается. Вот так работает Use Case. Это пример реализации метода Execute. На вход он принимает какие-то данные и Callback для Response. В данном примере используются не самые красивые методы обработки ошибки. Лучше всего его переделать, потому что в каждом Use Case писать такой body re pleat, не лучшая практика. В наш успешный callback засовываем объект, который мы получаем. Далее он идет Presentor. Простая схема. А теперь представьте, что вы пишете приложение в котором как минимум 20 разных объектов, которые требуют обработки. С этими объектами нужно сделать кучу разных операций, сортировка, добавление, изменения каких-то полей и так далее. Из этого получается 100 с лишним Interaction, еще представьте, что вам нужно использовать 15-20 Interaction в одном Presenter. Представили? Это боль! Все это делает проект нереально огромным, это неудобно. Уверен, что все кто пользовались таким подходом отказались от него и пришли к чему-то иному, так же поступили и мы. Гораздо удобнее объединять наши Interaction в один класс со своими методами для обработки данных. Это простой пример того, как будут выглядеть методы внутри репозитория, который работает с разными типами хранилищ. А вот так может выглядеть наше объединение Interaction в один класс Interaction. На этом всё. От себя хотелось бы добавить, что лично я для себя выделил одно правило, к которому я пришел опытным путём. Вы делать всё что угодно с вашим кодом и архитектурой. Главное, чтобы это было максимально удобно и красиво, но при этом всегда стоит задуматься о том, на какие грабли вы можете наткнуться и что может пойти не так. В первую очередь думайте о минусах и придумайте, как их превратить в плюсы.