Как организовать архитектуру приложения на Angular?
Организация архитектуры Angular-приложения должна обеспечивать масштабируемость, читаемость, переиспользуемость, тестируемость и простоту поддержки. Для этого нужно применять модульный подход, разделение ответственности, соблюдение архитектурных паттернов и стандартов проектирования.
1. Модульность
Каждая функциональная область приложения должна быть выделена в отдельный Angular-модуль:
-
AppModule – главный модуль, точка входа.
-
CoreModule – для сервисов, singleton-провайдеров и глобальных конфигураций.
-
SharedModule – для общих компонентов, директив, пайпов, которые переиспользуются.
-
Feature Modules – модули, реализующие конкретную бизнес-логику (например, UsersModule, ProductsModule).
Пример структуры:
src/
│
├── app/
│ ├── core/
│ │ ├── services/
│ │ └── guards/
│ │
│ ├── shared/
│ │ ├── components/
│ │ ├── directives/
│ │ └── pipes/
│ │
│ ├── features/
│ │ ├── dashboard/
│ │ │ ├── components/
│ │ │ ├── pages/
│ │ │ └── dashboard.module.ts
│ │ └── users/
│ │ ├── components/
│ │ ├── services/
│ │ ├── pages/
│ │ └── users.module.ts
│ │
│ ├── app.component.ts
│ ├── app.module.ts
│ └── app-routing.module.ts
2. Разделение слоёв
Presentation Layer (View Layer)
-
Компоненты отображают данные, обрабатывают события пользователя.
-
Минимальная логика, делегируют работу сервисам.
-
Использование container и presentational компонентов:
-
Container (умные): работают с сервисами, хранят состояние.
-
Presentational (глупые): только принимают @Input() и отдают @Output().
-
Service Layer
-
Хранение бизнес-логики.
-
Работа с API, state, кешем.
-
Все взаимодействия с backend и сторонними библиотеками инкапсулируются в сервисах.
State Layer
-
@ngrx/store, ComponentStore, SignalStore, BehaviorSubject или простые поля/объекты — в зависимости от сложности.
-
Изолированное хранение состояния.
-
Selectors, Actions, Effects для управления асинхронной логикой.
3. CoreModule
Используется для:
-
Глобальных сервисов с providedIn: 'root'.
-
Guard’ов, Interceptor’ов.
-
Конфигураций (например, глобальный HttpClient, токенов, интернационализация).
-
Подключается один раз — в AppModule.
@NgModule({
providers: \[
AuthService,
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
\]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) throw new Error('CoreModule должен быть импортирован только в AppModule');
}
}
4. SharedModule
Используется для:
-
Общих компонентов: ButtonComponent, CardComponent.
-
Пайпов: DateFormatPipe, CurrencyPipe.
-
Директив: IfAuthDirective, AutoFocusDirective.
SharedModule никогда не должен предоставлять сервисы. Он предназначен только для переиспользуемых деклараций.
5. Feature-модули
Каждая бизнес-сущность или страница имеет собственный модуль:
ng generate module features/users --route users --module app.module
-
Изолируют маршруты, компоненты, зависимости.
-
Позволяют подключать lazy loading.
-
Упрощают сопровождение и тестирование.
6. Ленивая загрузка (Lazy Loading)
Все крупные модули подключаются через loadChildren:
const routes: Routes = \[
{
path: 'users',
loadChildren: () => import('./features/users/users.module').then(m => m.UsersModule)
}
\];
Lazy loading:
-
Повышает производительность.
-
Делает бандлы меньше.
-
Разделяет зависимости.
7. Router структура
-
Основной роутинг – AppRoutingModule.
-
Каждый feature-модуль имеет собственный routing.module.ts с forChild.
-
Использование Guard’ов (canActivate, canDeactivate, canLoad).
8. State Management
Для небольших проектов:
-
Локальный @Input(), @Output().
-
Сервис с BehaviorSubject.
Для средних и крупных:
-
@ngrx/store + effects + selectors.
-
NgRx ComponentStore для локального состояния.
Важно использовать async пайпы, чтобы избежать подписок вручную.
9. Http и API взаимодействие
-
Все HTTP-запросы инкапсулируются в сервисах.
-
Использование HttpClient.
-
Общий ApiService может инкапсулировать базовые методы.
Пример:
@Injectable({ providedIn: 'root' })
export class UsersService {
constructor(private http: HttpClient) {}
getUser(id: string): Observable<User> {
return this.http.get<User>(\`/api/users/${id}\`);
}
}
10. Инфраструктура
Интерсепторы
-
AuthInterceptor — добавление JWT.
-
ErrorInterceptor — глобальная обработка ошибок.
-
LoadingInterceptor — индикация загрузки.
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
Guards
-
Защита маршрутов.
-
Авторизация/аутентификация.
-
Подтверждение выхода при изменениях.
11. Формы
-
Reactive Forms предпочтительны.
-
Форма — отдельный компонент (если сложная).
-
Использование FormBuilder и Validators.
12. Юнит- и e2e-тесты
-
Компоненты: TestBed, ComponentFixture.
-
Сервисы: изоляция зависимостей через mock.
-
E2E: Cypress или Playwright (альтернатива Protractor).
13. CI/CD и линтинг
-
ESLint/Prettier.
-
Husky + lint-staged.
-
Angular CLI с ng build --configuration production.
-
Unit/E2E тесты в pipeline.
14. Переводы и интернационализация
-
@ngx-translate/core или Angular i18n.
-
Отдельный сервис для локализации.
-
Хранение переводов в assets/i18n/*.json.
15. Типизация и модели
-
Интерфейсы: User, Post, Order.
-
DTO: входные/выходные типы от API.
-
Расположение: shared/models, features/users/models.
export interface User {
id: string;
name: string;
email: string;
}
16. Отображение и загрузка
-
SkeletonComponent или Shimmer при загрузке.
-
NgIf / NgSwitch для управления состоянием.
-
ErrorComponent, EmptyComponent — обёртки.
17. Обработка ошибок
-
Centralized HttpInterceptor.
-
Глобальный ErrorHandler.
-
Пользовательские Toasts или Snackbar.
Хорошо организованная архитектура Angular-проекта опирается на строгую модульность, изоляцию ответственности, ленивую загрузку, слоистую структуру и стандартизированную реализацию общих задач — от навигации до состояния и API-интеграции.