Как организовать архитектуру приложения на 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-интеграции.