«Цель архитектуры программного обеспечения - понятность, заменяемость. - Иоахим Курц »

Если вы читаете много блогов, смотрите много видеоуроков по шаблонам архитектуры, иногда вы можете запутаться только в терминологии. Именование элементов может отличаться от образца к образцу, но основные концепции остаются теми же. Я постараюсь добавить несколько синонимов, чтобы было легче понять.

Универсального решения для каждой проблемы не существует. Это объясняет, почему у нас много архитектурных паттернов. Каждый шаблон пытается решить какую-то конкретную проблему. Применение неправильного «решения» для какой-то проблемы просто усложняет ситуацию.

вступление

Когда я стал мобильным разработчиком, основываясь на предыдущем опыте инженера-программиста, я ожидал, что задачи будут техническими, математическими и алгоритмическими. Очень быстро я понял, что ошибался. Исключение может быть, если вы сразу перейдете к разработке SDK (framework, пакет…).

Обычно в разработке мобильных приложений дела идут быстрее. Адаптацию нужно делать намного быстрее. Деловая сторона, Владелец продукта. Менеджеры по большей части не имеют четкого видения конечной цели, только направление. А иногда даже старт разработки в формате R&D, PoC или A / B test.
Трудно делать хорошие прогнозы заранее, если вехи все время меняются, а в таблице нет четко определенных функций.

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

Разработчику для решения какой-либо проблемы необходимо разделить ключевые компоненты проблемы, приблизиться и сосредоточиться на цели, чтобы найти какое-то решение. Моя цель в этой статье - рассказать вам, что я считаю лучшим способом организации проекта и мой подход к шаблонам архитектуры на примерах проекта iOS. Эти принципы также могут быть применены к любому клиентскому проекту, основанному на пользовательском интерфейсе.

Семейство модель-представление-контроллер

Когда мы говорим о том, как организовать проект, большинство людей думают об архитектурных паттернах. Самый простой - MVC. Я не буду вдаваться в подробности каждого паттерна типа MVC. Если на экране слишком много ответственности, вы можете добавить дочерний контроллер представления. Если нужно больше разделения, может помочь MVC с некоторым сервисом. MVP может сделать эту работу, если представление имеет сложность. И мы можем выровнять это до MVVM, VIPER, CleanSwift и т. Д. На высоком уровне все эти шаблоны пытаются снять некоторую ответственность с кода, добавляя некоторый слой. Позже вернемся к этому.

Декомпозиция проекта

Когда мы говорим о декомпозиции проекта, есть много способов сделать это. Я представлю 4 разных способа, но их гораздо больше.

  • Технический. Это знакомо большинству. Это тот случай, когда вы группируетесь вокруг одного архитектурного паттерна. Например: в MVC есть папка View, которая содержит LoginView, RegisterView и все представления. Получите идею.
  • Визуальный - это то, что я предпочитаю в большинстве случаев. Если количество экранов значительно больше, чем количество функций, то это способ декомпозиции. Это позволяет вам вводить больше архитектурных шаблонов в один и тот же проект с меньшим риском путаницы. Также это лучший выбор для большинства приложений на рынке.
  • Домен - это как-то больше связано с функцией. Если на каком-то экране есть много функций, и его можно разделить в зависимости от этого. Например: некоторый график и некоторый список статей, этим можно управлять с помощью другого ViewController (дочернего VC), построенного разными моделями. Это полезно, когда приложение имеет один сложный экран с десятком функций или просматривает меньше SDK. Идея этой декомпозиции пришла из мира параллельного программирования.
  • Temporal - это последовательность чего-либо. Например: если вы создаете некую онбординговую структуру, в которой онбординг является линейным. Или какой-нибудь учебный пейджер. Тогда это твой выбор.

Когда разработчики начинают свою карьеру, они слишком много внимания уделяют этому техническому аспекту и, что еще хуже, применяют его ко всему проекту. Возможно, это предвзято из-за онлайн-руководств. Слишком много внимания уделяется технической декомпозиции приложения без учета реальных потребностей бизнеса.

Ориентируясь на разложение в техническом измерении, мы просто добавляем еще один слой. Может быть, некоторые части не нуждаются в сети. Возможно, некоторые функции не нуждаются в просмотре. Итак, у нас есть много мелких деталей, которые бесполезны и просто ухудшают читаемость. При таком подходе код может выглядеть чище, но это не значит, что он понятен.

Решайте разные задачи с разными размерностями декомпозиции, но локально. Проблема локальная, решение не нужно применять глобально ко всему проекту. Например: если есть экран, для которого требуется более сложный шаблон архитектуры, такой как CleanSwift или VIPER, это не означает, что его нужно применять ко всему проекту.

Это может произойти, когда команда старших разработчиков запускает проект и применяет какой-то шаблон, например VIPER, с шаблонами. В середине работы младший разработчик знакомится с проектом и не имеет возможности делать пошаговое знакомство с технологиями. Разработчик вынужден правильно изучить VIPER, бизнес-логику, структуру команды, возможно, какой-нибудь гибкий процесс. Это может быть ошеломляющим, устрашающим и находящимся под давлением.

Организация проекта

Если у вас много изменений и мозговой штурм с владельцем продукта, я бы разделил две концепции, вокруг которых вы должны создавать приложение. Сцена (например, экран входа в систему) и Функция (например: геолокация, когда пользователь входит в какой-либо конкретный магазин в радиусе действия, получает push-уведомление о лучших предложениях магазина). Также мне нравится создавать графики экранов и отмечать, какая часть какую функцию использует.

