Ya empezamos a entender qué son los eventos. Si le envías un mensaje a un amigo y no responde enseguida, normalmente solo esperas la respuesta — en vez de hacer polling continuo al messenger. Claro, salvo que sea algo muy urgente. En programación los eventos funcionan de manera parecida.
Permiten que el sistema funcione de forma asíncrona y aislada. Por ejemplo:
- Un servicio puede notificar a otro sobre un cambio de estado.
- El sistema puede encadenar acciones donde un evento inicia el siguiente.
¿Cuándo usar eventos en EDA?
Los eventos son ideales para casos donde:
- Acoplamiento débil entre componentes es crucial. Esto permite que el sistema sea robusto y modular.
- El procesamiento asíncrono de datos es preferible a las llamadas síncronas a la API.
- Trabajar con grandes volúmenes de datos que deben procesarse por pasos. Por ejemplo, procesamiento de pedidos en e-commerce o actualización del estado del sistema.
- Necesidad de scalability (escalabilidad). Los eventos ayudan a repartir la carga entre microservicios.
- Quieres que tus componentes funcionen de forma independiente, incluso si alguno de ellos está temporalmente caído.
¿Cómo aplicar eventos? Ejemplos prácticos
Notificaciones y eventos
Imagínate que tienes una app de e-commerce. Cuando un usuario realiza un pedido, esa acción puede lanzar varios procesos:
- Enviar una notificación del pedido.
- Descontar el producto del inventario.
- Empaque y envío.
Aquí el evento "Pedido creado" actúa como trigger. Los componentes interactúan mediante eventos, no directamente, lo que los hace independientes.
Ejemplo:
// Evento que se dispara al crear un pedido
public class OrderCreatedEvent {
private final String orderId;
private final LocalDateTime timestamp;
public OrderCreatedEvent(String orderId) {
this.orderId = orderId;
this.timestamp = LocalDateTime.now();
}
public String getOrderId() {
return orderId;
}
public LocalDateTime getTimestamp() {
return timestamp;
}
}
// Producer: crea el evento y lo envía
@Component
public class OrderEventProducer {
private final KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate;
public OrderEventProducer(KafkaTemplate<String, OrderCreatedEvent> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void publishOrderEvent(OrderCreatedEvent event) {
kafkaTemplate.send("orders-topic", event);
System.out.println("Order event sent: " + event.getOrderId());
}
}
// Subscriber: procesa el evento
@Component
@KafkaListener(topics = "orders-topic", groupId = "order-group")
public class NotificationService {
@KafkaHandler
public void handleOrderEvent(OrderCreatedEvent event) {
System.out.println("Sending notification for order: " + event.getOrderId());
}
}
Actualización asíncrona del estado
Supón que creas una app con un sistema para rastrear envíos. Los servicios de entrega funcionan de forma independiente, pero actualizan el estado en el sistema central.
- Cada servicio envía el evento "Envío actualizado".
- El servicio centralizado procesa los eventos y actualiza la base de datos.
Este enfoque reduce la carga sobre el servicio central y acelera el procesamiento.
Patrones de diseño para eventos
Patrón "Best-Effort Messaging". Este enfoque no garantiza la entrega del mensaje, pero encaja en casos donde la pérdida del mensaje no es crítica. Por ejemplo, el registro (logging) de eventos.
Patrón "Guaranteed Delivery". Si un evento debe entregarse obligatoriamente, usa librerías o brokers de mensajes que garanticen la entrega al menos una vez ("At Least Once"). Por ejemplo, Kafka maneja bien este caso usando replicación de mensajes.
Patrón "Dead Letter Queue (DLQ)". ¿Qué hacer si un mensaje no puede procesarse? Puedes enviarlo a la DLQ — una cola para mensajes "problemáticos". Esto permite hacer seguimiento de errores y su corrección.
Enfoques para optimizar el uso de eventos
* Uso de eventos con carga mínima*
Un evento debe llevar solo la información necesaria. Por ejemplo, el evento "Pedido creado" no debería contener 10 MB de datos del pedido. Basta con el identificador del pedido.
Ejemplo malo:
{
"orderId": "12345",
"customerData": { "name": "Iván Ivánov", "email": "ivanov@example.com" },
"products": [
{ "id": "prod1", "name": "Teléfono", "quantity": 1 },
{ "id": "prod2", "name": "Funda", "quantity": 2 },
...
]
}
Mejor opción:
{
"orderId": "12345",
"timestamp": "2023-10-20T12:00:00"
}
Jerarquía de eventos
Los eventos a menudo tienen relaciones entre ellos. Por ejemplo:
- El evento "Pago procesado" puede generar "Pago confirmado" o "Pago rechazado".
- Una buena estructuración de los eventos facilitará su procesamiento.
Consejos y recomendaciones prácticas
- No crees lógica demasiado compleja para procesar eventos. La simplicidad es la clave del éxito.
- Mantén el formato de los eventos. JSON o Avro ayudarán a estandarizar los mensajes.
- Monitorea los eventos regularmente. Usa herramientas como Grafana o ELK Stack para ver qué pasa con tus eventos.
- Piensa en el rendimiento del broker de mensajes. Por ejemplo, Kafka soporta miles de eventos por segundo — es ideal para sistemas de alta carga.
- Documenta los eventos. Describe qué significa cada evento para facilitar el trabajo a otros desarrolladores.
¿Cuándo no deberías usar eventos?
EDA no es una bala de plata. Si tu sistema exige un orden estricto de ejecución y sincronía, por ejemplo en el procesamiento de transacciones bancarias, los eventos pueden añadir complejidad innecesaria. Para esos casos son mejores las llamadas síncronas (REST, gRPC).
Así, el uso de eventos ayuda a construir sistemas de alto rendimiento, flexibles y escalables. Es importante recordar que los eventos son una herramienta y deben usarse con criterio. En la próxima lección empezaremos a ver las herramientas que ayudan a trabajar eficazmente con EDA, incluyendo Kafka, RabbitMQ y ActiveMQ.
GO TO FULL VERSION