Arquitectura de eventos
Arquitectura de eventos (EDA)
1) Qué es un evento y por qué EDA
El evento es un hecho inmutable que ya ha ocurrido en el dominio («PlayerVerified», «PaymentCaptured»). EDA construye integraciones en torno a la publicación de estos hechos y las reacciones a ellos:- la débil conectividad de los servicios,
- ampliar el tamaño de los consumidores de forma independiente,
- replay/reajuste de proyecciones,
- auditoría transparente.
EDA no cancela las API sincrónicas: las complementa llevando las dependencias de servicio cruzado a la capa asíncrona.
2) Tipos de eventos
Dominios: hechos comerciales significativos (OrderPlaced, BonusGranted).
Integración: «instantáneas «/cambios para sistemas externos (UserUpdated, WalletBalanceChanged).
Técnico: ciclo de vida y telemetría (Heartbeat, PipelineFailed).
Comandos (no eventos, pero cerca): instrucciones para «hacer X» (CapturePayment).
Recomendación: eventos de dominio - primarios; las integraciones están formadas por proyecciones para consumidores específicos.
3) Contratos de eventos y esquemas
Схема: Avro/Protobuf/JSON Schema + Schema Registry; estrategia de compatibilidad: 'BACKWARD' para la evolución de los consumidores, 'FULL' sobre temas críticos.
CloudEvents (id, source, type, time, subject, datacontenttype) son títulos uniformes.
Metadatos obligatorios: 'event _ id' (ULID/UUID), 'occurred _ at', 'producer', 'schema _ version', 'correlation _ id '/' causation _ id', 'idempotency _ key'.
Versificación: add-only campos, prohibición de renombraciones/roturas semánticas; nuevos tipos - nuevos temas/tipos.
json
{
"type":"record","name":"PaymentCaptured","namespace":"events.v1",
"fields":[
{"name":"event_id","type":"string"},
{"name":"occurred_at","type":{"type":"long","logicalType":"timestamp-micros"}},
{"name":"payment_id","type":"string"},
{"name":"amount","type":{"type":"bytes","logicalType":"decimal","precision":18,"scale":2}},
{"name":"currency","type":"string"},
{"name":"player_id","type":"string"}
]
}
4) Entrega, orden y coherencia
At-least-once como default → es necesaria la idempotencia de los manejadores.
Orden: garantizado dentro del lote (Kafka) o cola (RabbitMQ), pero puede ser violado cuando se retrae; la clave de evento debe reflejar un gránulo de dominio de orden (por ejemplo, 'player _ id').
Coherencia: para dinero/créditos - sólo a través de revistas/sagas/compensaciones; evite el LWW.
Modelo de lectura: las proyecciones y cachés pueden ser eventuales - muestre «va la actualización»... y use estrategias RNOT para rutas estrictas.
5) Outbox/Inbox и CDC
Outbox: el servicio escribe el hecho en su DB y en la tabla de outbox en una sola transacción → el worker publica en el bus.
Inbox: el consumidor guarda 'event _ id' con el resultado de procesamiento para el dedup.
CDC (Change Data Capture): flujo de cambios desde la DB (binlog/WAL) al bus para construir integraciones sin cambios en la aplicación.
Idempotency: procesar por 'idempotency _ key '/' event _ id', no cambiar el mundo exterior antes de fijar.
6) CQRS и Event Sourcing
CQRS: compartimos el modelo write y las proyecciones read; las proyecciones se construyen a partir de eventos y pueden retrasarse.
Event Sourcing: estado del agregado = la convolución de sus eventos. Pros: auditoría completa/repetición; contras: complejidad de las migraciones/esquemas/snapshots.
Práctica: ES no está en todas partes, sino donde la historia y la compensación son importantes; CQRS - casi siempre en EDA.
7) Sagas: orquestación y coreografía
Orquestación: el coordinador envía comandos y espera eventos-respuestas; conveniente para procesos complejos (KYC→Deposit→Bonus).
Coreografía: los servicios responden a los acontecimientos del otro; más fácil, pero más difícil de rastrear.
Siempre determine las compensaciones y los deduplines de los pasos.
8) Diseño de topologías (Kafka/RabbitMQ)
Kafka
Topic per evento de dominio: 'payments. captured. v1`, `players. verified. v1`.
Clave de partición: 'player _ id '/' wallet _ id' - donde el orden es importante.
`replication. factor=3`, `min. insync. replicas = 2 ', productor' acks = all '.
Retención: por tiempo (por ejemplo, 7-90 días) y/o compactación (último estado por clave).
Topics para retry y DLQ con backoff.
RabbitMQ
Exchanges: `topic`/`direct`, routing key `payments. captured. v1`.
Para un amplio fan out - 'topic' + varias colas; para RPC/comandos: colas individuales.
Quorum Queues para HA; TTL + dead-letter exchange para retraídos.
9) Observabilidad y SLO EDA
SLI/SLO:- End-to-end latency (occurred_at → procesado): p50/p95/p99.
- Lag/age: rezago de los consumidores (Kafka consumer lag, Rabbit backlog age).
- Throughput de publicación/procesamiento.
- DLQ-rate y proporción de repeticiones.
- Éxito de las operaciones comerciales (por ejemplo, «depósito confirmado ≤ 5c»).
- Correlación de eventos a través de 'trace _ id '/' correlation _ id' (OTel).
- Instancias (exemplars) de métricas de alineación →.
- Dashboards «Producer→Broker→Consumer» con alertas burn-rate.
10) Replay, retoque y backfill
Replay para reconstruir proyecciones/corrección de errores: conduzca a una nueva proyección/neymspace, luego cambie de lectura.
Retiro: requisitos legales/empresariales (GDPR/PCI); campos sensibles - cifrar y/o tokenizar.
Backfill: temas/colas desechables, límites RPS claros para no estrangular el prod.
11) Seguridad y cumplimiento
TLS in-transit, mTLS para clientes internos.
Autorización: per-topic/per-exchange ACL; multitenancy vía namespace/vhost.
PII: minimizar los campos en un evento; envelope metadatos por separado, cargas útiles para cifrar si es necesario.
Auditoría de acceso a eventos, prohibición de claves de «todo-poder».
Políticas de retiro y derechos de eliminación (GDPR): almacene enlaces a datos o eventos de tombstone y eliminación en proyecciones.
12) Pruebas en EDA
Pruebas de contrato: los consumidores validan sus expectativas de circuitos (consumer-driven).
Pruebas de respuesta: ejecutar una muestra histórica a través del nuevo controlador/versión del circuito.
Los escenarios chaos: retraso/pérdida del corredor, caída de nodos, retraso del consumidor → SLO permanecen dentro del marco.
Smoke in CI: un corto pipeline de fin a fin sobre temas temporales.
13) Migración de las «integraciones CRUD → EDA»
1. Identificar hechos de dominio.
2. Introduce outbox en los servicios originales.
3. Publique eventos de dominio mínimos y conecte 1-2 proyecciones.
4. Desactive gradualmente las integraciones sincrónicas puntuales reemplazándolas por suscripciones.
5. Introduzca Schema Registry y la política de compatibilidad.
6. Amplíe los eventos add-only con campos; rompe - sólo a través de nuevos tipos.
14) Anti-patrones
Eventos = «DTO API» (demasiado negrita, depende del modelo interno) - rompen a los consumidores.
La ausencia de Registro Schema y la compatibilidad son integraciones «frágiles».
Publicar desde código y escribir en DB no es atómico (no outbox) - pierde eventos.
«Exactly-once en todas partes» - alto precio sin beneficio; mejor at-least-once + idempotencia.
Una llave de lote «universal» → un lote caliente.
Replay directamente en la proyección prod - rompe los SLO en línea.
15) Lista de verificación de implementación (0-45 días)
0-10 días
Definir eventos de dominio y sus claves (gránulos de orden).
Implementar el Registro de Schema y aprobar la estrategia de interoperabilidad.
Añadir outbox/inbox a 1-2 servicio; mínima CloudEvents-envelope.
11-25 días
Introduzca retry/DLQ, backoff, idempotencia de los manejadores.
Dashboards: lag/age/end-to-end; alertas burn-rate.
Documentación de eventos (directorio), owner's y procesos de rugido de esquemas.
26-45 días
Reproducir/reconstruir la primera proyección; runbook replay y backfill.
Políticas de seguridad (TLS, ACL, PII), retoque, procedimientos GDPR.
Chaos-and game-days regulares para el corredor y los consumidores.
16) Métricas de madurez
El 100% de los eventos de dominio son descritos por esquemas y registrados.
Outbox/inbox cubre todos los productores/consumidores de Tier-0/1.
SLO: p95 end-to-end latency y consumer lag dentro de los objetivos ≥ 99%.
Replay/Backfill son factibles sin downtime; hay un runbook 'y probado.
Versionar: campos nuevos - sin romper; los viejos consumidores no caen.
Seguridad: TLS + mTLS, ACL per topic, registros de acceso, política de PII/retoque.
17) Mini Snippets
Kafka Producer (publicación confiable, ideas):properties acks=all enable.idempotence=true max.in.flight.requests.per.connection=1 compression.type=zstd linger.ms=5
Procesador de consumo (idempotencia, pseudocódigo):
python if inbox.contains(event_id): return # дедуп process(event) # побочные эффекты детерминированы inbox.commit(event_id) # atomically with side-effect commit_offset()
RabbitMQ Retry a través de DLX (idea):
- `queue: tasks` → on nack → DLX `tasks. retry. 1m '(TTL = 60s) → retorno a' tasks '; a continuación, '5m/15m'.
18) Conclusión
EDA transforma las integraciones en un flujo de hechos empresariales con contratos claros y coherencia gestionada. Construye la base: circuitos + registro, outbox/inbox, llaves de orden, manejadores idempotentes, SLO y observabilidad, retoque y retoque seguros. Entonces los acontecimientos se convertirán en su «fuente de verdad» para escalar, analizar y hacer nuevas cosas, sin conexiones frágiles y migraciones nocturnas.