Вы знаете, что такое модуляризация? В каких случаях стоит применять такой подход, а в каких – нет?
Сегодня мы с вами обсудим ключевые моменты в процессе модуляризации, рассмотрим вопросы построения и подключения модулей, а также обсудим, когда именно данный подход следует применять.
Итак, начнём с главного. Со временем, при разработке программного обеспечения с неправильно выбранной архитектурой разработчику становится сложно расширять и внедрять новую функциональность. Почему так происходит? Потому что становятся актуальными следующие проблемы:
- Нехватка времени. Из-за большой связности кода и невозможности переиспользовать текущие решения в смежных проектах, требуется дополнительное время или ресурсы на реализацию новой функциональности;
- Нехватка ресурсов. Добавление идентичного функционала в текущий или смежные проекты,или реализация нового функционала в существующем проекте требуют подключения дополнительных разработчиков;
- Нехватка финансов. При выделении большего количества времени и ресурсов необходимо тратить большее количество финансов.
И здесь мы вспомним о модуляризации. Модуль – это отдельная независимая функционально законченная часть или фрагмент приложения.
Под модуляризацией понимается процесс разбиения основной или всей части приложения на отдельные составные модули.
Теперь давайте же рассмотрим сами виды модулей и их иерархическую структуру.
Иерархически, модули можно разделить на 3 уровня:
Core Modules - содержат удобные расширения, фасады для работы с системными функциями (например, LAConext), различные переиспользуемые UIKit компоненты и т.п.
В нашей практике примером реализации такого модуля является отдельный сервис для сбора и обработки данных.
Feature Modules - независимые инкапсулируемые части программы, содержащие полную или частичную функциональность какой-то бизнес задачи или фичи.
Например, в отдельный модуль можно вынести сервис авторизации или регистрации.
App - основное приложение, связывающее все модули и реализующее роутинг между ними.
На нашем практическом опыте мы определились, что модули должны обязательно соответствовать некоторым требованиям:
- Независимость - модули должны иметь возможность инкапсулировать в себе часть отдельной функциональности и работать независимо от других модулей;
- Ответственность - модули должны быть ответственны и реализовывать только возложенный на них функционал;
- Переиспользуемость - модули должны иметь возможность быть использованы повторно (в других проектах). Это требование является следствием двух предыдущих, но не является обязательным;
- Инкапсулированность - модули должны скрывать частную реализацию той или иной функциональности, предоставляя публичный доступ только к нужным методам и свойствам.
Создание модуля - процесс рутинный, однако требующий соблюдения определенной последовательности действий.
Проработка API. Каждый модуль должен проектироваться исходя из его предназначения (принцип ответственности), не иметь лишней функциональности и чрезмерного фасада (принцип инкапсулированности).
Источник данных для компиляции. Модуль может содержать исходные ресурсы в различном виде, однако это стоит учитывать при проектировании. Структура модуля представляет собой:
- файл спецификации *.podspec - обязательно;
- файлы README и LICENSE - опционально;
- исходные файлы в виде *.framework, *.xcframework или набора *.swift и *.xib файлов, или любых других - обязательно.
Метод размещения модуля. Основные 2 способа, которые используем мы
при размещении модулей в проекте:
- в виде *.xcodeproj непосредственно в проекте;
- в виде отдельной зависимости, подключенной через менеджер зависимостей (мы используем cocoapods).
Для подключения модуля мы используем менеджер зависимостей cocoapods. Такой способ дает нам несколько преимуществ, которые рассмотрим далее.
Поставка модуля в виде pod-зависимости требует донастройки дополнительных компонентов:
- Модуль необходимо размещать в отдельном git-репозитории, который обычно создается в приватном пространстве
- Модуль необходимо расширить добавлением файла спецификации *.podspec
*.podspec – это файл спецификации, описывающий pod (модуль). Содержит в себе информацию о том, откуда должны быть извлечены исходные данные, общее описание модуля, параметры сборки и общие метаданные.
Наглядный пример синтаксиса podspec-файла:
s.name - имя модуля;
s.version - версия модуля;
s.summary и s.homepage - доп. информация (опционально);
s.author и s.license - информация о разработчике и лицензия (желательно);
s.platform - платформа для использования;
s.source - ссылка на репозиторий, откуда будет загружаться модуль;
s.ios.deployment_target - мин. версия платформы, которую поддерживает модуль (опционально);
s.ios.vendored_frameworks - имя фреймворка, который поставляется в модуле;
spec.source_files - путь на файлы, которые поставляются в модуле;
spec.dependency - имя дополнительной зависимости, необходимой для работы модуля (опционально).
Например, spec.dependency ‘Alamofire’, ‘4.9.3’ определяет, что для работы модуля AuthorizationService необходимо дополнительно установить библиотеку Alamofire версии 4.9.3.
Разместив модуль в git-репозитории и настроив для него файл спецификации, мы можем подключить его в наш проект через менеджер зависимостей cocoapods:
pod AuthorizationService, :git => 'https://gitlab/authorizationservice.git'
Cocoapods - мощный инструмент, позволяющий достаточно гибко поставлять модули в проект. На практике, часто приходится иметь возможность управлять версиями функциональностей, производить upgrade/degrade того или иного модуля, хранить несколько версий одного и того же модуля для различных проектов, и cocoapods позволяет легко этим управлять.
pod AuthorizationService, :git => 'https://gitlab/authorizationservice.git', :tag => ‘1.2.1’ - загрузка определенной версии модуля по tag
pod AuthorizationService, :git => 'https://gitlab/authorizationservice.git', :branch => ‘dev’ - загрузка определенной версии модуля из конкретного branch
pod AuthorizationService, :git => 'https://gitlab/ametov/authorizationservice.git', :commit => ‘45ae420wf’ - загрузка определенной версии модуля по hash commit
Для регулирования версии достаточно выполнить загрузку модуля посредством команды pod install.
Пришло время подвести итоги. Мы пришли к выводу, что модуляризация привносит в проект ряд технических и организационных преимуществ, а именно:
- Масштабируемость - разработка каждого модуля ведется отдельно, имеется возможность расширять команды разработки, которые работают над отдельными модулями и не пересекаются;
- Экономия ресурсов - модули реализуются с учетом возможности переиспользоваться или дорабатываться под конкретные нужды;
- Связность кода, а точнее ее уменьшение. Модули не пересекаются друг с другом, уменьшение связей и соблюдение code style влечет за собой упрощение разработки и уменьшение входного порога для разработчиков.
Однако следует понимать, что модуляризация – это процесс, который следует выстраивать при разработке продукта, закладывать дополнительное время на создание и тестирование модулей, поэтому на начальном этапе данный подход может существенно затруднить и затормозить разработку, однако в будущем заметно упростит разработку продукта.
А вы что думаете о модуляризации?