При использовании NX одним из основных принципов структурирования кода является использование библиотек. Библиотеки отлично подходят для повторного использования кода между вашими приложениями или даже другими библиотеками. Тем не менее, один спорный момент, который я видел, — это вопрос о том, что определяет библиотеку. Например, сколько компонентов входит в него? Или что, если он не используется несколькими приложениями?

Документация NX также довольно расплывчата по этому поводу. Они упоминают, что библиотеки должны быть небольшими, но что это значит? Один или два компонента? И почему нас в первую очередь волнует размер?

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

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

Давайте посмотрим, почему большие библиотеки в NX могут негативно повлиять на ваши сборки и почему большее количество библиотек — это хорошо.

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

apps/
  demo/src/app
libs/
  /cats
    /cat-pic
  /core
    /home
    /shell
  /dogs
    /dog-pic
  /shared
    /button
    /cat-service
    /dog-service

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

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

Следующая проблема связана с просмотром затронутых библиотек на основе изменений. Если мы внесем те же изменения в службу cat в общей библиотеке, запуск затронутой команды NX покажет нам это.

Вполне логично, что это повлияло на Cats-Cat-Pic, потому что оно использует общий сервис Cat, но поскольку Shared — это одна библиотека, любые другие библиотеки, которые зависят от чего-либо в Shared, также будут затронуты. Из-за этого изменения компоненты собаки также затронуты и будут перестроены, даже если они не претерпели изменений.

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

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

Давайте посмотрим, как это будет выглядеть, если мы разделим общую библиотеку на несколько библиотек.

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

Теперь, если мы внесем изменения в службу cat и запустим затронутую команду NX, мы увидим это

Теперь компонент собаки не указан, поскольку он не зависел от службы кошек. Кроме того, вы можете видеть, что был затронут не весь общий код, а только общий сервис cat. И это стало бы еще лучше с облаком NX и кэшированными сборками. Если в оболочку или компонент cat pic не было внесено никаких изменений, единственная библиотека, которую необходимо перестроить, — это служба cat, а все остальное повторно использует кэшированные сборки.

Я (очень) кратко упомянул границы области выше и должен указать, что они играют большую роль в структуре библиотеки для репозиториев NX. Придерживаясь границ области и типа библиотеки, вы можете запретить некоторым библиотекам импортировать код из других областей или типов библиотек. Например, библиотека, ориентированная на службу для выполнения вызовов API, может быть типом доступа к данным. Он не должен иметь возможности импортировать какие-либо библиотеки типов пользовательского интерфейса.

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

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

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

Уменьшение размеров библиотек абсолютно помогает вопреки убеждению, что чем больше библиотек, тем медленнее. Имея возможность разбивать библиотеки на более мелкие и более конкретные блоки, вы можете предотвратить ненужные сборки/тесты/линтинги.

Я надеюсь, что это поможет всем, кто борется с NX!

Вот ссылка на пример проекта, использованный выше.