Arquitetura orientada a eventos em TypeScript: quando o código "ouve" uns aos outros
\n\nImagine que você está escrevendo uma aplicação web complexa: um mensageiro, um robô de negociação ou um sistema de monitoramento de servidores. Em algum momento, você percebe: simplesmente chamar funções em cadeia é um inferno. Um módulo puxa outro, o terceiro depende do quarto e, ao alterar a lógica, tudo desmorona como um castelo de cartas.
\n\nFamiliar? Então você precisa da arquitetura orientada a eventos em TypeScript. Não é apenas um termo da moda, mas uma maneira real de tornar o código flexível, extensível e compreensível. Neste artigo, vamos explorar como a programação orientada a eventos funciona, escrever nosso próprio barramento de eventos em TypeScript e ver onde isso realmente ajuda.
\n\nO que é arquitetura orientada a eventos em termos simples
\n\nArquitetura orientada a eventos (Event-driven architecture) é uma abordagem onde os componentes do sistema se comunicam através de eventos. Um módulo gera um evento (por exemplo, "usuário registrado"), e outros se inscrevem nele e reagem. Eles não estão diretamente ligados entre si.
\n\nParece abstrato? Vamos a um exemplo da vida real. Imagine um escritório:
\n- \n
- Abordagem tradicional: O chefe vai pessoalmente a cada funcionário e diz: "Faça o relatório". \n
- Orientada a eventos: O chefe grita: "Preciso de um relatório!" — e todos que sabem fazê-lo começam a trabalhar. \n
No segundo caso, o chefe não precisa saber quem exatamente fará o relatório. Ele apenas envia o evento. E os funcionários podem se conectar e desconectar sem alterar a lógica do chefe. Essa é a essência da programação orientada a eventos.
\n\nPor que TypeScript é ideal para arquitetura de eventos
\n\nO JavaScript por si só é orientado a eventos (lembre-se do addEventListener), mas em projetos grandes, sem tipos, começa o caos. Eventos TypeScript resolvem esse problema: você sabe exatamente quais dados chegam no evento, e o compilador capturará o erro em tempo de desenvolvimento, não em tempo de execução.
Além disso, TypeScript suporta genéricos, sobrecargas e tipagem estrita de callbacks — tudo o que é necessário para criar um barramento de eventos confiável.
\n\nConstruindo um Event Bus em TypeScript do zero
\n\nVamos começar com um exemplo simples, mas funcional. Criaremos a classe EventBus — o barramento central de eventos através do qual todos os módulos se comunicarão.
// event-bus.ts\ntype EventHandler<T = any> = (data: T) => void;\n\nclass EventBus {\n private handlers: Map<string, EventHandler[]> = new Map();\n\n // Inscrição em um evento\n on<T>(event: string, handler: EventHandler<T>): void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, []);\n }\n this.handlers.get(event)!.push(handler as EventHandler);\n }\n\n // Cancelamento de inscrição\n off(event: string, handler: EventHandler): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n this.handlers.set(event, handlers.filter(h => h !== handler));\n }\n }\n\n // Emissão de evento\n emit<T>(event: string, data: T): void {\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach(handler => handler(data));\n }\n }\n}\n\nexport const globalEventBus = new EventBus();\n\nPronto, escrevemos uma arquitetura orientada a eventos em TypeScript simples. Agora, qualquer módulo pode importar o globalEventBus e ouvir ou gerar eventos.
Exemplo de uso: notificações e registro
\n\nImagine que temos um módulo de autenticação e um módulo de notificações. Eles não devem saber diretamente um do outro.
\n\n// auth-module.ts\nimport { globalEventBus } from './event-bus';\n\nexport function loginUser(email: string): void {\n // ... lógica de login\n console.log(`Usu