Aprende a implementar el Facade Pattern en Angular para simplificar tu código

implementacion-facade-pattern-angular

Como implementar Facade Pattern en Angular

¿Cansado de componentes que parecen controlar todo el backend desde la plantilla? Aprender como implementar Facade Pattern en Angular es la forma más práctica de devolverles una sola responsabilidad: presentar datos y disparar eventos. Aquí no hay dogma: solo reglas que evitan que tu código se convierta en un monstruo difícil de cambiar.

Resumen rápido (lectores con prisa)

Facade es una capa intermedia entre la vista y la complejidad del estado y servicios.

Cuando usarlo: para aislar la UI del ruido de RxJS/Store y facilitar cambios futuros.

Por qué importa: centraliza orquestación y simplifica tests de componentes.

Cómo funciona: la fachada expone una API semántica (Signals/Observables y métodos) que la UI consume.

Tiempo estimado de lectura: 4 min

Ideas clave

  • Facade: capa entre vista y complejidad (Stores, servicios HTTP, websockets).
  • Permite aislar componentes de la API del estado y de RxJS; facilita el cambio de implementación.
  • Centraliza orquestación y simplifica tests: mockeas una sola clase.
  • No sustituye al Store; la fachada orquesta y adapta, el Store sigue siendo el motor.
  • Considera exponer Signals para simplificar la UI (toSignal).

Tabla de contenidos

Como implementar Facade Pattern en Angular: qué y por qué

El Facade es una capa intermedia entre la vista y la complejidad (Stores, servicios HTTP, websockets, etc.). En Angular sirve para:

  • Aislar componentes de la API del estado (NgRx, Akita) y de RxJS.
  • Centralizar orquestación (llamar varios servicios, combinar selectores).
  • Simplificar tests: mockeas una sola clase en lugar de todo el Store.

No confundas Facade con reemplazo de NgRx. El Store sigue siendo tu motor; la fachada es el volante que usa el conductor. Documentación útil: NgRx Store. También verás utilidad en convertir selectores a Signals: toSignal.

Implementación paso a paso (ejemplo práctico)

Escenario: lista de usuarios, indicador de carga y refrescar.

1) Capa de estado (puede ser NgRx o un servicio con BehaviorSubject)

@Injectable({ providedIn: 'root' })
export class UserApi {
  getUsers(): Observable { return this.http.get('/api/users'); }
}

2) Facade (el contrato que consume la UI)

// user.facade.ts
import { Injectable, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { loadUsers } from './state/user.actions';
import { selectAllUsers, selectLoading } from './state/user.selectors';
import { toSignal } from '@angular/core/rxjs-interop';

@Injectable({ providedIn: 'root' })
export class UserFacade {
  private store = inject(Store);

  // Exponer Signals hace la UI más simple
  users = toSignal(this.store.select(selectAllUsers), { initialValue: [] });
  loading = toSignal(this.store.select(selectLoading), { initialValue: false });

  load(): void {
    this.store.dispatch(loadUsers());
  }

  refresh(): void {
    // posibles operaciones compuestas: cancelar, volver a cargar, métricas, etc.
    this.load();
  }
}

Usa métodos semánticos (load, refresh, selectUser) y nunca expongas acciones crudas desde el componente.

3) Componente (ligero)

@Component({ /* ... */ })
export class UserListComponent implements OnInit {
  facade = inject(UserFacade);

  ngOnInit() { this.facade.load(); }

  onRefresh() { this.facade.refresh(); }
}

La plantilla puede leer facade.users() y facade.loading() — sin pipes async, sin selectores en el componente.

Alternativa sin NgRx: BehaviorSubject interno en la fachada

Si no usas NgRx, la fachada puede orquestar servicios y exponer Observables/Signals:

private users$ = new BehaviorSubject([]);
get users() { return this.users$.asObservable(); }

load() {
  this.userApi.getUsers().subscribe(this.users$);
}

Esto mantiene la UI igual y te permite cambiar la implementación sin tocar componentes.

Testing: por qué es más fácil

En tests unitarios del componente mockeas UserFacade:

const mockFacade = { users: of([{id:1,name:'A'}]), loading: of(false), load: jest.fn() };
TestBed.overrideProvider(UserFacade, { useValue: mockFacade });

Resultado: tests más rápidos y menos acoplamiento a detalles de RxJS o del Store.

Buenas prácticas y anti-patrones

  • Fachadas por dominio, no un AppFacade gigante. Preferible: AuthFacade, CartFacade, UserFacade.
  • No mantengas estado duplicado en la fachada. Si ya existe un Store, la fachada delega, no re-implementa.
  • Expone API semántica, no selectores ni acciones. El componente debe leer “usuarios” y llamar “load()”, no importar selectores.
  • Considera Signals en la fachada para simplificar la UI: toSignal(…) en lugar del async pipe.
  • Evita lógica de negocio pesada en la fachada; su foco es orquestación y adaptación.

Criterio para decidir si usar Facade

Usa Facade cuando:

  • Tienes un equipo mixto (UI vs backend) y quieres proteger al equipo de UI del ruido de RxJS.
  • Prevés cambios futuros en la solución de estado.
  • Quieres tests de componente simples y rápidos.

No lo uses cuando:

  • El proyecto es un MVP pequeño con un desarrollador y flujo de trabajo rápido.
  • La fachada añade más ficheros que valor real.

Recursos y lecturas

FAQ

Respuesta:

El Facade Pattern en Angular es una capa intermedia que aísla la UI de la complejidad del estado y servicios (Stores, HTTP, websockets). Proporciona una API semántica que la vista consume.

Respuesta:

Úsala cuando quieras proteger al equipo de UI del ruido de RxJS, prever cambios en la solución de estado o simplificar tests. Evita fachadas si el proyecto es un MVP pequeño con un solo desarrollador.

Respuesta:

No. La fachada no reemplaza al Store: el Store sigue siendo el motor. La fachada actúa como adaptador o volante que orquesta y simplifica el acceso al estado.

Respuesta:

Debe exponer una API semántica: Observables o Signals (por ejemplo mediante toSignal) y métodos como load(), refresh(), selectUser(). No expongas selectores ni acciones crudas directamente.

Respuesta:

En tests de componentes mockeas la fachada completa en lugar del Store, reduciendo el acoplamiento a RxJS y al Store. Eso hace los tests más rápidos y simples.

Respuesta:

Anti-patrones: crear un AppFacade monolítico por dominio, duplicar estado en la fachada cuando ya existe un Store, y poner lógica de negocio compleja dentro de la fachada en lugar de orquestación y adaptación.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *