Я работаю с одностраничными приложениями с 2014 года (посмотрите мой профиль в LinkedIn, чтобы узнать, чем я занимаюсь сейчас), и день за днем ​​сложность фронтенд-разработки только возрастала. Когда я начал работать с Angular 2 еще в 2016 году, не было достойного решения для управления несколькими приложениями, общими компонентами или функциональными модулями. К счастью, сегодня ситуация намного лучше.

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

Эта проблема

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

На следующем изображении показано старое положение вещей - в основном каждое репо содержало одно приложение Angular. После успешной сборки основной ветки приложение было развернуто в определенной среде.

Вот те цели, которых я хотел достичь в итоге:

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

Оглядываться

Я потратил некоторое время на исследование и нашел эти два наиболее распространенных способа управления общими зависимостями. А теперь давайте присмотримся.

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

Мульти-репозиторий - тот же набор приложений и библиотек, но разделен на несколько репозиториев git. Каждое приложение обычно живет в собственном репо. Общие модули могут храниться в одном или нескольких репозиториях. Пакеты NPM используются для совместного использования кода в приложениях.

Несколько репозиториев с модулями NPM

В целом, создание многоразовых пакетов NPM существует уже давно. И сегодня он по-прежнему широко используется в крупных организациях. Мы, конечно, не говорим об открытых исходных кодах. Потому что модули NPM с открытым исходным кодом являются основными строительными блоками для каждого внешнего проекта. К счастью, в настоящее время существует множество отличных инструментов для управления пакетами. Если вы работаете с Angular, подумайте об использовании ng-packagr для создания библиотек. Подход с несколькими репо дает множество преимуществ, а также несколько недостатков:

Плюсы

  • Детализация - каждый блок кода имеет собственное пространство с инструментами, сторонними модулями и т. Д.
  • Изменение в общем репо не повлияет на иждивенцев, пока вы не обновите его версию.
  • Обновление сторонних зависимостей частями - по одному репо за раз.
  • Конфигурация непрерывного развертывания не требует много изменений.

Минусы

  • Управление несколькими репозиториями с помощью package.json и других файлов конфигурации.
  • Рабочий процесс разработки занимает больше времени - работа в нескольких репозиториях при создании функции, создание нескольких запросов на вытягивание, обновление версий общих библиотек.
  • Ведение частного реестра NPM для хранения общих модулей.
  • Навигация по кодовой базе сложнее - необходимо иметь в виду дерево репозитория, чтобы понимать структуру проекта.
  • Использование ссылки NPM / Yarn для объединения общих библиотек с приложением во время локальной разработки требует дополнительного времени.

Структура проекта в мультирепо

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

Рабочий процесс разработки

Процесс разработки новых функций и / или исправления ошибок может выглядеть следующим образом:

  1. Внесите изменения в общий модуль и любое из приложений.
  2. Свяжите репозиторий модуля с приложением, чтобы проверить изменения (с помощью NPM / Yarn link).
  3. Создайте PR для репозитория модуля - сначала его нужно объединить. После слияния новая версия публикуется в реестре NPM.
  4. Создайте PR для репозитория приложения. При необходимости обновите версию общего модуля. К сожалению, иногда разработчики забывают обновить версии модулей в приложении, когда ошибка или функция были связаны только с определенной библиотекой.
  5. Разверните обновленное приложение на сервере.

Управление версиями модулей NPM

Совместное использование зависимостей в виде модулей NPM имеет смысл для проектов с открытым исходным кодом и крупных корпоративных компаний с десятком команд. Есть много способов, как работать с версиями для развертывания dev и prod. Рассмотрим этот подход:

Для запросов на вытягивание в ветку разработки версия библиотеки обновляется автоматически. Например, вы можете использовать существующую версию модуля + номер сборки: 1.2.0–948785. Затем в package.json приложения найдите последнюю доступную версию: "module1": "^1.2.0" и перестройте ее.

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

Тема управления версиями является спорной и несколько сложной, поэтому попробуйте и посмотрите, работает ли она для нашей организации.

Другие способы рассмотреть

Очень рекомендую посмотреть следующие проекты: Rush from Microsoft:

Rush упрощает жизнь разработчикам JavaScript, которые создают и публикуют множество пакетов из общего репозитория Git.

И открытый проект Лерна:

Lerna - это инструмент, который оптимизирует рабочий процесс по управлению многопакетными репозиториями с помощью git и npm.

Это был общий обзор подхода с несколькими репозиториями, теперь давайте рассмотрим монорепозиторий с помощью Nx Workspace.

Монорепоральный способ

Монорепо какое-то время были в дикой природе. Но если вы с ним не знакомы, посмотрите, пожалуйста, этот незамысловатый обзор.

