ADR-0004 · Eventing: EventBridge dominio + SNS outbound + SQS bulkhead + Outbox¶
- Status: Accepted
- Date: 2026-04-30
- Deciders: Cristian Fernández (Zerviz Group)
- Related: ADR-0003 (DDB Streams), ADR-0007 (Strategy),
docs/discovery/00-phase1-prerequisites.md7.1/7.2/7.3.
Context¶
El legacy es síncrono y sin DLQ; cualquier latencia en Five9/Meta paga el cliente final (R-07). La decisión asentada es híbrida: EventBridge para eventos de dominio, SNS para notificaciones outbound (alineado con el Lab golden path), SQS+DLQ por contexto, circuit breaker en SSM.
Decision¶
EventBridge (eventos de dominio)¶
- Bus dedicado por env:
ze-{env}-domain-events(no usardefault). - Naming de eventos:
Source = ze.<service>,DetailType = <Aggregate>.<Event>en PascalCase. Ej:Source=ze.inbound-router,DetailType=Conversation.MessageReceived. - Detail payload obligatorio:
- Schemas:
EventBridge Schema Registrycon discovery activo en dev; promotion manual a qa/prod. - Reglas declarativas: un consumer = una rule + una SQS objetivo (Pipes para fan-out preservando orden cuando aplique). Filtros por
detail.tenant_idpara tier físico (multi-cuenta cross-account event publishing). - Cross-account: las cuentas tenant físico tienen permission policy que permite a
ze-prod-domain-eventsrecibir/emitir eventos cruzados.
SNS (notificaciones outbound)¶
- Topic:
ze-{env}-outbound-notificationspara fan-out simple (cuando un evento debe ir a múltiples destinos sin lógica de filtrado, ej. webhooks de cliente final, métricas externas). - No se usa SNS para eventos de dominio (esos van por EventBridge).
SQS bulkhead + DLQ (asentado 7.2)¶
- Patrón obligatorio: cada consumer EventBridge tiene su SQS principal + DLQ. Nunca un consumer lee directo de EventBridge (siempre vía SQS).
- Naming:
ze-{env}-<service>-<event-name>yze-{env}-<service>-<event-name>-dlq. - Retry policy:
maxReceiveCount = 5. Tras 5 fallos pasa a DLQ. - Visibility timeout: 6× timeout del Lambda consumer.
- Bulkhead inbound vs campañas: colas separadas para
inbound-router(alta prioridad, baja latencia) youtbound-dispatcher(best-effort, throughput). - DLQ alarms: CloudWatch alarm
ApproximateNumberOfMessagesVisible > 0durante 5 min ⇒ PagerDuty.
Outbox Pattern¶
- DDB Streams habilitado en tablas que requieren atomicidad DB ↔ evento:
ze-flow-state,ze-outbound-runs,ze-tenantsyze-conversations(cuando exista). - Lambda EventPublisher consume el stream y publica a EventBridge con idempotencia (key =
<tableName>#<eventID>). - Esto garantiza "una y sólo una" publicación incluso ante crash del servicio que escribió en DDB.
Circuit Breaker (asentado 7.3)¶
- Estado persistido en SSM Parameter Store:
/ze/{env}/breakers/<connector>/<state>con valoresclosed | open | half-open,last_failure_at,failure_count. - Middleware en
packages/connector-sdk/breaker.ts: aplica patrón Hystrix-like, con threshold por defecto 5 fallos en 60 s ⇒ open por 30 s; un trial request ⇒ half-open. - Métricas: custom
BreakerOpenCount,BreakerHalfOpenTrialsvía Powertools metrics.
Consequences¶
Positive¶
- Inbound robusto ante caídas de Five9/Meta/360 (mensaje espera en SQS hasta procesar).
- Outbox elimina ventanas de inconsistencia DB ↔ evento.
- Cross-account events permiten que tier físico participe del event mesh.
Negative¶
- 4 piezas (EventBridge, SNS, SQS, SSM) ⇒ curva de aprendizaje. Mitigado con
infra/modules/eventing/que provisiona el patrón completo en una llamada. - Outbox vía Streams agrega latencia ~1 s entre commit DB y publicación.
Alternatives considered¶
- Sólo SNS+SQS (Lab golden path): rechazado, EventBridge da Schema Registry + filtros declarativos + Pipes nativos.
- MSK / Kafka: descartado (overkill, costo, ops).
- EventBridge sin SQS bulkhead: rechazado, los retries de EventBridge son limitados; SQS da control fino.
- Step Functions Standard para todos los flows: descartado por costo; sí se usa Express dentro de
outbound-dispatcherSaga.
Tenant-isolation impact¶
tenant_idobligatorio endetailde cada evento. Rules pueden filtrar por tenant.- Cross-account: permission policy granular por cuenta tenant físico.
Blast radius¶
- DLQ por consumer = fallos contenidos. Un connector caído no detiene a los demás.
- Saga (Step Functions) compensación garantiza rollback de campañas parciales.
Cost note¶
- EventBridge: USD 1.00 / millón de eventos.
- SQS: USD 0.40 / millón de requests.
- SNS: USD 0.50 / millón de publishes.
- Dev: < USD 5/mes total. Prod (10k msg/día): ~USD 30/mes.
ISO 27001 controls touched¶
- A.12.4.1 (event logging): EventBridge histórico + DLQ traces.
- A.13.2.1 (políticas de transferencia de información): cross-account permission policy.
- A.14.2.5 (secure engineering): outbox pattern garantiza consistencia.
Sources¶
docs/discovery/00-phase1-prerequisites.md7.1, 7.2, 7.3.docs/discovery/07-lab-golden-path.md(patrón SNS+SQS adaptado).- AWS docs: EventBridge Schema Registry, Pipes, SSM Parameter Store.