Как увеличить эффективность архитектуры мобильного приложения

Всем привет, меня зовут Архип, в iOS-разработке уже больше 5 лет. На данный момент работаю в аутсорс-компании Artkai. Последние три года — в fintech-проектах. За это время у меня сформировался подход к разработке приложения, который помогает добиться максимальной эффективности и устойчивости архитектуры приложения. Эта статья будет полезна джуниорам, а также уже опытным разработчикам, которые сталкивались с проблемами поддержки приложения. Мы рассмотрим этапы проектирования, которые можно применять и для уже существующего проекта.

Трудности архитектуры проекта

Мы разрабатывали fintech-приложение для банка, который входит в топ-20 digital bank Ukraine (Banker 2021). Моей обязанностью была разработка мобильного приложения для iOS-платформы.

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

В книге Роберта Мартина «Чистая архитектура» приводится график, на котором отчетливо видно увеличения расходов компании на содержание сотрудников с выпуском новой версии. Этот пример основан на реальной истории. По своему опыту подобное также наблюдал.

Разрабатывая приложение, перед программистом стоит вопрос — какая архитектура или паттерн подойдет лучше всего для разработки нового приложения? На него не существует однозначного ответа, так как в мире нет идеальной архитектуры, которая подойдет на 100 процентов любому проекту. Но сразу выбирать архитектуру — это плохое решение. При выборе или создании архитектуры вам необходимо учитывать множество аспектов. Для преобразования существующего кода в новую эффективную архитектуру нужно сделать еще больше, чем сделать с нуля.

Весь процесс разбит на три этапа: анализ (Analysis), проектирование (Design) и непосредственно само программирование (Programming).

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

Шаг 1: Анализ проекта

Анализ проекта в целом дает понимание проблемы, которую вы решаете. Будет отлично, если заказчик предоставит это команде разработки, иначе необходимо будет провести анализ самостоятельно. Разработка модели позволяет спланировать и схематически отобразить работу вашей системы, выделить главных актеров, которые взаимодействуют с системой, и определить объекты, с которыми взаимодействуют актеры. А этап программирование — это уже непосредственно процесс реализации проекта или системы.

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

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

Анализ для мобильного приложения выглядел следующим образом.

  1. Что должно делать? — Регистрировать и предоставлять доступ к банковскому счету и инструментам управления счета.
  2. Перечень функционала: проверка баланса и список транзакций, перевод на банковскую карту, пополнение мобильного телефона и многое другое.
  3. Нефункциональные требования: приложение должно работать в офлайн-режиме, уведомлять пользователей об изменении баланса и многое другое.

Как можно заметить, пункт 1 и 2 схожи между собой, но мы их разделяем, что позволяет выделить основной глобальный функционал от вторичного. Также этот анализ позволяет сформировать первые требования к MVP, потому что, если мы начнем постоянно добавлять функционал, есть риск выпустить приложение со срывом всех сроков или, в худшем случае, утонуть в вечной разработке.

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

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

Основная задача архитектуры приложения — это максимально повысить продуктивность работы команды и качество программного кода. Кроме того, хорошие архитектурные практики могут помочь предотвратить жесткую привязанность к программному/аппаратному обеспечению. Для более детального анализа можно составить FURPS+ требования к системе. По сути, это классификация требований.

Аббревиатура образована от таких слов:

  • Functionality — функциональные требования (свойства, возможности).
  • Usability — требования к удобству использования (UX).
  • Reliability — требования к надежности.
  • Performance — требования к производительности.
  • Supportability — требования к поддержке.

Единственное требования — фразы должны быть короткими и не должны описывать реализацию.

Шаг 2. Проектирование

Далее важным этапом будет написать юзкейсы (Use Cases). Они описывают взаимодействие актера с системой, актер в нашем случае может быть отдельной системой или пользователем. Подготовленный документ с юзкейсами понадобится на всех этапах разработки от согласования с заказчиком до тестирования.

Юзкейсы можно описать UML-диаграммами или в табличном виде. Хотя UML-диаграммы отлично отображают главного актера и второстепенных. Для простоты понимания я всегда начинаю и делаю упор на табличное представление. На практике UML-диаграммы и таблицы юзкейсов дополняют друг друга, а не заменяют. Это важно понимать и помнить.

Цель:Пополнить мобильный счет
Действующий (главный) актер:Пользователь
Успешный сценарий: [Описание шагов для достижения результата]

...
4. Пользователь вводит номер моб. телефона.
5. Система проверяет номер.
6. Пользователь вводит сумму пополнения.
7. Система проверяет баланс.
...
8. Система проводит транзакцию.
9. Система уведомляет пользователя о списании средств.
Результат:Успешно пополнен счет моб. тел.

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

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

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

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

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

Тут все просто: мы должны выделить все существительные, выписать, обнаружить и избавится от дубликатов. Все, что останется, и будут наши объекты. У вас могут возникнуть сомнения по поводу некоторых оставшихся объектов, но не спешите от их избавляться. Так как мы еще активно не приступили ко второму этапу, мы не можем сказать наверняка. На практике они могут встречаться как атрибуты других классов или инкапсулировать их поведение в классе. Единственное — можно удалить систему из списка.

