Servicios en Angular e Inyección de Dependencias
Un servicio es una clase que encapsula lógica de negocio o funcionalidad que no pertenece directamente a un componente, como:
- Manejo de datos o API REST
- Almacenamiento local
- Autenticación y autorización
- Compartir información entre componentes
- Control de estado de la aplicación
Angular fomenta el uso de servicios para separar las responsabilidades y aplicar el principio Single Responsibility del modelo SOLID.
¿Qué es la inyección de dependencias (DI)?
La Inyección de Dependencias (Dependency Injection) es un patrón de diseño que permite a una clase obtener las instancias que necesita sin crearlas directamente, promoviendo:
- Bajo acoplamiento
- Alta cohesión
- Facilidad de prueba
- Reutilización de código
En Angular, la DI se implementa automáticamente mediante el sistema de Inyector (Injector
), que administra la creación, provisión y ciclo de vida de las dependencias.
Estructura básica de un servicio
auth-service.ts
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' }) // Se registra automáticamente
export class AuthService {
private isLoggedIn = false;
login() {
this.isLoggedIn = true;
}
logout() {
this.isLoggedIn = false;
}
getStatus(): boolean {
return this.isLoggedIn;
}
}
@Injectable()
le indica a Angular que esta clase puede ser inyectada.providedIn: 'root'
significa que el servicio estará disponible en toda la aplicación (Singleton global).
Inyección en componentes
login-button.ts
import { Component, inject } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'login-button',
standalone: true,
template: `<button (click)="login()">Login</button>`
})
export class LoginButton {
private authService = inject(AuthService); // Angular 14+
login() {
this.authService.login();
}
}
Desde Angular 14+, es posible usar inject()
en lugar del constructor para inyectar servicios.
Tipos de proveedores en Angular
Tipo | Uso | Ejemplo |
---|---|---|
providedIn: 'root' | Global singleton | Recomendado para la mayoría de casos |
providedIn: 'any' | Instancia por módulo o componente | Útil en lazy loading |
providers: [] en @Component | Scoped a un componente específico | Aísla estado |
Ejemplo con productos
product-service.ts
@Injectable({ providedIn: 'root' })
export class ProductService {
private products = signal<string[]>([]);
addProduct(name: string) {
this.products.update(p => [...p, name]);
}
getAll() {
return this.products;
}
}
product-list.ts
@Component({
selector: 'product-list',
standalone: true,
template: `<ul><li *ngFor="let p of products()">{{ p }}</li></ul>`
})
export class ProductList {
private service = inject(ProductService);
readonly products = this.service.getAll();
}
Relación con la arquitectura
En Clean Architecture o arquitectura modular:
- Servicios representan la capa de aplicación o infraestructura.
- Se inyectan en controladores, componentes o casos de uso.
- Pueden actuar como adaptadores entre componentes y lógica de negocio.
Buenas prácticas
- Registra servicios con
providedIn: 'root'
por defecto - Evita lógica de negocio compleja en componentes
- Inyecta con
inject()
en lugar deconstructor()
para servicios sin dependencia circular - Crea servicios especializados por dominio (SRP)
- Aisla servicios en módulos si tienen responsabilidades locales
Referencias
- Angular Team. (2024). Dependency injection in Angular. Angular.dev.
- Angular Team. (2024). Injectable providers. Angular.dev.
- Brown, S. (2023). Clean Angular Architecture. Leanpub.
- Netanel Basal. (2023). Simplifying Angular DI with inject().
- Eckles, J. (2023). Mastering Angular Services. Packt Publishing.