Второе, что я нашел полезным, - это концепция Многоуровневая архитектура. Версия с 3 + 1 слоем. В случае необходимости сложного нанесения, слои могут быть увеличены. Этого количества слоев хватит на большинство случаев. Это снижает сложность программного обеспечения и повышает понятность и читаемость кода.
Эта многоуровневая архитектура также может быть отражена в структуре папок.

  • Уровень приложения
  • Уровень презентации (уровень пользовательского интерфейса)
  • Уровень бизнес-логики (уровень домена)
  • Уровень данных (базовый уровень, уровень доступа к данным, уровень сохраняемости)

Уровень приложения

Это начальный уровень приложения. Эта часть является глобальным слоем, который должен содержать такие элементы, как AppDelegate, SceneDelegate, Configurations, Constants, стартовые компоненты DI. Скажем, все вещи, которые должны быть доступны во всем приложении и должны быть доступны с самого начала.

Уровень презентации

У большинства приложений много экранов. Больше экранов по сравнению с количеством функций. Тогда этот слой - центральная фигура. Как видно на картинке, он содержит папку CommonUI. Эта папка предназначена для хранения расширений, некоторых универсальных базовых классов, помощников пользовательского интерфейса, универсальных кнопок и т. Д. Она содержит папку Экран / Сцена, которая является центральным компонентом. Если у вас есть экран входа в систему, в нем будет папка LoginScreen с классами LoginView, LoginViewController, LoginInteractor, LoginViewModel (ViewData) и т. Д. Это зависит от шаблона архитектуры экрана.
Имейте в виду, что ViewModel в разных шаблонах может иметь немного разную ответственность и принадлежать разным слоям. Это может сбивать с толку. В моем примере это для шаблонов типа MCV, где ViewModel обслуживает только представление с простыми данными, такими как текст, число.

Уровень бизнес-логики

Этот уровень посвящен бизнес-правилам клиента. Это центр приложения, то, что приносит деньги или обеспечивает реальную ценность. Предоставляет смысл приложения. Я бы сказал, что это часть Особенности. На нескольких экранах могут использоваться одни и те же аспекты функций. С технической точки зрения он содержит службы, рабочий процесс, менеджера, работника, шлюз, источник данных, поставщика… Все они имеют для вывода какую-то структуру данных, которая обслуживает уровень представления или уровень ядра. Эти компоненты не имеют состояния. Уровень представления может попросить вас, например: UserWorkflow, предоставить вам пользователя по идентификатору. Что даст вам UserModel.

Некоторые факты:

  • Некоторые функции могут быть построены из нескольких служб и поставщиков.
  • Другие экраны могут представлять одну и ту же функцию по-разному или представлять какой-либо аспект функции.
  • Один экран может отображать больше функций.
  • Функция может быть представлена ​​без экрана (например, как Push-уведомление)

Уровень данных

Это та часть, где мы получаем данные из удаленных или локальных источников данных. Или после некоторой операции с данными мы их сохраняем. Это может быть как CoreData, какой-то сетевой API или UserDefaults. Это место, где мы можем найти некоторые модели, сущности, DTO (объект передачи данных), ModelController.

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

Итак, вернемся на пару шагов. Когда разработчики обычно начинают создавать приложение, основное внимание уделяется скорости разработки, повторному использованию, согласованности, переносимости, простоте обслуживания, затем это превращается в оптимизацию времени компиляции, простоту подключения, производительность во время выполнения и т. Д. В любом случае эти цели преследуют разные цели. таблицы и фокус могут быть на 1 или 2 одновременно.

Итак, цитата с самого начала говорит, что технически акцент должен быть сделан на понятности, заменяемости тем, с чем я полностью согласен. В каждом приложении есть пара сложных экранов и 10–15 и более действительно простых. С этой точки зрения я одобряю использование разных архитектурных шаблонов для каждого экрана. Обычно центральным классом является ViewController, и на каждом экране есть хотя бы один. Следующему разработчику на этом экране может помочь некоторое описание используемого шаблона или любая техническая информация. Я оставляю эту информацию в заголовке как комментарий.

Пример:

  • ScreenA (логин, простой) - используйте MVC + немного LoginService
  • ScreenB (некоторая сложная панель инструментов с большим количеством вызовов API, необязательными и разными диаграммами, и ожидается, что у нее будут новые функции, A / B-тестирование) - используйте CleanSwift
  • ScreenC (какой-то полусложный с парой частей просмотра) - MVC с дочерними VC

Плюсы такого подхода:

  • Возможность замены: экраны можно легко заменить, поскольку все компоненты экрана находятся в одном месте
  • Если на проект придет младший разработчик, то проще будет его освоить. Большинство частей легко понять. Можно приступить к работе над более легкими деталями.
  • Для большинства экранов достаточно MVC и некоторых Сервисов.
  • Нет необходимости следовать одному сложному шаблону архитектуры для простых экранов с почти пустыми классами.
  • Понятность: всего пара экранов сложнее
  • Отладка проще, поскольку важна понятность
  • Заменяемость: также означает, что его легче проверить

Минусы такого подхода:

  • Не лучшая организация, если есть еще функция, то экран
  • Плохо для разработки SDK

Резюме

Что-то вроде MassiveViewController не должно существовать. Если вы видите, что это происходит, спросите себя. Что делает этот экран? Для чего используется эта функция? Если ответ не является простым предложением, то, вероятно, нужна некоторая декомпозиция, рефакторинг. Универсального лучшего образца не существует. Каждый узор - это решение какой-то проблемы. Также спросите себя, решит ли это что-то глобально или локально. Если мы хотим хорошей понятности, нам нужно задать много вопросов.