Важным аспектом любой системы, определяющим будущую лёгкость обслуживания и чистоту кода, является ее модульная структура, при чем независимо от размера этой системы. В этой статье мы рассмотрим роль модулей core
и shared
в приложениях Angular, ответим на основные вопросы, касающиеся их использования, и сформулируем основные правила, которыми вы можете руководствоваться при разработках в Angular.
Angular весьма категоричен в отношении своей модульной структуры, по крайней мере это точно относится к конфигурации генераторов кода по умолчанию. В руководстве по стилю Angular создатели фреймворка прописали его философию и основные положения разработки одностраничных приложений.
Итак, у нас есть три причины придерживаться принципов данного руководства. И вот почему:
Модуль core
должен содержать код, во-первых, конкретный для вашего приложения, а во-вторых, выполняющий сквозные задачи приложений. Другими словами, это “система водоснабжения, электричество и отопление” вашего дома, если сравнивать создание приложения со строительством. Такой код может быть:
HttpClient
. Данный метод часто используется для уменьшения воздействия изменений интерфейса Angular в будущем;В заключении раздела отметим один важный момент. Модуль Core
следует импортировать только в корневой модуль Angular c именем AppModule
или любым другим.
Напомню еще раз: “модуль Core
следует импортировать только в корневой модуль Angular”.
Простой способ реализации этого правила — добавить так называемый Import Guard в код вашего класса CoreModule
. Ниже приведен образец реализации повторно используемого класса Import Guard:
/**
* Этот абстрактный класс используется для создания модуля через расширение этого класса;
* Препятствует импорту модуля в какое-либо другое место,кроме корневого модуля приложения.
*/
export abstract class EnsureImportedOnceModule {
protected constructor(targetModule: any) {
if (targetModule) {
throw new Error(`${targetModule.constructor.name} has already been loaded.`);
}
}
}
Данный код объявляет класс, инкапсулирующий реализацию для выбрасывания исключений среды выполнения, в том случае, если модуль импортируется не в корневой модуль приложения. Ниже приводится простой пример его использования:
@NgModule({
declarations: [...],
imports: [...],
exports: [...],
entryComponents: [...],
providers: [...]
})
export class CoreModule extends EnsureImportedOnceModule {
public constructor(@SkipSelf() @Optional() parent: CoreModule) {
super(parent);
}
}
Аннотация @SkipSelf
гарантирует, что родительский модуль модуля, использующего CoreModule
, внедряется Angular DI в конструктор CoreModule
, в то время как аннотация@Optional
замалчивает ошибки и вынуждает Angular DI предоставлять null
в случае, если модуль недоступен, (т.е. модуль, в который был импортирован CoreModule
, находится на вершине дерева иерархии модулей Angular). Все это означает, что:
parent
, не является null
;Это лишь один из многих способов реализации Import Guard. Что же касается других способов, то они по сути своей похожи.
Ваш модуль shared
должен содержать код, который можно было бы использовать повторно во всём приложении, а именно в компонентах, директивах, конвейерах. Этот процесс, опять таки, если проводить аналогию со строительством, подобен меблировке вашего дома, так как мебель не только можно использовать где угодно, но и передвигать внутри дома.
Модуль shared
, в отличие от core
, можно многократно импортировать в разные модули внутри приложения.
Учитывая, что код внутри модуля shared
можно использовать повторно множество раз, а в будущем нельзя исключать возможности его применения и в абсолютно разнородных приложениях, то целесообразно подключать к нему больше дочерних модулей. При этом старайтесь сохранять небольшой размер модулей и наделять их индивидуальными задачами, например, модуль, ответственный за формы, другой — за конверсию валюты, третий — за форматирование чисел и т.д. В перспективе при такой модульной структуре, извлечение модуля shared
в отдельную библиотеку станет делом пары минут.
Именно в связи с компонентами возникает камень преткновения во всем процессе выбора между модулями core
и shared
. Конечно же, в целом компоненты по своей природе предназначены для повторного использования, но будут ли они использованы повторно на самом деле? Задумайтесь над этим вопросом. Навигационные панели, футеры (нижние блоки сайта), макеты этих панелей и футеров, боковые меню и т.д., вероятнее всего, не будут применяться повторно, хотя все они являются компонентами.
Любой относящийся к приложению код, не имеющий внутрипроектных зависимостей, находящийся вне области видимости модуля Feature и являющийся компонентом, директивой или конвейером, должен в конечном итоге оказаться в модуле core
.
Следует уточнить, что внутрипроектные зависимости — это зависимости, которые находятся в том же проекте (приложении), но в другом модуле. Условие отсутствия внутрипроектных зависимостей необходимо для того, чтобы избавить вас от объявления циклических зависимостей между вашими модулями.
В каком же направлении двигаться — к модулю Feature или Shared?Если вы задаетесь этим вопросом, то следует принять во внимание следующие моменты:
Прежде всего, если вы работаете в гибкой интегрированной среде разработок, предпочтительнее создавать систему с учетом текущих функциональных требований: не планировать заранее, а корректировать модульную структуру вашей системы в соответствии с текущими системными требованиями и обязательно в перспективе проводить непрерывный рефакторинг.
Работая в негибкой среде, вам определенно следует прогнозировать возможные варианты изменения вашей системы. Например, учесть технические нюансы: кто и как будет интегрировать, какие модули будут интегрированы, стоит ли предоставить библиотеку или виджет с поддержкой ifame и т. д. Не забудьте и про функциональные нюансы, спросите себя: “Как изменится мое видение относительно применения моего ПО в ближайшем будущем?”. Если сейчас вам трудно ответить на все эти вопросы, то всегда можно вернуться к ним на последнем этапе проектирования ПО.
Помните, если ваша модульная структура будет отклонена на стадии рассмотрения по каким-либо причинам, то лучше всего будет обсудить этот вопрос с вашей командой.
Итак, вы познакомились с несколькими полезными советами о размещении кода в модулях core
/ shared
приложения Angular. На практике ваши решения о размещении кода в том или ином модуле системы могут быть неоднозначными.
Однако ваши решения должны быть понятны всей команде и соответствовать общему видению модульной структуры вашего приложения. Ведь, что может быть важнее командной работы?
В заключении, как и было обещано в начале статьи, представляю вам основные правила при выборе модуля:
core
.core
. shared
. Перевод статьи Radek Busa: “Where Shall I Put That?” — Core vs Shared Module in Angular”
Комментарии