В зависимости от технического стека в вашем проекте могут быть полезные инструменты для работы с монорепозиториями. Поскольку наш интерфейс построен на новейшей платформе Angular, мы решили оценить Nx - это инструмент, созданный на основе Angular CLI, который расширяет некоторые стандартные функции Angular. Он работает в этой области около 3 лет и собрал множество профессионалов, которые помогли разработать Angular и его экосистему. Прямо сейчас Nx также поддерживает React, Next.js, а также Express и Nest.js.

Подробнее о Nx Workspace.

Monorepo и Nx Workspace имеют следующие преимущества:

  • Разрабатывайте несколько проектов в одном репозитории - серверная часть Angular, React, Node.js. Легко делитесь интерфейсами между проектами FE и BE. Проекты зависят друг от друга и имеют общий код.
  • Нет необходимости в частном реестре NPM, в отличие от подхода с несколькими репо. Не нужно беспокоиться о версиях общих библиотек в случае модулей NPM.
  • Работать над большой функцией намного проще - большая функция реализуется за один PR в одной ветви функции. Легче оставаться в контексте.
  • One package.json - проще управлять зависимостями между несколькими приложениями и библиотеками. Больше никаких обновлений Angular в десятке репозиториев.
  • Затронуто Nx - вам не нужно перестраивать и повторно тестировать каждый проект в монорепозитории во время рабочего процесса CI / CD.

Конечно, есть и минусы:

  • Один package.json - в случае обновления сторонней библиотеки вы должны сделать это за одну попытку для всех приложений и библиотек. Время установки во время сборки больше, так как мы устанавливаем зависимости для всех проектов. Возможным решением для этого может быть использование кешированных node_modules.
  • Ограничения безопасности - сегодня нет возможности ограничить доступ определенных разработчиков к папкам в одном репозитории на Github.
  • Более сложная конфигурация непрерывного развертывания - создание конвейера CI / CD может быть немного сложнее для монорепозитория, чем для обычного репо, содержащего один проект.

Структура проекта в соответствии с настройками Nx по умолчанию

Представьте себе два приложения - портал и админку. А также API, запеченный с использованием Nest.js

apps |
  admin
  admin-e2e
  portal
  portal-e2e
  api
libs |
  components
  styles
  assets
  interfaces
  ...
  etc

Рабочий процесс Monorepo

Краткий обзор рабочего процесса с использованием Nx workspace.

  1. Реализуйте функцию в библиотеке и / или приложении и создайте единый запрос на перенос.
  2. После объединения изменений запускается конвейер непрерывного развертывания. Nx сравнивает изменения в функциональной ветви с основной ветвью и строит, тестирует только затронутые части.
  3. Выполняется развертывание только затронутых приложений.

Заблуждения о монорепо

Подход Monorepo является весьма спорным, и не все полностью понимают эту парадигму. Давайте проясним несколько распространенных заблуждений. Узнайте больше здесь.

Monorepo === Monolith - Nx позволяет нам развертывать только затронутые приложения. Мы по-прежнему можем создавать и тестировать только поврежденные части. Нам не нужно развертывать все сразу.

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

Не масштабируется - когда вы достигнете 100 тыс. файлов или больше, вам, вероятно, следует перейти на другие системы контроля версий, такие как Виртуальная файловая система для Git.

Сборки выполняются медленнее, каждое изменение потребует сборки всего в репозитории - Nx обрабатывает это, предоставляя сценарии build / test / e2e затрагиваемые. Также вы можете воспользоваться Nx Cloud или локальным кешированием вычислений.

Принято решение

Принимая во внимание перечисленные выше требования, а также прогнозируя рост набора продуктов в будущем, я решил пойти по пути монорепорации. Я не хотел брать на себя бремя поддержки частного реестра NPM и управления версиями модулей в нескольких приложениях. А во-вторых, мне кажется, что monorepo - отличный выбор для быстроразвивающихся стартапов. Конечно, без Nx Workspace мое решение могло бы быть другим, потому что оно предоставляет инструменты, устраняющие некоторые недостатки обычного монорепозитория.

Прошла всего пара месяцев, но сейчас чувствуешь себя лучше, и так процесс разработки стал намного более плавным. В процессе перехода на monorepo я также улучшил наш рабочий процесс CI / CD, следите за обновлениями, и мы поделимся некоторыми отличными советами в следующем посте.

P.S .: Если у вас есть собственная история о применении подхода с несколькими репо и / или монорепозиториями, поделитесь ею в разделе комментариев ниже.

Ресурсы:

Создание библиотек - https://angular.io/guide/creating-libraries
Ссылка NPM - https://docs.npmjs.com/cli/link
Rush - https://rushjs.io/
Лерна - https://lerna.js.org/
Начало работы с Nx -
https://nx.dev/latest / angular / Getting-started / Getting-started
Владелец кода на Github - https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about- владельцы кода
Git в масштабе предприятия -
https://github.com/microsoft/VFSForGit
Заблуждения о Monorepos - https: //blog.nrwl. io / misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c