архитектура мобильных приложений виды характеристика компоненты
Архитектура и дизайн Android приложения (мой опыт)
Сегодня я хочу рассказать об архитектуре, которой я следую в своих Android приложениях. За основу я беру Clean Architecture, а в качестве инструментов использую Android Architecture Components (ViewModel, LiveData, LiveEvent) + Kotlin Coroutines. К статье прилагается код вымышленного примера, который доступен на GitHub.
Disclaimer
Проблема: зачем нам нужна архитектура?
Большинство проектов, в которых мне доводилось участвовать, имеют одну и ту же проблему: внутрь андроид окружения помещается логика приложения, что приводит к большому объему кода внутри Fragment и Activity. Таким образом код обрастает зависимостями, которые совсем не нужны, модульное тестирование становится практически невозможным, так же, как и повторное использование. Фрагменты со временем становятся God-объектами, даже мелкие изменения приводят к ошибкам, поддерживать проект становится дорого и эмоционально затратно.
Есть проекты, которые вообще не имеют никакой архитектуры (тут все понятно, к ним вопросов нет), есть проекты с претензией на архитектуру, но там все равно появляются точно такие же проблемы. Сейчас модно использовать Clean Architecture в Android. Часто видел, что Clean Architecture ограничивается созданием репозиториев и сценариев, которые вызывают эти репозитории и больше ничего не делают. Того хуже: такие сценарии возвращают модели из вызываемых репозиториев. И в такой архитектуре смысла нет вообще. И т.к. сценарии просто вызывают нужные репозитории, то часто логика ложится на ViewModel или, еще хуже, оседает во фрагментах и активностях. Все это потом превращается в кашу, не поддающуюся автоматическому тестированию.
Цель архитектуры и дизайна
Цель архитектуры – отделить нашу бизнес-логику от деталей. Под деталями я понимаю, например, внешние API (когда мы разрабатываем клиент для REST сервиса), Android – окружение (UI, сервисы) и т.д. В основе я использую Clean architecture, но со своими допущениями в реализации.
Цель дизайна – связать вместе UI, API, Бизнес-логику, модели так, чтобы все это поддавалось автоматическому тестированию, было слабо связанным, легко расширялось. В дизайне я использую Android Architecture Components.
Для меня архитектура должна удовлетворять следующим критериям:
Решение
Принципиальная схема архитектуры представлена на рисунке ниже:
Мы движемся снизу вверх по слоям, и слой, который находится ниже, ничего не знает о слое сверху. А верхний слой ссылается только на слой, который находится на один уровень ниже. Т.е. слой API не может ссылаться на домен.
Слой домен содержит бизнес сущности со своей логикой. Обычно здесь находятся сущности, которые существуют и без приложения. Например, для банка здесь могут находиться сущности кредитов со сложной логикой расчета процентов и т.д.
Слой логики приложения содержит сценарии работы самого приложения. Именно здесь определяются все связи приложения, выстраивается его суть.
Слой api, android – это лишь конкретная реализация нашего приложения в Android – среде. В идеале этот слой можно менять на что угодно.
Причем, когда я приступаю к разработке приложения, я начинаю с самого нижнего слоя — домена. Потом появляется второй слой сценариев. На 2-ом слое все зависимости от внешних деталей реализуются через интерфейсы. Вы абстрагированы от деталей, можно сконцентрироваться только на логике приложения. Тут же уже можно начинать писать тесты. Это не TDD подход, но близко к этому. И только в самом конце появляется сам Android, API с реальными данными и т.д.
Теперь более развернутая схема дизайна Android-приложения.
Итак, слой логики является ключевым, он и есть приложение. Только слой логики может ссылаться на домен и взаимодействовать с ним. Также слой логики содержит интерфейсы, которые позволяют логике взаимодействовать с деталями приложения (api, android и т.д.). Это так называемый принцип инверсии зависимости, который позволяет логике не зависеть от деталей, а наоборот. Слой логики содержит в себе сценарии использования приложения (Use Cases), которые оперируют разными данными, взаимодействуют с доменом, репозиториями и т.д. В разработке мне нравится мыслить сценариями. На каждое действие пользователя или событие от системы запускается некоторый сценарий, который имеет входные и выходные параметры, а также всего лишь один метод – запустить сценарий.
Кто-то вводит дополнительное понятие интерактора, который может объединять несколько сценариев использования и накручивать дополнительную логику. Но я этого не делаю, я считаю, что каждый сценарий может расширять или включать любой другой сценарий, для этого не нужен интерактор. Если посмотреть на схемы UML, то там можно увидеть связи включения и расширения.
Общая схема работы приложения следующая:
UI запускает выполнение сценария с помощью команды. В моих проектах я использую собственную реализацию команд, они не являются частью архитектурных компонент или еще чего-либо. В общем, их реализация несложная, в качестве более глубокого знакомства с идеей можете посмотреть реализацию команд в reactiveui.net для C#. Я, к сожалению, не могу выложить свой рабочий код, только упрощенную реализацию для примера.
Основная задача команды — это запускать некоторый сценарий, передав в него входные параметры, а после выполнения вернуть результат команды (данные или сообщение об ошибке). Обычно все команды выполняются асинхронно. Причем команда инкапсулирует метод background-вычислений. Я использую корутины, но их легко заменить на RX, и сделать это придется всего лишь в абстрактной связке command+use case. В качестве бонуса команда может сообщать свое состояние: выполняется ли она сейчас или нет и может ли она выполниться в принципе. Команды легко решают некоторые проблемы, например, проблему двойного вызова (когда пользователь кликнул несколько раз на кнопку, пока операция выполняется) или проблемы видимости и отмены выполнения.
Пример
Реализовать фичу: вход в приложение с помощью логина и пароля.
Окно должно содержать поля ввода логина и пароля и кнопку “Вход”. Логика работы следующая:
MainActivity выглядит следующим образом:
Активити достаточно прост, правило UI выполняется. Я написал несколько простых расширений, типа bindVisibleWithCommandIsExecuting, чтобы связывать команды с элементами UI и не дублировать код.
Код этого примера с комментариями доступен на GitHub, если интересно, можете скачать и ознакомиться.
Как проектируют приложения: разбираемся в архитектуре
Старший iOS-разработчик из «ВКонтакте» рассказывает, почему архитектура не главное в проекте и как сделать продукт поддерживаемым и масштабируемым.
Катя Павловская для Skillbox Media
Евгений Ёлчев
Старший iOS-разработчик во «ВКонтакте». Раньше был фулстеком, бэкендером и DevOps, руководил отделом мобильной разработки, три года преподавал iOS-разработку в GeekBrains, был деканом факультета. Состоит в программном комитете конференции Podlodka iOS Crew, ведёт YouTube-канал с видеоуроками по Flutter. В Twitter пишет под ником @tygeddar.
Я люблю спорить о том, какая архитектура лучше. Может, из-за своего внутреннего перфекциониста или диплома архитектора информационных систем, а может, потому, что мне лень копаться в плохих проектах.
Спойлер: больше всего я люблю архитектуру MVC. Дальше расскажу, как она работает и почему мне не нравятся всякие MVVM, MVP и VIPER. Кстати, недавно я разобрался во Flux и её имплементации Redux и понял, что их я тоже недолюбливаю.
В основе статьи — тред автора в Twitter.
Что такое архитектура MVC
Я тогда был студентом, делал курсовые и пет-проекты. У них была сложная вёрстка и непростая структура БД, но максимально простая логика. Код получался простым, но в целом меня такой подход устраивал.
Мне не приходило в голову, что можно делать не так, как написано в документации к инструментам и в примерах на форумах. Я был счастлив и не забивал голову чепухой.
Я пробовал поменять логику в своём проекте — перенёс её из одного файла в другой, но разницы не заметил. По факту ничего и не изменилось, только код теперь лежал в другом файле.
Изучая дискуссии в интернете и рассуждая самостоятельно, я понял, что «толстая» модель мне не нравится. Пусть лучше модель остаётся базой данных, а контроллер и дальше управляет логикой.
Со временем мои проекты становились всё сложнее, а контроллеры пухлее (правда, не как UIViewController в iOS). Я пробовал с этим бороться, выносил логику в сторонние файлы, которые включал в контроллеры, но это мало что меняло: архитектура сохранялась, просто код переносился из одного файла в другой.
Почему MVC не работала в моих проектах
В 2013 году я пересел на Laravel, разобрался с автозагрузкой классов в PHP, начал разбираться с ООП и прочитал «Совершенный код» Стива Макконнелла.
Стало ясно, что не стоит складывать всё в один файл — код и классы должны организовывать структуру, а некоторые фрагменты кода лучше убрать из MVC и выделить в самостоятельные части, которые можно переиспользовать.
С этого момента я начал писать проекты по-другому. В них появились иерархии классов, которые хранили логику, а контроллер сильно похудел — он получал данные от базы, передавал их в разные пакеты, получал от них результат и отправлял на HTML-страницу.
Архитектура проектов не была идеальной, потому что не подошла бы ни для одной сложной системы. Но для моих целей она была крутой и удобной: код получался читабельный, а все элементы проекта оставались довольно независимы.
Как я делал систему управления
VDS-сервером
В сложной системе нельзя передавать все данные через один контроллер, поэтому каждый плагин отдельно реализовывал веб- и API-интерфейсы, доступ к данным и бизнес-логику, вынесенную в пакеты для переиспользования.
Получилось так: HTML ⟷ JavaScript (модели, общение с API) ⟷ API ⟷ переиспользуемые пакеты ⟷ бизнес-логика и доступ к данным. Всё это не было похоже на MVC.
Почему архитектура не главное в проекте
Когда я увидел, как это реализуют в других компаниях, то осознал несколько важных нюансов.
Архитектура не даёт преимуществ. Ни одна продвинутая архитектура не была лучше того, что я делал в самом начале. За всё время я перепробовал разные подходы и поэтому мог оценить их пользу для проекта. Но между MVC и MVP не было разницы — кроме названий классов и правил вроде тех, когда элементы вызывают друг друга.
Компании понимают архитектуру по-разному. Одни говорят, что используют MVVM, у других то же самое называется MVC. Я видел пять MVVM-систем, и все были разными. Исключение — VIPER, у которой благодаря Егору Толстому есть подробная документация и много примеров. Но даже там были отличия.
Популярная архитектура не значит лучшая. Выбирать архитектуру из-за мейнстримности бесполезно. Кто-то решает использовать MVVM, но одни и те же компоненты кладёт в разные части проекта.
Архитектура не спасёт проект. Сама по себе она не решает проблемы и не гарантирует успеха.
Что же такое MVC на самом деле
Я постоянно изучал архитектуры, читал книги и спорил с коллегами, несколько раз пересматривал идею MVC в языке Smalltalk и несколько раз менял к ней отношение.
В итоге я понял, что MVC — это не три файла, и даже не несколько классов для каждого элемента. Модель — не про данные и не про бизнес-логику, а контроллер давно не нужен, и пора использовать MV.
Приложения с бизнес-логикой и доступом к данным были и до MVC, им не хватало только пользовательского интерфейса. Главная задача MVC — связать UI со всем остальным. Единственная рекомендация от создателя — при надобности создавать для каждой View свой фасад для Model и слушать его через паттерн-наблюдатель.
View — это и есть пользовательский интерфейс, Model — остальное приложение. Задача Controller — не быть прослойкой между V и M, а всего лишь принимать информацию от пользователя.
Принцип MVC — не мешать UI с бизнес-логикой, базой данных и другими частями приложения. А как это реализовать, уже пускай думает архитектор. Это не космическая инженерия.
Важно понимать, что MVP, MVVM или VIPER не заменяют MVC, а только дополняют её. Контроллер уже не нужен, потому что за ввод данных отвечает View, это стало его неотъемлемой частью.
Получается, что MVC в Apple, MVVM и другие варианты — это MV, где контроллер убрали за ненадобностью. Из всех современных MV(x) именно MVVM больше всего похожа на каноническую MVC.
Все эти термины усложняют общение. Иногда сложно понять, о чём тебе говорят, хотя задача архитектуры в том, чтобы всё было проще и понятнее.
Как разобраться в любой архитектуре
Может показаться, что все архитектуры одинаковые, и им вообще не стоит уделять внимания. Но это не так. У меня есть несколько правил.
Главное — реализация. Глобальная архитектура не так важна, как её воплощение. Всё зависит от того, как вы называете классы, где храните элементы и как классы общаются между собой. Все из команды должны соблюдать ваш стандарт, и тогда проект будет проще поддерживать.
Model — ваша ответственность. Архитектура MVC не даёт инструкций, как правильно написать основную часть приложения. Ваша ответственность в том, чтобы не устраивать в Model кашу, где половина классов — Service, а вторая половина — Helper.
Нужно разбираться в основах. Не стоит изучать конкретную архитектуру, лучше понять, из чего она логически следует. Тут поможет история, объектно-ориентированное и функциональное программирование, паттерны, SOLID и всё остальное. Обязательно надо прочитать «Совершенный код» Стива Макконнелла.
С какого-то момента я начал теряться в проекте. У меня появилась куча сущностей с похожими названиями и похожими данными, я создал миллиард экшенов. В итоге сам запутался, что, как и с чем взаимодействует.
Redux хороша для больших проектов, ориентированных на офлайн, где одновременно происходит куча асинхронных неблокируемых событий. Там этот бойлерплейт стоит терпеть, потому что он спасёт вам жизнь. Но в обычном тонком клиенте лучше использовать стандартную MV и не париться.
Вывод: что прочитать об архитектуре
На «Хабре» есть отличные статьи об MVC — «Охота на мифический MVC. Обзор, возвращение к первоисточникам и про то, как анализировать и выводить шаблоны самому» и «Охота на мифический MVC. Построение пользовательского интерфейса». Обязательно прочитайте их, если интересуетесь архитектурой — автор тщательно и на хороших примерах разобрал, что это такое.
Архитектурный дизайн мобильных приложений
Признак плохого дизайна №1:
Наличие объекта-«бога» с именем, содержащим «Manager», «Processor» или «API»
Ведущий iOS-разработчик Redmadrobot Егор BepTep Тафланиди — о том, как добиться стройного архитектурного дизайна мобильного приложения, используя классические шаблоны проектирования и логическое разделение исходного кода на модули.
Все архитекторы, которых я встречал, в разной степени обладали одной и той же профессиональной деформацией характера: в своей речи они старались избегать конкретики. Подобный подход легко понять, ведь суть гибкости любой системы заключается в абстрагировании от конкретных решений. Чем дольше решение может оставаться отсроченным — тем гибче система. Если UI достаточно абстрагирован от модулей уровня сохранения данных — то считывание файла с жёсткого диска с лёгкостью можно будет подменить скачиванием той же информации с серверного API.
И все же, попробуем разложить по полочкам архитектурный дизайн типичного iOS-приложения.
Я опишу глобальный подход к делению логики приложения на уровни и слои, а также приведу конкретных участников тех или иных процессов, каждый — со своей собственной ответственностью и взаимодействиями с окружением.
Установка
TL;DR: ПРОГРАММИСТЫ — ТОЖЕ НАУЧНЫЕ СОТРУДНИКИ
Студенты высших учебных заведений изучают естественные науки, учатся менеджменту, получают знания о прикладной психологии и пр. Каждая наука несёт в себе обязательный багаж накопленных сведений — академические знания, без которых продвижение самой науки вперёд было бы затруднительно. Без этого фундамента от науки не было бы и практической пользы: технологу на производстве гораздо выгоднее использовать проверенные временем подходы, гарантированно дающие результат, чем придумывать что-то своё. Естественно, сам этот фундамент постоянно эволюционирует и достраивается новыми сведениями, практиками.
За спиной программирования стоит точно такая же строгая наука, как и, например, за процессом производства аспирина. Информатика, как типичный представитель формальных наук, обладает тем же набором и структурой формальных методов, использует наработанные, проверенные подходы к изучению нового, и, естественно, к ней прилагается собственный багаж академических знаний. Научный подход гарантированно ведёт к получению качественного результата, а знание и применение базовых принципов проектирования ПО гарантированно облегчит поддержку продукта.
Книга «Язык паттернов» Кристофера Александра, давшая толчок в сторону использования шаблонов в программировании, была выпущена в 1977 году, а паттерн Model-View-Controller описан в 1979-м.
Вооружившись наработками в области разработки ПО и надев белый халат теоретика, попробуем выстроить типичный проект приложения для мобильной платформы.
N.B. Скорее всего, в реальной жизни (с учётом бизнес-требований к системе и принимая во внимание сроки разработки) ваш проект будет выглядеть несколько иначе. Тем не менее, можно будет с уверенностью сказать, что за спиной подобного продукта стоит не просто ваша выдумка, а обоснованный набор решений.
СФЕРИЧЕСКАЯ СИСТЕМА В ВАКУУМЕ
Модель песочных часов
СИТХОВ ВСЕГДА ДВОЕ: ОДИН СТРОИТ СЕРВИСЫ, ДРУГОЙ ВОЗДВИГАЕТ НА НИХ UI
Первым же шагом отделим модель от контроллера и представления, таким образом выделив два уровня приложения: «нижний» обращён в сторону серверных мощностей, «верхний» смотрит на пользователя.
На проектах мобильных приложений слои контроллера и представления, как правило, довольно органично способны разрабатываться одним человеком, в то время как остальная бизнес-логика, касающаяся персистентности и обработки данных, безболезненно может быть доверена второму разработчику. Тем самым мы сразу распараллеливаем процесс изготовления ПО на два независимых потока.
Будем придерживаться классического принципа, который гласит, что слои приложения общаются между собой посредством модельных объектов — классов с «прозрачной» реализацией, состоящей исключительно из акцессоров и мутаторов (get- и set-методы; фактически, класс будет включать в себя только свойства). Подобные классы не несут с собой никакой логики, и предназначены лишь для хранения и перемещения данных.
Центральной сущностью приложения — «горлышком» песочных часов — будет выступать объект, иначе называемый «инвертор зависимостей». Он же станет единственным singleton-классом, и на нём будет лежать единственная ответственность — предоставлять «верхним» слоям приложения сервисы.
Сервисы
ИСТОРИЯ ШТЕПСЕЛЬНОГО СОЕДИНЕНИЯ
Как делаются нормальные серверные приложения? Очень просто. Всегда есть какая-то база данных (или их может быть несколько), в которой есть таблицы. Таблицы состоят из записей. Каждая запись содержит набор полей, в которых хранится информация.
Записи представляются в виде модельных объектов с полями, тем самым в приложении возникает логическое разграничение по типам данных: одна таблица — один тип данных. Таблица «Пользователи» — тип данных «Пользователь» (поля: ID, ФИО, адрес). Таблица «Сообщения» — тип данных «Сообщение» (поля: ID, Тема сообщения, Тело сообщения, Кому, От кого, Дата). И так далее.
Таким образом, вся логика крутится вокруг таблиц и типов данных:
1. Модельный объект, представляющий тип данных.
2. Сериализация модельного объекта для сохранения его в таблицу.
3. Десериализация модельного объекта из базы данных.
4. Преобразования модельного объекта: подсчёт какой-то статистики,
сортировка данных, поиск данных и т.д.
5. Сериализация модельного объекта для передачи его по сети.
6. Десериализация модельных объектов, приходящих по сети.
7. Выставленный в сеть web-сервис, соответствующий данному типу
данных.
Всё это выстраивается в своего рода стек, а само серверное приложение состоит из нескольких таких независимых стеков, каждый из которых соответствует какому-то типу данных.
Грубо говоря, если по спецификации API у вас имеется web-сервис api.domain.com/1-0/messages с классическим CRUD-интерфейсом — это означает, что за ним стоит вышеупомянутый «стек» для типа данных «сообщение».
Create = POST;
Read = GET;
Update = PUT;
Delete = DELETE.
N.B. Существуют различные интерпретации операторов POST и PUT. В одних реализациях POST отвечает за создание сущностей, в других — за обновление. Канонического трактования, увы, не существует, но в этом нет ничего страшного.
Как правило, сервер поддерживает стандартизованный набор запросов, дифференцируемый посредством URL-суффиксов к соответствующему web- сервису:
ИТОГО:
Всё, что нам следует сделать — это изготовить вилку к розетке.
А именно:
1. Оформить транспортный уровень приложения в виде сущности, которая будет инкапсулировать под собой HTTP-соединение вместе со всеми его настройками (включая безопасность, таймауты и пр.)
• Интерфейс сущности должен представлять собой точно такой же CRUD, как и выставленный в Интернет web-сервис. Если есть доступ к адресу
• Сущность обязана быть модульной. Если в один прекрасный момент ваш замечательный web-сервис
файловой системы.
2. Оформить парсеры и сериализаторы, которые будут генерировать модельные объекты и, наоборот, преобразовывать их в вид, удобоваримый для вашего транспортного уровня.
•Парсеры и сериализаторы несут в себе знания о том, как преобразовывать модельные объекты в словари а-ля JSON или XML. Не пытайтесь прикрутить эти знания к самим модельным объектам — они никак не относятся к другим слоям приложения.
• Существует русскоязычный термин «топографический преобразователь», который мог бы заменить англоязычные «парсер» и «сериализатор», но только вот сам термин как-то не прижился…
3. Оформить сущность, которая будет отвечать за кеширование.
• Сущность должна просто уметь потокобезопасно предоставлять доступ к полученным данным, переписывать и обновлять эти данные более свежими.
4. Оформить сам «сервис» — сущность, которая будет ответственна за координацию транспортного уровня, парсеров, сериализаторов и кеша.
• Интерфейс сервиса должен полностью соответствовать бизнес-требованиям слоя UI. Если для UI необходимо получить какую-то сущность — должен быть метод для получения этой сущности. Если UI требует сохранить сущность с определёнными параметрами — у сервиса должен быть метод с этими параметрами. В случае, если UI требует массив объектов данного типа, отсортированный по какому-то критерию, — сервис должен предоставлять соответствующий метод, который вернёт отсортированный массив.
• И да, не забываем классический принцип: слои приложения общаются между собой посредством модельных объектов. Использование словарей/map для передачи данных недопустимо — только строгая типизация.
Таким образом, мы фактически делаем свой собственный стек, соответствующий серверному, но действующий в обратную сторону. Вот так будет выглядеть последовательность из шагов, которые необходимо преодолеть данным, чтобы добраться от центрального хранилища до конечного пользователя:
Сколько типов данных — столько и сервисов. Эту цифру довольно просто вычислить из спецификации API.
N.B. «Чистые» сервисы в природе встречаются не так часто. Бывает, тот или иной сервис обслуживает сразу несколько типов данных. Это обусловлено тем, что модельные сущности могут быть вложенными друг в друга: одна сущность может нести в себе массив объектов другого типа, или иметь подобный объект в качестве свойства.
В этом нет ничего страшного, просто ваши парсеры и сериализаторы должны адекватно воспринимать подобного рода вложенность, и генерировать объекты соответствующих типов.
Сервисы обязаны быть максимально автономными, что не мешает вам выстраивать между ними логические зависимости. К примеру, это вполне логично, что в приложении есть сервис, отвечающий за авторизацию и поддержание сессии с сервером, а остальные сервисы зависят от него — используют предоставляемый им token для формирования запросов.
N.B. Каждый из сервисов так или иначе будет обладать CRUD-подобным интерфейсом, построенным вокруг типа данных, обрабатываемых этим сервисом. Это может служить отличным поводом для выделения абстрактного сервиса с абстрактным CRUD-интерфейсом для последующего наследования от него других сервисов, что обеспечит высокую степень переиспользования кода.
Выстраивание уровня model подобным образом суть реализация паттерна слоистой архитектуры с наложением идей сервис-ориентированной архитектуры.
Мы не изобрели ничего нового.
Уровень UI
ВРЕМЯ ЗАМЕЧАТЕЛЬНЫХ ИСТОРИЙ
Итак, вы пришли на проект, скачали с репозитория исходники разрабатываемого приложения. Какой самый простой способ выяснить, что это приложение делает? Правильно! Запустить его! Перед вами знакомые таблицы с ячейками, стилизованные кнопки, барабаны с датами, навигация, названия экранов… Теперь задача: как соотнести это всё с исходным кодом, который разбросан по нескольким сотням файлов, что маячат перед вами в IDE?
Мы уже обособили один из уровней приложения — фактически model теперь может выступать совершенно самостоятельным модулем, который можно переподключить к какому-нибудь другому проекту, использующему тот же back-end. Или даже переиспользовать для другого back-end’a, если вы соблюли все принципы проектирования и сохранили определённый уровень абстракции.
Но вот незадача: у нас ещё целых два (!) слоя, а единственная намётка на то, что и где в исходных кодах находится — это само работающее приложение, запущенное на устройстве или в эмуляторе. Тут-то на помощь и приходят пользовательские истории.
Если ваше приложение хоть сколько-нибудь соответствует здравому смыслу, оно будет следовать определённым пользовательским историям, которые декларируют сценарии, необходимые для реализации того или иного действия. Самый классический пример — это истории регистрации и авторизации. Во время регистрации пользователь вводит какие-то личные данные, эти данные проверяются на адекватность, задаются логины и пароли, всё это может быть сдобрено мерами безопасности в виде SMS и так далее. Соответственно, в приложении существует целая история, касающаяся регистрации. Следуя логике, дабы облегчить собственную жизнь, наиболее очевидным решением будет следование этим пользовательским историям при построении структуры уровня UI.
Более того, SDK приложений под iOS предоставляют весь необходимый для этого инструментарий: достаточно просто завести под каждую историю свой Storyboard — и разделить структуру проекта так, чтобы все классы строились вокруг этих историй. Далее эти Storyboard’ы можно даже динамически соединять друг с другом посредством нехитрых библиотек, но это уже детали реализации.
В конечном итоге у вас получится структура, разделенная на модули по историям, в каждом модуле будут свои представления и контроллеры с классами-утилитами, и в этой структуре можно будет легко ориентироваться, исходя лишь из того, что у вас творится на экране устройства/эмулятора.
N.B. И да, конечно, не забываем классические принципы проектирования. Применяем SOLID, DRY, KISS и YAGNI по полной программе, главное — не лениться, и разделять сущности в соответствии с ответственностью, которую они несут. Остальное подскажет опыт.
Можно набросать приблизительную диаграмму классов:
Естественно, совершенству нет предела, и данный подход не претендует на звание эталонного.
Заключение
ИТОГИ И ОБСУЖДЕНИЕ
Мы кратко рассмотрели, как можно возвести стройный архитектурный дизайн мобильного приложения. Для этого мы применили сплав из классических шаблонов проектирования (MVC, SOA и Multilayered Architecture), прибегнув также к логическому разделению исходного кода на модули, основываясь на пользовательских историях.
А теперь небольшой вопрос к community:
Использовал ли кто-нибудь на боевых проектах архитектуру View-Interactor-Presenter-Entity-Router, и каким образом было достигнуто переиспользование сущности/шаблона Router? Как можно избежать разрастания Router’a?