Event Bus e padrão Observer em Go (Golang)
No mundo do desenvolvimento moderno em Go (Golang), surge frequentemente a necessidade de organizar uma interação fracamente acoplada entre os componentes da aplicação. Duas abordagens clássicas são o padrão Observer e sua implementação mais avançada — o Event Bus (barramento de eventos). Esses mecanismos permitem que partes do programa reajam às ações de outras, sem criar dependências rígidas.
Neste artigo, vamos analisar como implementar um Event Bus em Go puro, sem dependências externas, examinar o padrão Observer e mostrar exemplos de código para processamento síncrono e assíncrono de eventos.
O que é o padrão Observer?
O padrão Observer (Observador) é um padrão de design comportamental que cria um mecanismo de inscrição, permitindo que objetos acompanhem e reajam a eventos que ocorrem em outros objetos.
Elementos principais:
- Subject (Sujeito) — objeto que gera eventos.
- Observer (Observador) — objeto que se inscreve nos eventos.
- Event (Evento) — dados transmitidos do sujeito aos observadores.
O que é Event Bus?
Event Bus é um barramento centralizado que gerencia a transmissão de eventos entre publicadores (publishers) e assinantes (subscribers). Diferente do Observer clássico, onde o sujeito armazena diretamente a lista de observadores, o Event Bus atua como intermediário. Isso torna o sistema ainda mais flexível e escalável.
Implementação de Event Bus em Go
Go não possui uma implementação nativa de Event Bus, mas pode ser facilmente criado usando goroutines, canais e interfaces. Vamos considerar uma implementação simples, porém completa.
1. Definindo tipos básicos
// Event representa a estrutura do eventotype Event struct { Name string Data interface{}}
// EventHandler — função manipuladora de eventostype EventHandler func(Event)2. Criando o Event Bus
type EventBus struct { handlers map[string][]EventHandler mu sync.RWMutex}
// NewEventBus cria um novo barramento de eventosfunc NewEventBus() *EventBus { return &EventBus{ handlers: make(map[string][]EventHandler), }}
// Subscribe inscreve um manipulador em um eventofunc (eb *EventBus) Subscribe(name string, handler EventHandler) { eb.mu.Lock() defer eb.mu.Unlock() eb.handlers[name] = append(eb.handlers[name], handler)}
// Publish publica um evento de forma síncronafunc (eb *EventBus) Publish(name string, data interface{}) { eb.mu.RLock() defer eb.mu.RUnlock() if handlers, ok := eb.handlers[name]; ok { event := Event{Name: name, Data: data} for _, handler := range handlers { handler(event) } }}3. Publicação assíncrona
Para processamento assíncrono, podemos executar cada manipulador em uma goroutine separada:
// PublishAsync publica um evento de forma assíncronafunc (eb *EventBus) PublishAsync(name string, data interface{}) { eb.mu.RLock() defer eb.mu.RUnlock() if handlers, ok := eb.handlers[name]; ok { event := Event{Name: name, Data: data} for _, handler := range handlers { go handler(event) } }}Exemplo de uso
Vamos ver como usar nosso Event Bus em uma aplicação real. Suponha que temos um sistema que registra ações do usuário e envia notificações.
func main() { bus := NewEventBus()
// Inscrevendo-se no evento "user.login" bus.Subscribe("user.login", func(e Event) { userData := e.Data.(map[string]string) fmt.Printf("Log: Usuário %s fez login", userData["username"]) })
// Inscrevendo-se no mesmo evento para en Também na biblioteca
Faker para PHP: guia completo com exemplos
Tokens JWT em JavaScript (Node.js): guia completo
Monitoramento de aplicações C# (.NET) com AppMetrics
Trabalhando com S3 e armazenamentos em nuvem em TypeScript: bibliotecas e exemplos
Parsing de HTML em Go: visão geral das bibliotecas e exemplos de código