Event Bus e padrão Observer em TypeScript: da teoria à prática
O desenvolvimento web moderno exige uma arquitetura flexível e escalável. Dois padrões-chave que ajudam a organizar a interação entre componentes são o Event Bus e o Observer. Neste artigo, analisaremos sua implementação em TypeScript, vantagens e cenários práticos de uso.
O que é o padrão Observer?
O padrão Observer (Observador) é um padrão de design comportamental que define uma dependência "um para muitos" entre objetos. Quando um objeto (publicador) altera seu estado, todos os objetos dependentes (assinantes) são automaticamente notificados e atualizados.
Elementos principais do padrão:
- Subject (Publicador) — objeto que monitora alterações e notifica os assinantes.
- Observer (Observador) — objeto que se inscreve nas alterações do publicador.
- Event Bus (Barramento de Eventos) — mecanismo centralizado que gerencia assinaturas e o envio de eventos entre componentes.
Implementação do Event Bus em TypeScript
TypeScript oferece excelentes recursos para tipagem estrita, tornando a implementação do Event Bus segura e previsível. Vamos considerar uma implementação básica:
type EventHandler<T = any> = (payload: T) => void;
class EventBus { private listeners: Map<string, EventHandler[]> = new Map();
// Inscrição em um evento subscribe<T>(event: string, handler: EventHandler<T>): void { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event)!.push(handler); }
// Cancelamento de inscrição unsubscribe<T>(event: string, handler: EventHandler<T>): void { const handlers = this.listeners.get(event); if (handlers) { this.listeners.set( event, handlers.filter(h => h !== handler) ); } }
// Publicação de evento emit<T>(event: string, payload: T): void { const handlers = this.listeners.get(event); if (handlers) { handlers.forEach(handler => handler(payload)); } }
// Limpeza de todas as inscrições clear(): void { this.listeners.clear(); }}
export const eventBus = new EventBus();Exemplo de uso com eventos tipados
Para aumentar a segurança de tipos, podemos definir interfaces de eventos:
// Definindo tipos de eventosinterface AppEvents { 'user:login': { userId: string; timestamp: number }; 'user:logout': { userId: string }; 'data:update': { id: string; data: unknown }; 'notification:show': { message: string; type: 'success' | 'error' | 'info' };}
// Criando um barramento tipadoclass TypedEventBus { private listeners: Map<string, Function[]> = new Map();
on<K extends keyof AppEvents>( event: K, handler: (payload: AppEvents[K]) => void ): void { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event)!.push(handler); }
emit<K extends keyof AppEvents>( event: K, payload: AppEvents[K] ): void { const handlers = this.listeners.get(event); if (handlers) { handlers.forEach(handler => handler(payload)); } }
off<K extends keyof AppEvents>( event: K, handler: (payload: AppEvents[K]) => void ): void { const handlers = this.listeners.get(event); if (handlers) { this.listeners.set( event, handlers.filter(h => h !== handler) ); } }}
export const typedBus = new TypedEventBus();Exemplo prático: sistema de notificações
Vamos considerar um cenário real — a criação de um sistema de notificações usando Event Bus: