aws.crafter.run

Comunicación

Pub/sub con EventBridge

También llamado Content-based routing

Publicar eventos en un bus donde cada consumidor declara con un patrón qué le interesa, de modo que el productor no conozca ni un solo destino y añadir consumidores no toque nada.

IntermedioVerificado el

El problema

Empezaste con un topic de SNS llamado pedidos y funcionaba. Luego alguien necesitó solo los pedidos de España, así que apareció pedidos-es. Después alguien quiso solo los de importe alto, y nació pedidos-grandes. Hoy tienes veinte topics, una convención de nombres que nadie recuerda y un productor que publica en cinco sitios distintos.

El acoplamiento no desapareció al usar un topic. Se mudó: ahora vive en el nombre del canal, y cada consumidor nuevo con un criterio nuevo obliga a crear un topic y a tocar el productor para que publique también ahí.

Es el mismo problema que resolvía el fan-out, un nivel más arriba: seguimos pidiendo al productor que sepa quién escucha.

La solución

Un bus, y reglas que miran dentro del evento.

crearPedidobuspedidosReglas · cada una con su patrónreglaimporte > 500antifraudereglatodos los pedidosanalíticareglapais = ESfacturación ESMáximo 5 destinos por regla, y no se amplía · el patrón cabe en 2 048 caracteres
El consumidor declara lo que le interesa; el productor no declara destinos. Añadir una cuarta regla no toca nada de lo que ya existe — ni el productor se entera de que apareció, ni de que mañana desaparezca.

El productor publica siempre en el mismo bus y no declara destinos. Cada consumidor crea una regla con un patrón sobre el JSON del evento:

{
  "detail-type": ["PedidoCreado"],
  "detail": {
    "pais": ["ES"],
    "importe": [{ "numeric": [">", 500] }]
  }
}

Nadie tuvo que crear un topic pedidos-españa-grandes. El productor no sabe que esa regla existe, no se entera si mañana desaparece, y añadir un cuarto consumidor no toca una sola línea de lo que ya funciona.

Esa es la propiedad que hace que EventBridge escale organizativamente: el coste de sumar un equipo que reacciona a tus eventos deja de recaer sobre ti.

El archivo es lo que suele decidir de verdad

Un bus puede archivar los eventos que pasan por él y reproducirlos después. Es lo que SNS estándar no tiene y no va a tener.

Lo que conviene saber antes de contar con ello:

Cuándo usarlo

Cuándo NO usarlo

Cómo implementarlo

  1. Crea un bus propio. El bus por defecto recibe además los eventos de todos los servicios de AWS de tu cuenta; mezclar ahí tus eventos de dominio complica patrones y permisos.
  2. Fija un esquema de eventos: source, detail-type y un detail con versión. Los patrones se escriben contra esos campos, así que son contrato.
  3. Una regla por consumidor lógico, no una regla gigante con cinco destinos.
  4. Cola SQS entre la regla y la función cuando el consumidor deba aguantar picos o caídas, igual que en un fan-out.
  5. DLQ en cada destino. Si el destino falla, EventBridge reintenta y luego descarta.
  6. Archivo desde el principio si algún día vas a querer replay: no se puede archivar hacia atrás.

El código

// ── Publicar: el productor no nombra ningun destino ────────────────
await eb.send(new PutEventsCommand({
  Entries: [{
    EventBusName: 'app',
    Source: 'pedidos',
    DetailType: 'PedidoCreado',
    Detail: JSON.stringify({ v: 1, id, pais: 'ES', importe: 750 }),
  }],
}));
// ── Suscribirse: el consumidor declara su patron ───────────────────
new events.Rule(this, 'AntifraudePedidosGrandes', {
  eventBus: bus,
  eventPattern: {
    source: ['pedidos'],
    detailType: ['PedidoCreado'],
    detail: { importe: [{ numeric: ['>', 500] }] },
  },
  // Una cola en medio para que el consumidor absorba picos y caidas,
  // exactamente por la misma razon que en un fan-out con SNS.
  targets: [new targets.SqsQueue(colaAntifraude, {
    deadLetterQueue: dlqAntifraude,
  })],
});

// El archivo hay que crearlo ANTES: no se archiva hacia atras.
new events.Archive(this, 'ArchivoPedidos', {
  sourceEventBus: bus,
  retention: cdk.Duration.days(90),
  eventPattern: { source: ['pedidos'] },
});

Te va a morder

Coste

Concepto Precio aproximado
Eventos personalizados ~1,00 USD / millón
Eventos de servicios de AWS al bus por defecto sin coste
Archivo por GB almacenado
Replay por evento reproducido

Comparado con SNS —del orden de 0,50 USD por millón de publicaciones, con las entregas a SQS y Lambda gratis— EventBridge sale claramente más caro por evento. Estás pagando el enrutado por contenido, el archivo y el replay. Si no vas a usar ninguno de los tres, estás pagando de más por nada.

Detalle que conviene aprovechar: reaccionar a un cambio de estado de EC2 o a un Object Created de S3 no se factura en el bus por defecto. Publicar tus propios eventos de dominio, sí.

Fuentes

Destinos por regla, reglas con comodines, PutEvents e invocaciones por región, reglas y buses por cuenta, tamaño del patrón y de la política del bus: Amazon EventBridge quotas. Retención indefinida por defecto, filtrado del archivo, replay solo al bus de origen, ausencia de orden, intervalos de un minuto, recomendación de esperar 10 minutos, 10 replays concurrentes, borrado a los 90 días y campo replay-name: Archiving and replaying events in Amazon EventBridge. Cuotas de SNS usadas en la comparación: Amazon SNS endpoints and quotas.

Patrones relacionados

Un enlace sin la relación nombrada es un "ver también". Aquí cada uno dice qué relación tiene y por qué.