CLEAN ARCHITECTURE — overview

Сразу предупрежу — это вводная статья на тему чистой архитектуры. Здесь вы не найдете все ответы на ваши возможные вопросы. Возможно в последующей статье — планируется серия.

Итак, наверно каждый андроид разработчик слышал про такое понятие как чистая архитектура (Clean Architecture). Если вы не слышали до сих пор, значит вы, скорей всего, живете в бункере с 2012 года.

Ранее мы говорили о таких патернах архитектуры как MVC, MVP, MVVM. И кто-то может сказать — у нас есть патерны архитектуры, мы разделяем наш код на 3 класса — model, view и что-то еще. И это первое самое большое недопонимание. Патерны архитектуры MV* являются лишь способом выстроить логику для отображения. Если вам кажется, что вам достаточно 3 классов для того, чтобы ваш код работал, то да, возможно это так. Но ведь в принципе достаточно и 1 класса — hello god object Activity. Дело в том, что если мы будем писать весь код, который отвечает за данные в одном классе, то он будет слишком громоздский.

В чем проблема больших классов? Во-первых их сложно читать. Во-вторых сложно вносить правки. В-третьих, чем больше кода, тем сложней писать тесты на класс.

И здесь мы дадим некоторое определение чистого кода (если вы не читали книгу Роберта Мартина — Чистый код, то сейчас самое время). Чистым можно считать тот код, который легко читать (названия классов, методов, переменных настолько понятны, что даже джавадоки не нужны, а комментарии тем более), нетрудно понять суть, можно легко вносить изменения не боясь ошибок, которые могут возникнуть в другом месте — т.е. слабосвязанный код. Плюс конечно же класс должен быть тестируем. И не стоит забывать о таких принципах разработки как SOLID, DRY, KISS, YAGNI.

И этого всего недостаточно. Ведь если в патернах MV* мы разделяли все на 3 слоя — отображение, данные и логика отображения, то в рамках CLEAN architecture мы будем разделять весь код на условные 3 слоя — data, domain, presentation. И самое сложное на мой взгляд во всей этой истории — понимание этих слоев. Кто за что отвечает.

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

Главное для каждого разработчика, на мой взгляд, это найти такое решение в плане архитектуры, которое самым удачным способом подходит в рамках конкретной задачи/проекта. Многие люди узнав о чистой архитектуре пишут условные хеловорлды не за 5 минут, а за 50. Продуктовой ценности в этом конечно же не так много. Плюс затраты по времени не оправданы. Поэтому — как сказал один умный разработчик (Сонмез) — не будьте религиозны по поводу технологий.

Теперь, давайте попробуем разобраться в этих понятиях 3 слоев. Data — данные. Здесь вроде как все понятно, но нет. Дело в том, что термин данные имеет слишком широкий круг использования. Мы употребляем этот термин повсеместно. И поэтому нужно уточнение. Что есть данные в чистой архитектуре. Ведь данные действительно разные — данные пользовательского ввода, данные полученные от сервера, данные промежуточного слоя (некий буфер данных).

С первым видом данных все вроде понятно — данные пользовательского ввода. Так как они получены непосредственно пользователем, то мы поговорим о них позже, не наш случай. Данные среднего звена — тоже не то (об этом позже). Данные для нашего слоя data мы будем считать те данные, которые пользователь по сути и не видит. Например данные, полученные от сервера в сыром виде, данные полученные от локальной базы данных, данные (далее информация) любого рода, которые не видны конечному пользователю в том виде, в котором они получены. Также к слою данных можно отнести такие вещи как ошибки, в том виде, в котором они пользователю не особо интересны, плюс конечно же они не удобочитаемы для рядового разработчика или человека со стороны, который случайно открыл ваш проект (например ошибка сетевая 404, 500, любые наследники Throwable).

И на этой ноте давайте сакцентируем внимание. Следующий слой это domain. В простонародии мы называем его — слой бизнес логики. И это наверно самый сложный к пониманию слой. Ведь если с данными более менее понятно — данные из сети, базы данных, их ошибки в чистом виде (джава Exception), то что такое бизнес логика и что есть данные бизнес логики? Удобочитаемый код даже для нетехнических профессий. Мне кажется это могло быть одним из лучших определений домейн слоя. В слое бизнес логики не должно быть ничего из всего того, что вы написали в дата слое. Никаких упоминаний баз данных, сетевых запросов и их ошибок. В домейн слое должны быть максимально дружелюбные и удобочитаемы для человека (нетехнического) классы и методы.

