История началась с того, что у приложения Getcontact стало больше пользователей, кодовая база приложения росла и росла, с другой стороны, к нам присоединялись новые разработчики для реализации новых функций.
Нам требовалась более автономные и независимые команды, и в этом случае мы обнаружили, что монолитная кодовая база не подходит. Тогда мы решили разделить наше приложение на модули и вот история этого.
Преобразование монолитного приложения в модульную архитектуру — это путь к масштабируемости, и очень важно установить набор правил для построения проекта. Это предполагает создание различных типов модулей, каждый из которых служит определенным целям, и выработки правил для них. Эти правила и стали нашим маяком, позволяющим видеть и решать предстоящие задачи.
Рассмотрим по порядку, в чем смысл каждого из них.
Модули функций
Модуль фич (Feature Module) — это как мини-супергерой в приложении, отвечающий за решение небольших, но важных задач. Это бизнес-логика, логика пользовательского интерфейса или логика данных определенных областей.
Вот здесь-то и кроется хитрость, позволяющая предотвратить возникновение беспорядочных круговых зависимостей. Модули функций должны заниматься своими делами и не связываться друг с другом. Это означает, что модуль A не должен иметь представления о том, чем занимается модуль B. Пусть они будут независимыми и самостоятельными.
Зависимости модулей
Модули координаторов
Иногда мы сталкиваемся с ситуацией, когда необходимо объединить два функциональных модуля на одной странице. Вот тут-то и приходят на помощь наши надежные модули координаторы, позволяющие использовать несколько функциональных модулей в рамках одного графа.
Пример экрана координатора
Возможно, вам интересно, как это работает. Вот секретный соус. Мы создаем новый сверкающий модуль и добавляем в менеджер зависимостей внутри него как Feature A, так и Feature B. Затем на уровне кода мы можем легко создать страницу или компонент, демонстрирующий совокупную мощь этих функций. Это похоже на суперкоманду, в которой функции работают слаженно.
Кроме того, координаторы дают нам возможность заменять функции в модуле верхнего уровня, основываясь на определенной логике, например, показывать функцию определенной группе пользователей, включать функцию в качестве эксперимента в конкретной версии или проводить A/B-тестирование — все это входит в обязанности координаторов.
Модуль координатора
Кроме того, в некоторых случаях нам необходимо иметь общую бизнес-логику, например, проверять аутентификацию или данные пользователя. Для этого случая нам необходимы подключаемые модули
Модули плагинов
Представим себе ситуацию, когда у нас есть запрос конфигурации, который мы хотим использовать во всем приложении. Первоначальный подход может заключаться в том, чтобы реализовать этот запрос отдельно в каждом функциональном модуле, где он необходим. Однако такой подход не является идеальным, поскольку в случае будущих изменений в модели или бизнес-логике нам придется тщательно модифицировать весь написанный код.
В двух словах, именно здесь на помощь приходят модули плагинов. Они берут на себя ответственность за обработку небольших фрагментов общей бизнес-логики. Если у нас есть фрагмент кода, который должен быть общим для всех модулей и не включает в себя элементы пользовательского интерфейса, а также не предоставляет инфраструктурных функций, то он идеально подходит в качестве модуля плагина. Это позволяет упростить управление и сопровождение общего кода, сделать будущие модификации и обновления менее обременительными.
Пример модуля плагина
Модули библиотек
Помимо модулей-координаторов и модулей-плагинов, необходимо иметь несколько общих классов, структур данных или библиотек. Это позволяет избежать дублирования кода и способствует его многократному использованию. Для этого мы вводим специальный тип модулей — библиотечные модули.
Библиотечные модули берут на себя ответственную роль по предоставлению компонентов инфраструктуры или библиотек операционной системы. Например, если нам необходимо решать такие задачи, как отправка сетевых запросов или локальное хранение данных, мы создаем такие модули, как Network, Cache и Database. Эти модули инкапсулируют необходимые функциональные возможности, обеспечивая организацию и многократное использование кода для этих конкретных целей.
Модули запуска (лаунчеры)
Последним, но не менее важным, является тип модулей лаунчеров.
Представьте, что мы завершили рефакторинг и разделение модулей, в результате чего получили несколько модулей, каждый из которых выполняет свою роль, как отдельные детали Lego. Теперь у нас есть пазл из нескольких кусочков, и пришло время собрать их воедино. Для этого мы создаем новый модуль, который выступает в роли конечного соединителя, отвечающего за сбор всех остальных модулей и построение графа зависимостей для всего приложения.
Представьте себе, что это главный конструктор в мире Lego, собирающий все отдельные компоненты в целостную структуру. Этот центральный модуль выполняет роль клея, соединяя все части и обеспечивая их гармоничную работу в единое целое. Он как бы наносит последние штрихи на нашу головоломку, превращая ее в законченный и функциональный шедевр.
Еще один аспект: для повышения скорости разработки мы можем создавать несколько модулей запуска. Допустим, команда A сосредоточена на разработке функций A, B, C и D, а команда B работает над функциями X, Y и Z.
В этом случае обе команды могут создать свой собственный модуль запуска на этапе разработки. Такой подход позволяет им работать независимо друг от друга, не беспокоясь о лишних зависимостях и загрузке не связанных между собой модулей. Все, что им нужно сделать, — это добавить свои специфические функции и координаторы в соответствующие модули запуска. При каждом запуске вместо того, чтобы перемещаться по всему потоку приложения, они могут просто сосредоточиться на своих конкретных функциях и взаимодействовать с ними.
Примечание: здесь приведен пример, дающий более полное представление о Getcontact-Bootcamp, однако следует учитывать, что он не является полной реализацией данной архитектуры.
Еще про модуляризацию
Дополнительные материалы: