Angular сервисы

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

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

Ключевые функции сервисов:

  • обработка бизнес-логики
  • выполнение HTTP-запросов
  • управление состоянием приложения
  • обмен данными между компонентами
  • интеграция с внешними API

Такое разделение позволяет избегать дублирования кода и упрощает тестирование.

Механизм внедрения зависимостей

Angular использует систему Dependency Injection (DI), которая позволяет автоматически создавать и предоставлять экземпляры сервисов там, где они необходимы. Сервисы регистрируются в инжекторах, а компоненты получают их через конструктор.

Основные преимущества DI:

  • снижение связности кода
  • удобство замены реализаций
  • упрощение модульного тестирования
  • контроль жизненного цикла объектов

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

Создание сервиса

Сервис в Angular представляет собой класс с декоратором, который определяет его поведение в системе внедрения зависимостей.

Простейшая структура:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private data: string[] = [];

  addItem(item: string): void {
    this.data.push(item);
  }

  getItems(): string[] {
    return this.data;
  }
}

Ключевой параметр providedIn: 'root' делает сервис синглтоном на уровне всего приложения.

Области предоставления сервисов

Сервисы могут быть зарегистрированы в разных уровнях приложения:

Глобальный уровень

Сервис создается один раз и доступен во всем приложении. Используется для хранения общего состояния и работы с API.

Уровень модуля

Сервис создается отдельно для каждого модуля. Это позволяет изолировать функциональность.

Уровень компонента

Каждый компонент получает собственный экземпляр сервиса. Это полезно при необходимости локального состояния.

Такой механизм обеспечивает гибкость управления состоянием и зависимостями.

Жизненный цикл сервисов

Сервис создается при первом запросе к нему через DI и уничтожается вместе с соответствующим инжектором. Если сервис зарегистрирован на уровне root, он живет на протяжении всего жизненного цикла приложения.

Важно учитывать:

  • singleton-сервисы сохраняют состояние между компонентами
  • локальные сервисы пересоздаются при инициализации компонента
  • уничтожение сервиса связано с уничтожением области видимости инжектора

Работа с состоянием

Сервисы часто используются как централизованное хранилище состояния. Это особенно актуально в приложениях средней и высокой сложности.

Пример хранения состояния:

@Injectable({
  providedIn: 'root'
})
export class StateService {
  private state = {
    user: null,
    isAuthenticated: false
  };

  setUser(user: any): void {
    this.state.user = user;
    this.state.isAuthenticated = true;
  }

  clear(): void {
    this.state.user = null;
    this.state.isAuthenticated = false;
  }

  getState() {
    return this.state;
  }
}

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

Интеграция с HTTP

Сервисы часто выступают как слой взаимодействия с сервером. Обычно используется HttpClient, который инкапсулируется внутри сервиса.

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  constructor(private http: HttpClient) {}

  getUsers(): Observable<any> {
    return this.http.get('/api/users');
  }

  createUser(data: any): Observable<any> {
    return this.http.post('/api/users', data);
  }
}

Такой подход отделяет сетевую логику от компонентов и упрощает поддержку кода.

Реактивный подход и RxJS

Сервисы часто используют реактивные потоки для управления данными. Это позволяет строить асинхронные цепочки обработки событий.

Основные элементы:

  • Observable — поток данных
  • Subject — источник событий
  • BehaviorSubject — поток с начальным значением

Пример:

import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private loggedIn = new BehaviorSubject<boolean>(false);

  isLoggedIn$ = this.loggedIn.asObservable();

  login(): void {
    this.loggedIn.next(true);
  }

  logout(): void {
    this.loggedIn.next(false);
  }
}

Реактивная модель позволяет компонентам подписываться на изменения состояния без прямой зависимости от источника данных.

Взаимодействие между сервисами

Сервисы могут зависеть друг от друга, формируя цепочки бизнес-логики. Angular DI автоматически разрешает такие зависимости.

Пример:

@Injectable({
  providedIn: 'root'
})
export class LoggerService {
  log(message: string): void {
    console.log(message);
  }
}

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private logger: LoggerService) {}

  createUser(name: string): void {
    this.logger.log(`User created: ${name}`);
  }
}

Такой подход способствует модульности и повторному использованию кода.

Тестирование сервисов

Сервисы являются одной из самых удобных частей Angular для модульного тестирования, так как не зависят от DOM.

Пример теста:

import { TestBed } from '@angular/core/testing';

describe('DataService', () => {
  let service: DataService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(DataService);
  });

  it('should add item', () => {
    service.addItem('test');
    expect(service.getItems()).toContain('test');
  });
});

DI облегчает подмену зависимостей и создание моков.

Типичные ошибки при работе с сервисами

Часто встречающиеся проблемы:

  • хранение UI-логики в сервисах
  • чрезмерное использование singleton-сервисов
  • отсутствие разделения ответственности
  • прямое изменение состояния без методов-оберток
  • игнорирование отписки от Observable

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

Организация структуры сервисов

В крупных приложениях сервисы группируются по функциональным областям:

  • auth
  • users
  • products
  • shared utilities

Такой подход облегчает масштабирование и сопровождение кода.

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