El patrón publicador-suscriptor (pub/sub) es ampliamente utilizado en el desarrollo de software, especialmente para comunicación entre componentes, sistemas de micro-frontends y comunicación entre aplicaciones secundarias.
A continuación se presenta una implementación simplificada de este patrón:
- Susripción
- Restricción de tipos durante la inicialización
- Publicación
La restricción de tipos permite que los suscriptores y publicadores conozcan de antemano qué tipos de eventos están disponibles, eivtando el uso de tipos desconocidos por alguna de las partes.
type Manejador<T> = (dato?: T) => void;
export default class EventBus<T> {
// Almacén de suscriptores por tipo de evento
public manejadores: Record<string, Manejador<T>[]> = {};
// Tipos de eventos permitidos
public tiposPermitidos: string[] | undefined = undefined;
constructor(tipos?: string[]) {
this.tiposPermitidos = tipos;
}
// Registrar un suscriptor
agregarSuscriptor(tipo: string, manejador: Manejador<T>) {
const tipos = this.tiposPermitidos;
if (tipos && !tipos.includes(tipo)) {
console.warn(`No se pudo crear el suscriptor para "${tipo}". Los tipos permitidos son: ${tipos.join(', ')}`);
return;
}
if (typeof manejador !== 'function') {
console.warn('Error al crear suscriptor: el segundo parámetro debe ser una función');
return;
}
if (!this.manejadores[tipo]) {
this.manejadores[tipo] = [];
}
this.manejadores[tipo].push(manejador);
}
// Suscribirse solo una vez, se elimina automáticamente después de ejecutarse
agregarSuscriptorUnico(tipo: string, manejador: Manejador<T>) {
if (typeof manejador !== 'function') {
console.warn('Error al crear suscriptor: el segundo parámetro debe ser una función');
return;
}
constenvoltorio = (dato?: T) => {
manejador(dato);
this.eliminarSuscriptor(tipo,envoltorio);
};
this.agregarSuscriptor(tipo,envoltorio);
}
// Eliminar un suscriptor específico
eliminarSuscriptor(tipo: string, manejador: Manejador<T>) {
if (this.manejadores[tipo]) {
this.manejadores[tipo] = this.manejadores[tipo].filter(fn => fn !== manejador);
}
}
// Publicar un evento, ejecuta todas las funciones suscritas
notificar(tipo: string, dato?: T) {
const tipos = this.tiposPermitidos;
if (tipos && !tipos.includes(tipo)) {
console.warn(`No se puede publicar "${tipo}". Los tipos permitidos son: ${tipos.join(', ')}`);
return;
}
if (this.manejadores[tipo]) {
this.manejadores[tipo].forEach(fn => {
fn(dato);
});
}
}
}
Ejemplo de uso
Inicializar y exportar una instancia del bus de eventos (en sistemas de micro-frontends, pasar esta instancia a las aplicaciones secundarias). Esto permite la comunicación entre diferentes partes de la aplicación.
// Inicialización
export const eventosMaestro = new EventBus(['MICRO_APP_MONTADO', 'MICRO_ACCION']);
// Suscripción
eventosMaestro?.agregarSuscriptor('MICRO_ACCION', (parametros) => {
// Ejecutar alguna acción
});
// Publicación
eventosMaestro.notificar('MICRO_ACCION', {
//...parametros
});
Este patrón proporciona un acoplamiento débil entre componentes, permitiendo que el publicador y el suscriptor operen de forma independiente sin conocerse directamente.