руководитель группы, Яндекс, Сербия, г. Белград
ИСПОЛЬЗОВАНИЕ МОДУЛЬНЫХ МОНОЛИТОВ ДЛЯ РАЗРАБОТКИ МАСШТАБИРУЕМЫХ WEB-ПРИЛОЖЕНИЙ
АННОТАЦИЯ
Модульные монолиты представляют собой перспективный подход в разработке масштабируемых веб-приложений. Этот архитектурный стиль сочетает преимущества классических монолитных приложений с гибкостью микрофронтендов. Основной идеей модульных монолитов является разделение системы на автономные модули с четко определенными границами, минимальными зависимостями и изоляцией данных. Это позволяет упростить сопровождение и тестирование приложения, а также увеличить его производительность и масштабируемость.
Одним из ключевых аспектов является строгая модульная структура, которая достигается за счет использования специализированных программных интерфейсов и обеспечения модульной изоляции. Такой подход способствует улучшению архитектуры приложения, снижению технической сложности и повышению устойчивости к изменениям, что делает его особенно актуальным для проектов с возрастающей сложностью и большими объемами данных.
ABSTRACT
Modular monoliths represent a promising approach in the development of scalable web applications. This architectural style combines the advantages of classic monolithic applications with the flexibility of microfrontends. The main idea of modular monoliths is to divide the system into autonomous modules with well-defined boundaries, minimal dependencies, and data isolation.
This makes it easier to maintain and test the application, as well as increase its performance and scalability. One of the key aspects is the strict modular structure, which is achieved through the use of specialized application programming interfaces and ensuring modular isolation. This approach helps to improve the application architecture, reduce technical complexity, and increase resilience to changes, which makes it especially relevant for projects with increasing complexity and large amounts of data.
Ключевые слова: модульные монолиты, масштабируемые веб-приложения, изоляция данных, модульная архитектура, программные интерфейсы, паттерны проектирования, системный дизайн, микрофротенд.
Keywords: modular monoliths, scalable web applications, data isolation, modular architecture, software interfaces, design patterns, system design, microfrotend.
Введение
Современная разработка веб-приложений сталкивается с растущей сложностью систем и увеличивающимися требованиями к их производительности и масштабируемости. В условиях ускоренной цифровизации бизнеса и увеличения объемов данных, традиционные подходы к разработке монолитных приложений начинают демонстрировать свои ограничения.
Одной из наиболее актуальных проблем в этой области является необходимость адаптации архитектуры программных систем для обеспечения их гибкости и устойчивости к изменениям [1]. В связи с этим, концепция модульных монолитов приобретает все большую популярность среди разработчиков, предлагая сбалансированное решение между классическими монолитами и делением приложения на микрофронтенды.
Модульные монолиты представляют собой подход, который позволяет разделить систему на отдельные модули, сохраняя при этом единую кодовую базу. Каждый модуль выполняет свою бизнес-логику, минимизируя зависимости от других частей системы [2].
Этот подход предоставляет возможность более гибко управлять приложением, улучшать его масштабируемость и производительность, что особенно важно для крупных и сложных веб-приложений. В условиях роста требований к качеству и скорости разработки, архитектурный стиль модульных монолитов оказывается крайне актуальным.
Актуальность данной темы обусловлена необходимостью решения проблемы сохранения эффективности труда команды разработчиков, несмотря на быстрое увеличение кодовой базы современных веб-приложений. При увеличении объема функциональности системы важно сохранить высокую производительность и избежать деградации качества программного продукта, что может быть достигнуто через правильную организацию модульной структуры.
Преимущества модульных монолитов включают упрощение разработки, сокращение времени на внедрение новых функций и уменьшение риска ошибок за счет изоляции модулей и четко определенных программных интерфейсов.
Цель данной работы заключается в исследовании особенностей применения модульных монолитов для разработки масштабируемых веб-приложений, анализе их преимуществ и недостатков, а также оценке перспектив этого подхода в современных условиях разработки программного обеспечения.
1. Общая теория модульных монолитов
Модульность представляет собой подход к проектированию или производству объектов в виде отдельных компонентов.
Это определение является обобщённым и не совсем подходит для разработки программного обеспечения. Более точное определение можно дать через понятие модульного программирования. Модульное программирование – это метод создания программного обеспечения, основанный на разделении программы на независимые, взаимозаменяемые модули. Каждый из модулей включает все необходимые элементы для реализации определённой функциональной задачи.
Программный интерфейс модуля описывает доступные элементы для взаимодействия с другими модулями, а реализация модуля заключается в программном коде, соответствующем этим элементам [3]. Для того чтобы архитектура могла считаться модульной, модули должны:
- Быть автономными и взаимозаменяемыми;
- Содержать все необходимые компоненты для выполнения определённой части бизнес-логики;
- Обладать чётко установленным программным интерфейсом.
Рассмотрим эти аспекты более подробно. Важно отметить, что стабильность модулей, от которых зависит рассматриваемый модуль, имеет большое значение. Чем реже изменяются эти модули, тем меньше они оказывают влияние на работу зависимого модуля. Эти положения отражены в принципе стабильной зависимости (Stable Dependency Principle).
В зависимости от конкретной архитектуры, контракт модуля может принимать различные формы. Это может быть фасад для синхронных вызовов, как в случае с простым вызовом функций, или же публикация событий для асинхронного взаимодействия.
Независимо от формы, контракт представляет публичный API модуля, делая инкапсуляцию ключевым элементом модульности [4]. Далее на рисунке 1 будет проиллюстрировано сравнение трёх архитектурных подходов к разработке масштабируемых веб-приложений: классического монолита, микрофронтенда и модульного монолита,
Линия, представляющая модульный монолит - выделена жёлтым цветом, демонстрирует более стабильное снижение скорости разработки по мере увеличения размера проекта по сравнению с традиционным монолитом и микрофронтендом. В малых проектах разработка модульного монолита сопряжена с большими трудностями по сравнению с классическим монолитом, что связано с увеличенными накладными расходами на проектирование модулей.
Этот процесс требует значительных усилий по структурированию системы на этапе проектирования, что может усложнить разработку. В то же время использование микрофронтенда предполагает существенные первоначальные вложения в инфраструктуру, что приводит к заметному снижению скорости разработки на начальных стадиях проекта. Однако по мере роста проекта производительность классического монолита значительно ухудшается. В таких условиях модульный монолит демонстрирует схожие показатели с микрофронтендом, несмотря на более скромные требования к инфраструктуре на ранних этапах разработки. Это свидетельствует о том, что модульный монолит лучше справляется с ростом сложности проекта и поддерживает производительность разработки на более высоком уровне в отличие от других архитектурных решений.
Рисунок 1. Сравнение трех архитектурных подходов к разработке масштабируемых веб-приложений
Таким образом, модульные монолиты представляют собой компромисс между классическим монолитом и микрофронтендом, предлагая более гибкий и масштабируемый подход к разработке веб-приложений, особенно при увеличении размеров проектов.
2. Правила модульных монолитов
Модульные монолиты строятся на основе идей, направленных на улучшение модульной структуры, повышения удобства обслуживания и улучшения масштабируемости в рамках единой кодовой базы. Выделяют 3 основных правила модулей, в частности ими являются:
1. Соблюдение строгой структуры модуля;
2. Обеспечение изоляции модулей между собой;
3. Унификация коммуникации между модулями.
Далее более подробно остановимся на каждом правиле.
Так если говорить о структуре модуля, то она является фундаментом модульных монолитов и предполагает строгую организацию кода в рамках чётко определённых модульных границ.
Основной задачей является обеспечение того, чтобы каждый модуль представлял собой отдельный компонент системы, отвечающий за определённую бизнес-логику или функциональность. Такой подход позволяет упрощать управление кодовой базой и делает приложение более читаемым и поддерживаемым [5]. Ниже на рисунке 2 будут представлены элементы структуры модуля.
Рисунок 2. Элементы структуры модуля
В свою очередь крайне важно, чтобы каждый модуль был реализован похожим образом, иначе у команды могут возникнуть трудности с переключением между разработкой разных модулей и разных частей системы. Также стоит ограничить как набор технологий, так и верхний уровень структуры директории у модуля, например разделить весь код модуля логически на 4 или более сегментов.
Что позволит достичь того, что у каждого сегмента будет своя зона ответственности, как например это отражено ниже на рисунке 3.
Рисунок 3. Сегменты модуля
Таким образом сегменты позволяют достичь следующего:
1) унифицировать кодовую базу
2) легко решить куда поместить новый код;
3) достичь быстрого переключения разработчика между модулями;
4) защитить код от смешивания UI и бизнес-логики.
Далее рассмотрим общую характеристику изоляции кода. Суть изоляции заключается в том, что каждый модуль должен минимизировать свои зависимости от других модулей, что позволяет избежать «эффекта домино», когда изменение в одном модуле приводит к необходимости изменения других модулей.
Это особенно критично для поддержания гибкости и возможности масштабирования системы в долгосрочной перспективе. Изоляция достигается за счёт чётко определённых программных интерфейсов, через которые модули могут взаимодействовать между собой.
Эти интерфейсы должны быть стабильными [6].
Первым важным аспектом изоляции является устранение побочных эффектов при подключении модуля. Под побочными эффектами подразумевается любое несанкционированное взаимодействие модуля с глобальными объектами, загрузка каскадных таблиц стилей (CSS) или изменение DOM-структуры других модулей. Например, доступ к глобальным объектам может привести к непредсказуемому поведению системы, если несколько модулей будут одновременно изменять одни и те же переменные. Изоляция помогает предотвратить такие конфликты, обеспечивая, что каждый модуль работает в рамках своей зоны ответственности, не влияя на работу других частей системы.
Контроль доступа к функциональности модуля — второй ключевой аспект изоляции. Для этого используется система импортов, которая позволяет четко определить, какие элементы кода (файлы, стили, ассеты) могут быть использованы вне модуля, а какие остаются внутри.
Важно также ограничить доступ к хранилищам данных, таким как IndexedDB или Local Storage, для предотвращения случайных или намеренных изменений данных модуля извне.
Эффективный контроль доступа гарантирует, что модули обмениваются только необходимой информацией, сохраняя внутреннюю целостность и безопасность данных.
Для организации контроля доступа на практике рекомендуется создавать индексный файл в корне каждого модуля.
Этот файл служит точкой входа, в которой определен круг функций и переменных, доступных другим модулям. Внутри индексного файла можно использовать различные техники, такие как ES модули или внедрение зависимостей, которые позволяют определить набор доступных методов для использования в других модулях.
Внедрение зависимостей (Dependency Injection, DI) является распространённым и эффективным методом, применяемым в разработке программного обеспечения. Применение DI способствует созданию гибкой архитектуры приложений, обеспечивая отделение логики создания зависимостей от их использования.
В этом подходе объекты, которые требуются для выполнения определённых операций, создаются и управляются внешними компонентами, а не самим объектом, использующим эти зависимости.
Принцип внедрения зависимостей требует, чтобы модули не полагались на конкретные реализации друг друга, а использовали абстракции. Такой подход даёт модулям полный контроль над моментом и условиями создания зависимости, а также над функциональностью модуля, доступной для использования в других модулях.
Основная задача внедрения зависимости — уменьшить связанность между модулями, обеспечивая заменяемость реализаций без изменения логики зависимого модуля. Это делает DI одним из ключевых элементов разработки в современных веб-приложениях и корпоративных системах [7].
Событийная модель (Event-driven), в свою очередь, позволяет организовать взаимодействие между модулями через события, что способствует снижению жестких зависимостей и улучшению гибкости архитектуры приложения.
Под событием может пониматься любое действие, совершенное пользователем, будь то нажатие на кнопку, прокрутка страницы или ввод данных в текстовое поле. Однако такой архитектурный подход находит свое применение не только в пользовательских интерфейсах, но и в разработке серверной части [8].
Ключевая концепция событийно-ориентированной архитектуры заключается в разделении системы на автономные модули, которые взаимодействуют между собой через события. Каждый модуль занимается обработкой лишь тех событий, которые имеют для него значение, что позволяет формировать независимые компоненты, которые можно разрабатывать по отдельности.
Эти компоненты, будучи объединены в сложные системы, сохраняют свою независимость и могут функционировать в автономном режиме [9].
Если же говорить об изоляции CSS, то она предусматривает чёткое разделение стилей приложения на независимые модули, каждый из которых несёт ответственность за свою область функциональности. Изоляция CSS в модульном монолите обеспечивает независимость стилей каждого модуля, что упрощает разработку новых модулей, делает более безопасным внесение изменений в существующие стили и способствует более простому масштабированию проекта.
Shadow DOM - один из ключевых компонентов спецификации Web Components, обеспечивающий полную изоляцию DOM и стилей для каждого компонента.
Он создаёт отдельное «теневое дерево», которое является самостоятельной частью основного DOM, и любые стили, применённые внутри этого теневого дерева, не могут воздействовать на другие элементы за его пределами.
Этот подход гарантирует полную изоляцию стилей внутри каждого компонента, что идеально подходит для модульных монолитов.
Преимущества использования Shadow DOM заключаются в том, что он обеспечивает строгое разделение не только стилей, но и структуры элементов. Стили, применённые внутри Shadow DOM, не могут «просочиться» наружу и изменить внешний DOM, и наоборот — глобальные стили не могут повлиять на элементы внутри теневого DOM. Это позволяет создавать полностью независимые модули, каждый из которых сохраняет уникальные визуальные особенности, независимо от глобальных стилей приложения.
Однако следует учитывать, что использование Shadow DOM может потребовать дополнительных усилий по интеграции, особенно если приложение активно взаимодействует с фреймворками, не имеющими встроенной поддержки Web Components. Тем не менее, для модульных монолитов, требующих высокой степени изоляции, это один из самых эффективных подходов [10].
Последним же правилом является изолирование данных приложения на уровне модулей.
Изоляция данных в модульных монолитах играет ключевую роль в разработке масштабируемых web-приложений, так как она способствует разделению ответственности между модулями и уменьшает взаимные зависимости компонентов системы. Модульный монолит подразумевает организацию кода таким образом, что каждая его часть выполняет строго определенные задачи, оставаясь независимой от других компонентов, что делает приложение более управляемым и устойчивым к изменениям.
Основной принцип изоляции данных заключается в предотвращении прямого доступа модулей к данным друг друга.
Это достигается за счет четкого разграничения областей видимости и использования специализированных программных интерфейсов для взаимодействия между модулями.
Такой подход позволяет контролировать, какие данные и методы их модификации доступны для использования другими частями системы, снижая риск возникновения побочных эффектов при внесении изменений в систему. При этом изоляция данных способствует упрощению процесса интеграции новых модулей и их модификацию, позволяя разработчикам вносить изменения локально, не затрагивая остальные части приложения [11].
Еще одним важным аспектом изоляции данных является использование надежного хранилища для данных модуля Важно, чтобы каждый модуль был автономным в плане хранения данных и работы с ними. Это не только способствует улучшению производительности системы за счет горизонтального масштабирования, но и повышает ее отказоустойчивость, так как сбои в одном модуле не приведут к критическим последствиям для всей системы. При этом изоляция данных способствует упрощению тестирования отдельных модулей, поскольку их поведение можно проверять в условиях, приближенных к реальным, без зависимости от внешних компонентов.
3.Рекомендации по делению кода на модули
В современном процессе разработки программного обеспечения особое внимание уделяется правильной организации кода, которая позволяет повысить читаемость, тестируемость и масштабируемость приложения.
Одним из подходов, который активно используется для достижения этих целей, является Feature Sliced Design (FSD), представляет собой подход к проектированию модульной архитектуры, который основывается на разделении функциональных областей приложения по различным аспектам, с учетом бизнес-логики, пользовательских интерфейсов и технических требований.
Одной из ключевых рекомендаций является разделение кода на слои, где каждый слой отвечает за определенный аспект работы приложения. В FSD выделяют несколько уровней:
- Приложение (App) представляет собой комплексный уровень, включающий как технические, так и бизнес-аспекты системы. Здесь могут располагаться компоненты, такие как провайдеры контекста, а также инструменты для аналитики. Основная задача данного уровня — загрузка всех остальных модулей приложения и настройка коммуникации между ними.
- Страницы (Pages) служат основой для создания полных страниц в веб-приложениях или экранов в мобильных приложениях. Этот уровень схож по структуре с Widgets, однако масштабы его элементов шире. Внутри каждого элемента на этом уровне содержатся компоненты интерфейса, которые могут быть напрямую интегрированы в навигацию приложения. Также здесь допускается логика для получения данных и обработки ошибок.
- Виджеты (Widgets) представляют собой независимые элементы пользовательского интерфейса, сформированные на базе более мелких единиц, таких как сущности и функции. Их задача — дополнять интерфейс интерактивными элементами. Чаще всего, бизнес-логика на этом уровне не хранится, поскольку она сосредоточена в фичах. Однако в случаях, когда виджет включает сложную интерактивность, бизнес-логика может быть размещена и здесь, особенно если она специфична для данного элемента.
- Функции (Features) направлены на реализацию пользовательских действий, которые позволяют взаимодействовать с бизнес-сущностями, достигая поставленных целей. Эти действия могут включать интерактивные элементы интерфейса, внутренние состояния и API-запросы, необходимые для выполнения операций, создающих ценность для пользователя.
- Сущности (Entities) — это ключевые концепты, лежащие в основе проекта и представляющие реальные бизнес-объекты. Содержимое этого уровня включает статические компоненты интерфейса, а также данные и API-запросы, необходимые для работы с этими сущностями.
- Общий (Shared) уровень состоит из универсальных модулей и абстракций, которые не привязаны к специфике конкретного проекта. Он не подчиняется структуре слайсов, характерной для других уровней, и содержит сегменты, которые могут быть использованы в различных контекстах [12].
Такое разделение помогает улучшить читаемость и поддерживаемость кода, делая его более модульным и независимым.
Feature Sliced Design предполагает атомарность модулей — каждый модуль должен быть минимально необходимым для выполнения своей задачи. Это значит, что если приложение состоит из нескольких модулей, каждый из них должен иметь чёткую и ограниченную зону ответственности. Такой подход делает код более устойчивым к изменениям и упрощает его тестирование и рефакторинг. Ниже на рисунке 4 будет представлен данный подход.
Рисунок 4. Feature Sliced Design
При разработке сложных программных систем критически важно уделять внимание правильному направлению импортов между модулями. Это не только обеспечивает изоляцию компонентов, но и предотвращает возникновение «циклических зависимостей». Такие зависимости могут привести к трудностям в сопровождении и масштабировании кода. На рисунке 5 отражён пример данной рекомендации.
Рисунок 5. Направление импортов
Запрет зависимостей между модулями внутри одного слоя архитектуры является одним из ключевых принципов в Feature Slice Design. Этот подход, также известный как «горизонтальная изоляция», способствует улучшению структуры приложения, так как модули, находящиеся на одном уровне, не зависят друг от друга напрямую, что упрощает их тестирование, обновление и рефакторинг. Взаимодействие между модулями должно происходить через абстракции и интерфейсы, которые обеспечивают чёткое разделение ответственности и исключают прямые связи между компонентами. Таким образом, внесение изменений в один модуль не приводит к необходимости модифицировать другие модули, что значительно повышает гибкость системы, что отражено на рисунке 6.
Рисунок 6. Запрет импортов внутри слоя
Таким образом строгая изоляция логики внутри модулей позволяет локализовать изменения, минимизировать риски и сделать приложение более устойчивым к изменениям. В результате, правильная организация направлений импортов обеспечивает модульность и надежность системы, снижая сложность ее поддержки и ускоряя процесс разработки.
Заключение
Использование модульных монолитов в разработке веб-приложений способствует значительному улучшению гибкости и масштабируемости системы. Разделение системы на независимые модули с четко определенными границами позволяет уменьшить взаимозависимости компонентов и упростить управление кодом. Это делает процесс сопровождения и тестирования приложения более эффективным, так как каждый модуль может быть протестирован и модифицирован отдельно, не затрагивая остальные части системы.
То есть можно сказать, что модульные монолиты представляют собой сбалансированное решение между классической монолитной архитектурой и микрофронтендами. Они объединяют лучшие черты обоих подходов, предлагая гибкость и модульность микрофронтенда в рамках единой кодовой базы, что особенно важно для крупных проектов.
В долгосрочной перспективе такой подход способствует повышению устойчивости системы к изменениям и улучшению производительности разработки, делая модульные монолиты перспективным выбором для создания масштабируемых веб-приложений.
Список литературы:
- Кряжева Е. В., Королев А. Е. Проектирование веб -приложения каталога пользовательских интерфейсов //Дневник науки. – 2024. – №. 8.
- Шишкина К.С., Белаш В.Ю. Проектирование модели веб-приложения для студенческого общежития кгу им. циолковского //Дневник науки. – 2024. – №. 5.
- Шахов А. Э. Архитектурное проектирование веб-приложений //Актуальные проблемы науки и образования в условиях современных вызовов (шифр–МКАП 24). – 2023. – С. 27-29.
- Богданенко, Д. А. Подходы к архитектурному проектированию веб-приложений / Д. А. Богданенко // Молодой ученый. — 2018. — № 9 (195). — С. 24-29.
- Кряжева Е. В., Васина Т. А. Общие подходы к проектированию ВЕБ-приложений //Заметки ученого. – 2021. – №. 9-2. – С. 32-36.
- Кравченко Д. А. Микросервисная архитектура //Интерактивная наука. – 2022. – №. 4 (69). – С. 43.
- Мытников А. Н., Анохин В. Р. Механизмы обеспечения информационной безопасности в фреймворке Nestjs //Современные вопросы естествознания и экономики. – 2023. – С. 574-579.
- Su R., Li X. Modular Monolith: Is This the Trend in Software Architecture? //Proceedings of the 1st International Workshop on New Trends in Software Architecture. – 2024. – С. 10-13.
- Архитектура event-driven приложений. [Электронный ресурс] Режим доступа: https://habr.com/ru/companies/otus/articles/740578/ (дата обращения 21.09.2024).
- Деконструкция монолита: Максимально производительный подход к проектированию программ. [Электронный ресурс] Режим доступа: https://habr.com/ru/companies/piter/articles/844572/ (дата обращения 21.09.2024).
- Gonçalves N. et al. Monolith modularization towards microservices: Refactoring and performance trade-offs //2021 IEEE 18th International Conference on Software Architecture Companion (ICSA-C). – IEEE, 2021. – С. 1-8.
- Слои. [Электронный ресурс] Режим доступа: https://feature-sliced.design/ru/docs/reference/layers (дата обращения 21.09.2024).