Пример организации кода для сложного Angular проекта

Официальная документация Angular неплохо описывает, а angular-cli автоматически создает структуру относительно простого проекта. Но по мере его развития сложность неизбежно растет и возникает естественная необходимость как-то этой сложностью управлять. В том числе и за счет декомпозиции.


8aknc2hxngej-9gxclaus2-9wec.png


(Изображение взято из статьи »12 Things to Help Large Organizations Do Angular Right» )


Данная публикация есть практическое осмысление статей »12 Things to Help Large Organizations Do Angular Right» (Victor Savkin, Co-founder of Narwhal Technologies (nrwl.io) и «Angular: Understanding Modules and Services» (Michele Stieven, Web Developer & JS enthusiast) через призму собственного опыта работы с фреймворком.


Задача


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


  • Моно-репозитарий — весь исходный код в одном репозитарии с единственной пакой node_modules на все веб-приложения и библиотеки.
  • Совместимость с angular-cli без дополнительных надстроек и расширений
  • Возможность независимо друг от друга компилировать библиотеки и собирать веб-приложения.


Пример приложения


Для примера, создадим рабочий прототип пиложения hero-app с зависимой библиотекой common-lib. Для примера двух компонентов достаточно, но на практике их количество может быть любым в разумных пределах.


Организация кода


Судя по всему, базовая концепция angular-cli заключается в создании именно рабочего пространства (workspace) для семейства проектов, а не рабочего каталога для одного единственного. Во всяком случае, свойство apps в виде массива в .angular-cli.json на это ненавязчиво указывает. Этой возможностью и воспользуемся.


Создадим при помощи стандартного ng new рабочий каталог, а затем немного модифицируем его, поместив приложение и библиотеку в отдельную папку в src — собственно, она и будет корневой папкой для всех остальных «строительных блоков» нашего решения.


gaotoy1wehj5jxrd-rujiuk4ckm.jpeg


Затем модифицируем .angular-cli.json так, чтоб он адекватно воспринимал наши изменения в структуре проекта:


wftctylsyegvjzehvj5hhvi7tkk.jpeg


(Примечание: Изначально казалось, что там все просто, но были (и есть) небольшие сюрпризы с относительными путями и тем, как angular-cli их обрабатывает)


Код библиотек хотелось держать как можно чище, но, поскольку для нормальной работы angular-cli требует наличия конкретных значений в .angular-cli.json, то все, отчасти ненужные, а где-то и просто общие на всех файлы, — поместим в папку src/_common.


Импорт библиотек


Использование общей корневой папки src для всего кода делает технически простой задачу импорта нужных файлов, но код вида:


import {FooBarComponent} from '../../../../../foo-bar.component';


не только эстетически выглядит ужасно, но и создает проблемы, если мы решим позднее наши библиотеки опубликовать в виде независимых npm-пакетов.
Благо, возможность конфигурации tsconfig.json элегантно решает эту проблему.


l2lnrlhw1yzzpnoxysv6fvhjz-8.jpeg


Результат


vd5v9wtjru59npysw1hgwyxxtia.jpeg


Теперь с каждым компоненом можно работать независимо, выполняя декомпозицию решения, а общая кодовая база позволяет собирать «из кирпичиков» нужные модули и приложения.


Важный акцент: описан не способ создания «настоящих» библиотек для Angular (это отдельная история вроде вот этой), а приведен просто способ организации кода для достаточно сложного проекта (для решения этой же проблемы есть и другие более профессиональные инструменты).


Послесловие


Чтоб не отвлекаться от основной темы статьи, за скобками повествования остались еще ряд моментов вроде:


  • Подключение сторонних библиотек и стилей и вынос настроек подключения в общий код
  • Использование глобальных стилей для стилизации вложенных компонентов
  • Подключение tslib (TypeScript helpers) для уменьшения размеров бандла
  • Приемы организации приложения: AppRootModule, «Features»-module, структура папок (использование index.ts c реэкспортом)
  • Особенности импорта модулей между корневым и дочерними lazy-loaded модулями (использование Module.forRoot)


Но в той или иной форме вы сможете найти примеры их решения коде проекта на GitHub.

© Habrahabr.ru