Для примера возьмем любое приложение. Наш продакт оунер( или кто у вас отвечает за бизнес требования) говорит — нужен экран, на котором мы покажем пользователю все его сообщения. Также в случае ошибок — ошибки. Предположим мы упростили наш экран до 2 видов ошибок — нет соединения с интернетом и сервер недоступен. Здесь мы не будем говорить о том, что нет соединения с интернетом ни через 4г ни через вайфай, это неважно. Нет соедиения и точка. Каким образом мы все проверили — все в дата слое. Наш дата слой отвечает за это — какими способами мы проверяем наши данные о наличие соедиения с интернет. И здесь самое время сказать о таких вещах как маперы. Ведь с одной стороны у нас данные от устройства об отсутствии сети или того, что сервер недоступен (ошибки 400, 500), а с другой стороны нам нужны ясные и понятные классы для этих вещей. Тогда мы создаем классы бизнес логики под это дело и пишем мапер, который исходя из логики данных будет предоставлять нам тот или иной класс бизнес логики — ServiceUnavailable, NetworkConnection, etc.

То есть человек, который будет читать ваш код, пусть даже не разработчик, должен видеть нечто такое — получаем сообщения, можем получить или их или ошибки 2 видов. Ему должно быть все понятно. Именно поэтому я и придерживаюсь того, что юнит тесты может писать на ваш код кто-то другой, например тестировщик. Ведь код понятен без лишних слов. Все классы и методы названы просто и понятно. Класс сообщения, в котором такие вещи как тело сообщения, флаг доставлено/прочитано собеседником, плюс время отправления в нормальном виде, например ISO8601, а не миллисекунды, которые могут быть в дата слое (предположим вам сервер отправил время в миллисекундах, не знаю).

Чтобы суметь отделить зерна от плевел — бизнес логику от всего остального(спойлер — логика отображения) — нужно просто внимательно послушать человека, который пишет вам таску. Получить сообщения и показать их или же ошибки 2 видов. Все. Каким образом например вы покажете время отправления или же ошибку (полноэкранным сообщением или всплывающим) — это уже не бизнес логика, а логика отображения. Понятийно у вас есть такое существо как сообщение и у него есть несколько ингредиентов. Каким образом эти поля наполняются — вопрос к маперу, который неважно откуда возьмет эти данные (от сервера или из кеша) и проверив их валидность создаст модель бизнес логики или же породит ошибку. Кстати, ровно так же нам неважно на уровне бизнес логики каким образом отобразим ошибку и какой у нее будет текст,стиль и т.д. это все логика отображения.

Итак, вроде разобрались, у нас есть данные откуда-то там в каком-то непонятном для простого человека виде в слое дата, которые мы с помощью маперов трансформируем в удобочитаемый код для нетехнарей. Дальше нам осталось сделать одну простую вещь — показать что-то в каком-то виде пользователю. И это наш presentation слой. Вот здесь уже пишем наши любимые viewModel, activity/fragment. Но опять же нужно из тех классов, которые хранят данные бизнес логики перебросить в слой презентационный. И опять пишем маперы. Маперы от слоя бизнес логики к слою презентации простые — например если нам пришла ошибка об отсутствии соединения с интернетом — мы порождаем некий класс, который хранит в себе (например) строчку (на разных языках, это же андроид) в которой сам текст ошибки. Может быть еще и изображение какое-то. Этим всем занимается вьюмодель или презентер, что там у вас. Он отдаст в активити/фрагмент ваше сообщение — R.string.connection_error, а там уже вы с помощью Toast или наследников View отобразите юзеру эту ошибку.

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

Если у вас есть вопросы и/или пожелания к последующей статье (например вы бы хотели более подробно что-либо из чистой архитектуры рассмотреть или же у вас специфичный случай и вы не знаете к какому слою относится эта логика) то можете написать мне в телеграм @JohnnySC

Запись опубликована в рубрике Программирование Android. Добавьте в закладки постоянную ссылку.