Event Bus y el patrón Observer en Go (Golang)
En el mundo del desarrollo moderno con Go (Golang), a menudo surge la necesidad de organizar una interacción débilmente acoplada entre los componentes de una aplicación. Dos enfoques clásicos son el patrón Observer y su implementación más avanzada: el Event Bus (bus de eventos). Estos mecanismos permiten que unas partes del programa reaccionen a las acciones de otras, sin crear dependencias rígidas.
En este artículo, analizaremos cómo implementar un Event Bus en Go puro sin dependencias externas, examinaremos el patrón Observer y mostraremos ejemplos de código para el procesamiento de eventos síncrono y asíncrono.
¿Qué es el patrón Observer?
El patrón Observer (Observador) es un patrón de diseño de comportamiento que crea un mecanismo de suscripción, permitiendo que unos objetos observen y reaccionen a eventos que ocurren en otros objetos.
Elementos principales:
- Subject (Sujeto) — el objeto que genera eventos.
- Observer (Observador) — el objeto que se suscribe a los eventos.
- Event (Evento) — los datos transmitidos del sujeto a los observadores.
¿Qué es un Event Bus?
Un Event Bus es un bus centralizado que gestiona la transmisión de eventos entre publicadores (publishers) y suscriptores (subscribers). A diferencia del Observer clásico, donde el sujeto almacena directamente la lista de observadores, el Event Bus actúa como intermediario. Esto hace que el sistema sea aún más flexible y escalable.
Implementación de un Event Bus en Go
Go no tiene una implementación incorporada de Event Bus, pero se puede escribir fácilmente usando gorutinas, canales e interfaces. Consideremos una implementación simple pero completa.
1. Definimos los tipos básicos
// Event representa la estructura de un eventotype Event struct { Name string Data interface{}}
// EventHandler — función manejadora de eventostype EventHandler func(Event)2. Creamos el Event Bus
type EventBus struct { handlers map[string][]EventHandler mu sync.RWMutex}
// NewEventBus crea un nuevo bus de eventosfunc NewEventBus() *EventBus { return &EventBus{ handlers: make(map[string][]EventHandler), }}
// Subscribe suscribe un manejador a un 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 un 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. Publicación asíncrona
Para el procesamiento asíncrono, podemos ejecutar cada manejador en una gorutina separada:
// PublishAsync publica un evento de forma así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) } }}Ejemplo de uso
Consideremos cómo usar nuestro Event Bus en una aplicación real. Supongamos que tenemos un sistema que registra las acciones del usuario y envía notificaciones.
func main() { bus := NewEventBus()
// Nos suscribimos al evento "user.login" bus.Subscribe("user.login", func(e Event) { userData := e.Data.(map[string]string) fmt.Printf("Log: El usuario %s inició sesión", userData["username"]) })
// Nos suscribimos al mismo evento para También en la biblioteca
Zap / Logrus para Go: guía completa con ejemplos
Okio para Kotlin: guía completa con ejemplos prácticos
GraphQL en Kotlin: Reseña de bibliotecas y herramientas para desarrollo del lado del servidor
ts-mockito para TypeScript: guía completa con ejemplos