«Каждый программист должен создать свой архитектурный паттерн»
Народная мудрость.
Постановка проблемы
На сегодняшний день наиболее известны такие архитектурные паттерны как MVC, MVVM, MVP, Viper, Clean Code.
Все они в той или иной мере работают с тремя основными сущностями - Модель, Вью, Контроллер, добавляя время от времени некоторые дополнительные, например, Presenter.
Вторая общая особенность данных архитектурных паттернов состоит в том, что названные выше сущности выделяются и классифицируются исходя из их технических характеристик. Например, Вью - это то, что отображает данные на экране, Модель - содержит в себе данные и их обработку, а Контроллер осуществляет взаимодействие между ними.
Но эти характеристики не отражают сущности приложения в целом. Это как если бы мы разделили воду на водород и кислород и пытались бы из их особенностей понять сущность воды.
Фрагментарность используемых сущностей и отсутствие целостного видения приложения приводит к общеизвестным проблемам, связанным с трудностями понимания кода и его управлением.
Отсюда, ни один из этих паттернов не гарантирует, что на определённом этапе разработки приложения не возникнет ситуация, когда код станет тяжеловесным и очень сложным для управления.
Именно в такие моменты приходится переосмысливать общую архитектуру проекта и отвечать на вопросы “Зачем нужен тот или иной код, какую задачу он решает?”, “Где расположен код, реализующий ту или иную функциональность и как он работает?”. И т.д.
Продолжая пример с изучением воды следует сказать, что единицей её анализа является молекула воды. Это мельчайшая частица воды, которая тем не менее содержит в себе все её свойства.
В программе такой мельчайшей и одновременно целостной единицей является задача, которую решает тот или иной блок кода.
Отсюда, возникла идея использовать в качестве отправного пункта для организации кода именно те задачи, которые этот код решает.
При этом, задача понимается как бизнес-процесс.
Что такое бизнес-процесс
Термин “бизнес-процесс” пришёл из концепции реинжиниринга бизнес процессов. Наиболее известная и основополагающая книга по этой теме - Michael Hammer, James Champy. "Reengineering the Corporation: A Manifesto for Business Revolution".
Само слово "бизнес" в русском языке понимается в большей мере как коммерческая деятельность, связанная с зарабатыванием денег. В то же время в английском языке оно имеет более широкий смысл, включая, например, и такие смыслы как "дело", "занятие", "действие" и др.
Необходимо отметить, что понятие бизнес-процесса не чуждо программированию. Например, здесь используются такие термины как бизнес-логика, предметная логика, поток пользователя и др. Более того, в программном обеспечении класса ERP и CRM вся логика программ строится именно на основе бизнес-процессов предприятия.
Существует несколько, достаточно разных, определений бизнес-процесса.
В данном случае мы придерживаемся мнения о том, что бизнес-процесс - это определённое действие (активность, деятельность), реализующее задачу, связанную с работой приложения.
Это действие имеет линейный, последовательный характер, т.е., у него есть начало и окончание.
Бизнес-процесс всегда решает определённую задачу, у него есть цель и результат.
Бизнес-процессы могут взаимодействовать между собой.
Результат одного бизнес-процесса может быть использован другими бизнес-процессами.
Бизнес-процесс может включать в себя подчиненные суб-процессы.
Выделение бизнес-процесса, различение родительских и дочерних, определяется разработчиками проекта.
Пример бизнес-процессов
Например, предположим, что у нас есть приложение, на одном из экранов которого отображается список элементов.
Таким образом, мы имеем бизнес-процесс “Показать элементы”.
В свою очередь, этот бизнес-процесс включает в себя загрузку данных в оперативную память, создание макета для отображения данных, передачу данных в таблицу.
Далее, передача данных в таблицу может включать в себя настройку таблицы, конфигурацию динамического прототипа ячейки, конвертацию исходных значений в текстовый формат и др.
Все эти действия есть бизнес-процессы разных уровней декомпозиции.
Уровни бизнес-процессов
Полезно различать бизнес-процессы по трём уровням:
1-й уровень: Пользовательский
Это задачи, которые задаёт пользователь.
Например, посчитать количество калорий за день.
2-й уровень. Пользовательско-технический
Это первичное описание программы для решения пользовательских задач.
Например: показать на экране список приёмов пищи, а также количество калорий для каждого из них. И отобразить общее количество калорий за день.
3-й уровень. Технический
Это технические задачи, которые подробно описывают работу программы и которые пользователь не видит.
Например: Загрузить данные, сохранённые на диске, в оперативную память для отображения их в таблице.
Различение этих уровней помогает выделить те или иные бизнес-процессы по ходу разработки программы.
Исходя из вышесказанного, при структурировании кода первым шагом является определение базовых бизнес-процессов, а затем, путём декомпозиции, определение дочерних бизнес-процессов вплоть до отдельных функций.
Обозначение (называние) бизнес-процессов
Задачи, которые решают те или иные бизнес-процессы, указываются в названиях папок в навигаторе проекта.
Идеальный вариант, когда в названии бизнес-процесса используются 2 слова: действие и объект, на который оно направлено. Например, Display Foods.
Но в ряде случаев может использоваться и большее количество слов. Например, Display Editing Meal.
Названия файлов
В свою очередь, в названиях файлов указываются название объекта, который реализует (выполняет) задачу, затем тип объекта (класс, метод, свойство и др.) и его локация, т.е., где он находится: то ли является глобальным объектом, то ли является расширением какого-то объекта.
Один файл - одна задача
Основное правило при структурировании кода - помещать каждое действие, каждую отдельную задачу нижнего уровня в отдельный файл.
Для этого активно используются расширения.
При этом, файлы с расширениями группируются не по принадлежности к определённым объектам, таким как классы, структуры и т.д., а по принадлежности к определённым бизнес-процессам.
Отсюда, расширение одного класса может оказаться в группе файлов, принадлежащей бизнес-процессу другого класса.
Многозадачные файлы
В силу особенностей языка Swift и Xcode соблюдение данного правила - один файл одна задача - не всегда возможно.
Так например, свойства класса могут находится только в том файле, в котором объявлен класс. Таким образом получается, что один файл содержит код для решения нескольких задач.
В этом случае, папка, которая включает в себя файл с объявленным классом, использует слово manage, которое означает, что в данном файле выполняются несколько действий, относящихся к разным задачам и/или бизнес-процессам.
В свою очередь, один и тот же бизнес-процесс может реализовываться в нескольких файлах. Например, переход и передача значения в другой контроллер.
В этом случае файлы данного родительского бизнес-процесса группируются вместе, в одном блоке с названием этого бизнес-процесса. А папки, включающие отдельные файлы, называются по названиям задач, которые включены в данный родительский бизнес-процесс.
Пример реализации подхода Бизнес-процесс нотация
Рассмотрим образец реализации Бизнес-процесс нотации на примере приложения, которое отслеживает количество калорий.
Бизнес-процессы верхнего уровня
Самый верхний родительский бизнес-процесс - это задача всего приложения, т.е., отслеживание калорий.
Этот бизнес-процесс включает в себя бизнес-процессы второго уровня, которые показаны на Рис. 1
Здесь соблюдается алгоритм, показанный выше. А именно, что обозначение бизнес-процесса производится в названии папки.
И это обозначение включает в себя как правило действие и объект, которым это действие управляет.
В данном случае практически все бизнес-процессы обозначены с помощью слово manage. Это обобщающее слово, показывающее, что такой бизнес-процесс включает в себя несколько дочерних бизнес-процессов.
Базовые бизнес-процессы приложения
Наибольшую смысловую нагрузку в обработке контента данного приложения несут на себе бизнес-процессы Manage Foods и Manage Meals.
Рассмотрим внутреннюю архитектуру первого из них.
Бизнес-процесс Manage Foods: Папки и дочерние бизнес-процессы
Как видим, бизнес-процесс Manage Foods включает в себя 6 дочерних бизнес-процессов.
Первое слово в названии дочерних процессов это глагол, который обозначает действие. Второе и последующие слова в названиях процессов обозначают объекты, по отношению к которым выполняются данные действия.
Model Food - моделировать продукт, создать модель объекта Food.
Test Foods - тестировать продукты, т.е., в данном случае создать демо-данные для проверки. Разработчик решил их использовать для тестирования, а также оставить их как подсказку пользователям на первых этапах освоения приложения.
Load Foods - загрузить данные о продуктах в приложение.
Storage Foods - сохранить данные о продуктах в оперативной памяти приложения.
Show Foods in the Table - показать данные о продуктах на экране.
Edit Foods - редактировать данные о продуктах.
Бизнес-процесс Manage Foods: Файлы и объекты
Опустимся теперь на уровень ниже и посмотрим как оформляются файлы бизнес-процессов. Рассмотрим это на примере дочерних бизнес-процессов родительского бизнес-процесса Manage Foods.
1. Model Food
Первая папка Model Food обозначает код, который решает задачу создать модель объекта Food. И эта папка содержит один файл, который называется Food_Class_Global.swift.
Где:
Food — это название объекта.
Class — это тип объекта.
Global — это локация объекта. В данном случае Global означает, что этот объект находится в глобальной зоне видимости.
2. Test Foods
Следующая папка - Test Foods - включает в себя код, который решает задачу проверки пригодности формата данных о продуктах для использования их в программе. Эта папка также содержит один файл с именем demoFoods_Variable_Global.swift.
При этом demoFoods - это название объекта, Variable - это тип объекта и Global это локация объекта в глобальной зоне видимости. Т.е., по сути, это Stub data.
3. Load Foods
Следующая папка — Load Foods — обозначает бизнес‑процесс загрузки данных в оперативную память приложения. Она включает в себя 2 файла:
loadRealOrDemoFoodsData_Method_ExtFoods.swift
Этот файл содержит в себе метод loadRealOrDemoFoodsData и является расширением класса Foods.
Как уже было сказано ранее, желательно, чтобы один файл содержал в себе один метод или одно свойство.
Данный файл как раз реализует это правило.
Заодно рассмотрим и его внутреннюю структуру.
В верхней части файла обозначается задача, которую решает код в этом файле. Второй строкой комментария добавляются детали данной реализации. После чего идёт тело расширения и сам метод.
Таким образом, структура файла проста и понятна. Обозначена задача и код решает эту задачу.
Как известно, именно к такой, простой и понятной структуре файла следует стремиться.
viewDidLoad_Method_ExtFoods.swift
Второй файл в этой папке также является расширением класса Foods и содержит в себе, как видно из названия файла , стандартный метод viewDidLoad.
Здесь необходимо также отметить, что метод viewDidLoad - это один из тех случаев, когда уместить одну задачу в один файл не представляется возможным, поскольку при запуске приложения и срабатывании метода viewDidload, он может запускать внутри себя код для реализации нескольких разных задач.
4. Storage Foods: Бизнес-процессы разработки и бизнес-процессы выполнения программы
В этом месте можно обратить внимание ещё на одну особенность группировки кода по бизнес-процессам. Дело в том, что в дополнение к предыдущим критериям различения разных видов бизнес-процессов можно добавить и бизнес-процессы разработки приложения, в отличие от бизнес-процессов работы уже готового приложения.
Например, если мы говорим о загрузке данных с диска во временную память приложения, то при разработке приложения вначале необходимо объявить свойство для хранения данных, а затем выполнить процесс загрузки. В случае же анализа работы уже готового приложения, оно не знает изготовления, разработки, и по умолчанию подразумевает, что приложение уже создано. Отсюда, вначале реализуется процесс загрузки данных, а затем процесс их сохранения в соответствующем свойстве.
Именно этот момент и иллюстрирует Рис. 4, где папка Load Foods и соответствующий ей бизнес-процесс, предшествует папке Storage Foods. И файл с объявленным классом Foods появляется в навигационном дереве уже после используемых своих же расширений.
Такое деление в ряде случаев помогает выделить бизнес-процессы в общей архитектуре программы.
Это второй случай файла с многозадачностью. Т.е., файл с классом Foods используется в первую очередь для хранения данных в оперативной памяти. В нем размещаются все свойства класса.
5. Show Foods in the Table
Следующий бизнес-процесс - показ данных на экране, в данном случае в таблице: Show Foods in the Table. Он содержит один файл, названный TableDataSource_Methods_ExtFoods.
Как видим, в названии этого файла используется слово не Method, а Methods, что означает, что в этот файл включены несколько методов.
Но в отличие от предыдущего файла с классом Foods, внутри которого может находиться несколько блоков кода для решения разных задач, внутри этого файла находится несколько методов, которые решают одну и ту же задачу - передача данных из оперативной памяти в таблицу, т.е., в видимую для пользователя часть приложения. Это типовые методы numberOfSections(in:), tableView(:numberOfRowsInSection:) и tableView(:cellForRowAt:).
Также отметим, что здесь есть и методы динамического построения интерфейса по ходу выполнения программы (количество секций, строк, конфигурирование ячейки), так и методы собственно выполнения программы(передача данных в ячейку).
Компоновка всех этих трёх методов в отдельное расширение класса Foods позволяет разгрузить базовый файл класса и сгруппировать код по принципу решения определённой бизнес-задачи.
Еще один момент, связанный с группировкой кода состоит в том, что в этом случае в названии файла невозможно обозначить один объект, поэтому даётся обобщающее название, понятное разработчику - TableDataSource_Methods.
Режимы показа данных
В этом же бизнес-процессе - Show Foods in the Table - есть два своих дочерних процесса: Sort Foods и Search and Filter Foods.
Эти дочерние бизнес-процессы реализуют ту же задачу показа данных, но в разных режимах: либо вывести на экран данные в определённом порядке, либо выбрать для показа только часть данных.
Каждая из этих задач реализуется в своём отдельном файле, которые являются расширениями базового класса Foods.
В целом, следует отметить, что активное использование расширений базового класса, в том числе вынос в них стандартных методов, таких как viewDidLoad и TableViewData Source методов, позволяет освободить, разгрузить базовый файл класса и таким образом нивелировать проблему Massive View Controller.
6. Edit Foods
Рассмотрим теперь дочерние процессы бизнес-процесса Edit Foods. Они показаны на Рис. 5.
Бизнес-процесс Edit Foods включает в себя следующие дочерние бизнес-процессы:
Delete Food
Pass Food to Edit
Load Editing Food
Storage Editing Food
Prevent Input Error
Pass Edited Food Back to Foods
Get Edited Food in Foods
Persist Edited Foods
Как видно из этого перечня бизнес-процессов, здесь выстраивается определённая последовательность действий по реализации задачи Edit Foods. Например, элемент передаётся для редактирования, получается, сохраняется, изменяется, возвращается обратно, получается и изменения сохраняются на диск.
Каждая задача в этом блоке реализуется в своём отдельном файле, что можно увидеть на Рис. 6.
Важно отметить, что здесь мы видим не только последовательность задач, но и названия знакомых методов, которые реализуют эти задачи.
Например, prepareForSegue, viewDidLoad, unwindSegue и др. Это позволяет легче ориентироваться в коде.
И теперь, на примере этих бизнес-процессов, необходимо сделать очень важное замечание.
Что такое Модель?
Что, в упомянутых в начале этой статьи архитектурных паттернах, понимается под Моделью?
В одном случае под Моделью понимается структура данных, модель объекта. Например, структура или класс могут включать в себя ряд свойств и методов.
Во втором, более неявном случае, под моделью понимается всё, что не относится к интерфейсу, к визуальной составляющей приложения.
И вот сейчас, на основе представленных выше совокупности и последовательности бизнес-процессов можно и следует говорить о Модели приложения в целом.
И действительно, глядя на Рис. 5 и на Рис. 6, мы видим целостную картину приложения, как приложение работает, какие составные части оно включает и в какой последовательности выполняются эти бизнес-процессы.
Это и есть целостная модель приложения, его внутренняя архитектура.
Примечание
Создание программ часто сравнивают с постройкой дома. С этой точки зрения, традиционная группировка кода по техническим характеристикам (view, model, service и т.д.) похожа на ситуацию, когда дом рассматривается просто как набор строительных материалов, например, кирпичи, оконные рамы, трубы для водопровода и т.д. Отсюда, во многом теряется видение дома как целостного объекта с его интенцией. То же происходит и с программами, что потом проявляется в трудностях, связанных с целостным пониманием кода и с его управлением.
Типовые бизнес-процессы. Типовая архитектура
Рассмотрим ещё одну интересную особенность, выявленную в ходе структуризации кода на основе бизнес-процессов приложения.
На Рис. 7 показаны дочерние процессы двух базовых бизнес-процессов приложения: Manage Foods и Manage Meals.
Как видим они полностью совпадают и включают в себя по 6 дочерних процессов:
Model
Test
Load
Storage
Show
Edit
Такое совпадение наводит на мысль о том, что на определённом уровне анализа бизнес-процессов они могут быть одинаковыми для разных объектов приложения.
Другими словами, речь может идти о типовой архитектуре бизнес-процессов.
Заключение
Самый большой плюс от использования Бизнес-процесс нотации состоит в том, что с её помощью можно увидеть целостную модель всего приложения.
Т.е., увидеть набор задач, увидеть их последовательность как звенья, этапы определённого бизнес-процесса, а также видеть методы, свойства, которые реализуют те или иные задачи и их локацию.
Немаловажным также является возможность выполнять декомпозицию и композицию приложения в режиме реального времени, открывая в навигаторе проекта группу папок или скрывая их.
Все это значительно упрощает понимание структуры кода приложения. При такой компоновке гораздо легче найти код, который относится к определённой задаче, и с другой стороны, гораздо быстрее понять какую задачу решает соответствующий блок кода.
Все это существенным образом повышает возможность понимания кода и возможность качественного управления им.
P.S.
Данная статья представляет собой обобщение опыта автора по разработке мобильного приложения на основе подхода, в основу которого положен анализ программы как системы бизнес-процессов.
Очевидным преимуществом такого подхода является возможность построения целостной модели приложения.
Автор конечно хотел бы услышать мнение уважаемой аудитории по поводу жизнеспособности данного подхода.
0pauc0
Скажите, чем вам вместо невнятного и расплывчатого термина "бизнес-процесс" не нравится, ну например термин "операция" (речь же в статье идет о реальных производственных или цифровых делах)?
Было бы красиво - "операция по первичной обработке сырья", затем "операция транспортировки заготовки", а потом "операция изготовления детали", конечно-же "операция контроля качества детали", далее "операция транспортировки детали", "операция сборки агрегата из деталей" и т.д.
Применительно к цифровым делам - "операция получения исходных данных", далее "операция по передаче данных через Кафку", затем "операция обработки сообщения в Кафке", конечно-же "операция по доставке данных сервису итоговой обработки", в ней-же "операция контроля достоверности данных", далее "операция по передаче в Графану", естественно "операция визуализации данных в Графане" и т.п.
Не правда ли, очень похоже на операционную карту и технологическую маршрутную карту? А для них есть даже ГОСТы и международные стандарты, и работают они во всех крупных и средних производствах во всем мире (названия могут быть другими но суть та же).
AppCrafter Автор
Вопрос конечно интересный))
Я бы не сказал, что "бизнес-процесс" невнятный и расплывчатый термин. Он очень даже активно используется. Тот же Google выдаёт несколько тысяч результатов по нему.
А ChatGPT показал следующее соотношение между этими двумя терминами:
Бизнес-процесс состоит из множества операций, объединенных общей целью.
Операция — это более мелкая "единица работы", которая выполняется в рамках бизнес-процесса.
Это выглядит достаточно логично.