Event Bus y el patrón Observer en Go (Golang): implementación y ejemplos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

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

Recommendations