Далее мы создаем CRC-карты (Class-responsibility-collaboration card). Они помогут обнаружить классы и их взаимодействие. Обычно они включают в себя имя класса, ответственность или обязанности и другие классы, с которыми связан. Стандартное отображение CRC-карты:

TransactionListView [название класса]
Ответственность:Объекты сотрудничества (связь с другими классами):
Know current transactionsTransaction storage
Remove(hide) selected transactionTransaction
Select transaction to displayTransaction

CRC-карта может включать в себя дополнительные поля, такие как родительский класс, подкласс и тому подобнее. Здесь также нет строгих ограничений.

Далее мы создаем концептуальную модель. Она демонстрирует связи и отношения между классами.

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

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

После анализа, требований, юзкейсов, СRC-карт, концептуальной модели мы можем приступить к проектированию, а именно созданию UML-диаграмм классов. Мы не будем рассматривать процесс создания UML-диаграмм. В книге Мартина Фаулера «UML Distilled» подробно раскрыта эта тема, она будет полезна для тех, кто не знаком с языком UML. Эта книга понадобится, если вы хотите шагнуть дальше, чем уровень Junior developer.

Шаг 3. Реализация программного кода

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

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

  1. Приложение работает нестабильно и зачастую выдает ошибки.
  2. Вашу кодовую базу сложно понять.
  3. Ваш код невозможно или сложно использовать повторно. Или много дублирующего кода.
  4. Постоянные конфликты в коде с другими разработчиками.
  5. Внесенные правки в код порождают крупные рефакторинги кода.
  6. Сложности при написании юнит-тестов.
  7. Ваш код долго компилируется.
  8. Добавление нового функционала замедляет работу всей команды.
  9. Мелкие изменения требуют регресс-тестов.
  10. Программный код неявно влияет на другие процессы.

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

Также, чтобы избежать приведенных причин, следует изучить и понимать SOLID-принципы, архитектурные паттерны и паттерны проектирования. Обычно все источники лишь поверхностно затрагивают различные паттерны, и вы вынуждены сами разобраться в деталях. А в интернете можно встретить статьи, где некоторые паттерны классифицируют как плохие. По сути, нет плохих паттернов, многие из них очень схожи между собой и практически не имеют различия. Накопленный багаж знаний поможет добиться максимальной эффективности в разработке. На своей практике могу утверждать, что использование вслепую уже готовых паттернов — это не всегда лучшее решение. Программист должен их понимать и применять заложенный в них подход для решения конкретных задач и модифицировать под потребности проекта.

Вывод

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

👍НравитсяПонравилось4
В избранноеВ избранном4
LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Сокращу до смысла: делайте хорошо, плохо не делайте, имитируйте бюрократический идиотизм чтобы бюрократы не лишились работы. А то начинаются ж потом вопли «За что его уволили, он же ничего не сделал?»

Сама система не должна отвечать за проверку номера, баланса и тому подобное, иначе это уже процедурное мышление.

А есть научные исследования, какие либо доказательства, что ооп-грабли чем то лучше процедурных?

Нефункциональные требования: приложение должно работать в офлайн-режиме, уведомлять пользователей об изменении баланса и многое другое.

Не функциональные требования: это производительность, требования по-памяти, минимальное поддерживаемое оборудование, версия ОС.

Должно работать в офлайн-режиме, уведомлять пользователей об изменении баланса — это функциональное требование. Не вводите народ в заблуждение.

топ-20 digital bank Ukraine

Знаю я эти украинские банковские архитектуры. Кривая апишка из говна и палок на миддлвару потому что родное-ублюдочное Б2 нихрена под карточки не поддерживает и приходится извращаться. Чувак это не к тебе негатив ;)

Ни одного слова про VIPER, PIDOR, Clean Architecture или какой-либо другой паттерн который бы подходил, по мнению автора, к архитектуре приложений под iOS.

Ваш код долго компилируется.

Это вообще как то связано с «плохим» кодом? Может просто с размером проекта?
Или автор как говориться человек простой — пишу код в одной файле, выделять отдельные файлы под интерфейсы / extensions / вспомогательные функции — не, не слышал.

Дизлайк, отписка

В этой статье рассматривается этап анализа и подготовки, а не сравнение уже существующих архитектур. Вы редко встретите или будете использовать какую-то архитектуру без изменений и в итоге будет некий гибрид или видоизменённый под нужды текущего проект, а тут указаны нюансы, которые необходимо учесть при разработке для достижения хорошого программного кода.
«Плохой код» — это все что негативно влияет на процесс разработки, и тут также необходимо учесть нюансы используемой сдк, платформы и тд. В XCode Вы можете подключить swiftlint для ведения одного стиля кода.

Без конкретных примеров это все бла-бла масло масляное.

Архип, получилось очень душно и непонятно почему это про мобильную разработку

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

Подписаться на комментарии