Введение

Как думаете, какой паттерн самый масштабируемый? Может быть, это вселенная? Или жир в вашем животе? А как насчет фрактала? Это бесконечный узор, где каждая из частей и есть отдельный узор. Выглядит это примерно так:

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


Недостатки мышления, отвергающего масштабируемость

Каждый развивающийся продукт рано или поздно приходится масштабировать. И если вам кажется, что вы не столкнетесь с масштабируемостью, это означает, что ваша архитектура еще “сырая”. Все боятся наступления этого дня, ведь вам придется потратить на масштабируемость много денег и времени. Давайте подумаем, от каких мыслей нужно отказаться, чтобы избежать большого стресса в будущем.

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

У меня не хватает времени. Конечно, не хватает. В динамично меняющемся мире каждая компания стремится производить больше и за меньшее количество времени. Иначе бизнес просто погибнет. Подобный образ мышления приводит к тому, что вы меньше думаете о нужных вещах и почти всегда сталкиваетесь из-за этого с неприятными последствиями. Общий совет для всех: сделайте паузу и подумайте о том, как разделить приложение на инкапсулированные части и устранить зависимость между ними. Помните: архитектура не обязательно должна быть идеальной. Решения для всех вариантов уже существуют. Заимствуйте и адаптируйте их под свои потребности.

Я думаю о продукте, а не о команде. Команда развивается вместе с продуктом. Учтите, что команды тоже должны масштабироваться. Если команда не масштабируется, это приводит к непреодолимым проблемам. Потеря ключевых инженеров равносильна потере части бизнес-структуры. В результате происходит обмен знаниями внутри и между командами, затем знания “рассеиваются” по разным командам, из-за чего в голове у каждого инженера, недавно принятого на работу в компанию, начинается путаница. Чтобы избежать этих трудностей, нужно делать с командами то же самое, что и с продуктом: инкапсулировать и разделять.


Очистите свой разум и займитесь масштабированием

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

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

Монорепозиторий представляет собой единый репозиторий, который содержит все проекты, включая фронтенд и бэкенд. Рассмотрим основные принципы монорепозитория.

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

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

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

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


Краеугольные камни масштабируемой конструкции Angular

После того, как мы коснулись высокого уровня масштабируемости, пришло время пойти дальше и узнать, как масштабировать фреймворк Angular без ограничений. Для начала поговорим об основных компонентах, из которых будет состоять наша архитектура.

Модули Angular  —  наиболее детализированная часть большого приложения. Модули инкапсулируют небольшую часть логики и состоят из набора компонентов, сервисов, директив и т. д. Они также могут встраиваться друг в друга. Идея проста  —  обернуть логику и встраивать ее по мере роста сложности.

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

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

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


Разделяйте и властвуйте в контексте масштабирования Angular

Вы наверняка слышали фразу “разделяй и властвуй” тысячу раз в своей жизни. В ней заложен глубокий смысл. Возьмем ее на вооружение для нашего проекта. Начнем со структуры папок проекта Angular и потом будем его масштабировать.

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

Вроде все просто? Теперь давайте разберемся с терминами.

  • Функции (features). Большой фрагмент бизнес-логики, обычно связанный с той или иной страницей и завернутый в один модуль. Когда он начинает расти и обзаводится дочерними страницами, он может быть сгруппирован более детально, например:
-features
-login
-books-gallery

И можно осуществить такое масштабирование:

-features
-login
-books
-books-gallery
-components
-services
-utils
-types
-configs
-books-details
  • Libs. Это своего рода корзина, куда мы складываем все многократно используемые данные, разделенные между функциями. Она может содержать виджеты, компоненты пользовательского интерфейса, хранилище данных, утилиты, типы и конфигурации.
-libs
-ui-components
-widgets
-data-store
-utils
-types
-configs
  • UI-компоненты (ui-components). Это формальные компоненты, очень специфичные для UI. Это своего рода библиотека пользовательского интерфейса. У них есть сходство с элементами материального интерфейса, состоящего из кнопок, карточек и диалоговых окон. Эти компоненты можно повторно использовать в других проектах для сохранения принципа последовательности.
  • Виджеты (widgets). Небольшие бизнес-компоненты, строительные блоки нескольких функций. Они также могут состоять из ui-компонентов, например book-card содержит в себе ui-компоненты  —  карточку и кнопку, а также используется в нескольких функциях  —  books-gallery, books-details.
  • Хранилище данных (data store). Содержит разделяемое состояние (state) и логику, связанную с API, которая может быть внедрена в другую функцию. Например, NgRx-хранилище.
  • Утилиты (utils). Простые вспомогательные функции, используемые во всех функциях. Могут находиться внутри одной функции, но не являются многоразовыми.
  • Типы (types). Совместно используемые типы, интерфейсы и перечисления.
  • Настройки (configs). Папка для всех многократно используемых констант и mock-объектов.

Окончательная структура единого проекта будет выглядеть следующим образом:

-features
-login
-books
-books-gallery

-components
-services
-utils
-types
-configs

-books-details
-libs
-ui-components
-widgets
-data-store
-utils
-types
-configs

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

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

-apps
-login
-features
-libs
-books
-features
-libs
-shell
-libs

-ui-components
-widgets
-data-store
-utils
-types
-configs

Возможно, вы заметили новое приложение под названием shell. Оно используется в микрофронтенде для определения приложения хоста, которое лениво загружает другое микроприложение.


Заключение

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

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Vitalii Shevchuk: 🔥 How to Scale Angular Without Limits

Предыдущая статьяЯзык C: основы синтаксиса
Следующая статьяКак запросить датафрейм Pandas с помощью SQL