Saltar a contenido

Correlation Identifier

1. Nombre del Patrón

  • Nombre oficial: Correlation Identifier
  • Categoría: Message Construction (Construcción de Mensajes)
  • Traducción contextual: Identificador de Correlación

2. Resumen Ejecutivo

Correlation Identifier es el patrón que resuelve el problema de vincular un mensaje de respuesta con su mensaje de petición original en un sistema de mensajería asíncrono. El solicitante genera un identificador único, lo incluye en el mensaje de petición, y el destinatario lo copia en el mensaje de respuesta. Al recibir la respuesta, el solicitante usa el identificador para asociarla con la petición correcta.

El problema que resuelve es sutil pero crítico: en messaging asíncrono, las respuestas no llegan por el mismo canal que las peticiones, y pueden llegar en un orden diferente al que fueron solicitadas. Si un solicitante envía tres peticiones concurrentes y recibe tres respuestas, ¿cómo sabe cuál respuesta corresponde a cuál petición? Sin Correlation Identifier, es imposible determinarlo.

Más allá de Request-Reply, Correlation Identifier es el mecanismo fundamental que conecta mensajes relacionados en cualquier flujo distribuido: pasos de una saga, fases de un pipeline, eventos de un workflow, o spans de un trace. Es, en esencia, el "hilo" que une mensajes dispersos en un sistema distribuido en una conversación o proceso lógico coherente.

El patrón tiene una relevancia extraordinaria en las arquitecturas modernas: el correlation-id es un header estándar en prácticamente todos los protocolos de messaging, y conceptos como el trace_id de OpenTelemetry, el saga_id de un orquestador, y el conversation_id de un chatbot son variantes especializadas del mismo principio.


3. Definición Detallada

Propósito

Correlation Identifier proporciona un mecanismo para vincular mensajes que pertenecen a la misma conversación, transacción o flujo de procesamiento. Su propósito principal es permitir que un receptor identifique a qué contexto previo pertenece un mensaje recibido, sin depender del orden de llegada, del canal de origen, ni de la inspección del contenido del mensaje.

Lógica Arquitectónica

En una comunicación síncrona (HTTP, gRPC), la correlación es implícita: la respuesta llega por la misma conexión que la petición, así que no hay ambigüedad sobre a qué petición corresponde. En messaging asíncrono, esta correlación implícita desaparece porque:

  • La petición y la respuesta viajan por canales diferentes.
  • Puede haber múltiples peticiones en vuelo simultáneamente.
  • Las respuestas pueden llegar en un orden diferente al de las peticiones (por diferencias en tiempo de procesamiento, routing, o prioridad).
  • Múltiples requestors pueden compartir un canal de respuesta común.

Correlation Identifier resuelve todas estas ambigüedades introduciendo un identificador único que viaja con el mensaje a lo largo de todo su ciclo de vida:

  1. El requestor genera un identificador único (UUID, ULID, snowflake, etc.).
  2. Lo incluye en la petición como header correlation_id.
  3. El replier copia ese correlation_id en la respuesta.
  4. El requestor, al recibir la respuesta, busca el correlation_id en su registro de peticiones pendientes y los vincula.

La Tríada de Identificadores: message_id, correlation_id, causation_id

En sistemas maduros, se distinguen tres identificadores complementarios en cada mensaje:

  • message_id: identificador único de este mensaje específico. Cada mensaje tiene un message_id diferente. Sirve para deduplicación y auditoría.
  • correlation_id: identificador de la conversación o flujo al que pertenece este mensaje. Múltiples mensajes comparten el mismo correlation_id si pertenecen al mismo flujo. Sirve para correlación y tracing.
  • causation_id: identificador del mensaje que causó directamente este mensaje. Es el message_id del mensaje anterior en la cadena. Sirve para reconstruir el grafo de causalidad.

Ejemplo de un flujo de tres pasos:

Paso message_id correlation_id causation_id Descripción
1 msg-001 corr-ABC (null) Petición inicial
2 msg-002 corr-ABC msg-001 Respuesta a la petición
3 msg-003 corr-ABC msg-002 Acción derivada de la respuesta

Los tres mensajes comparten el mismo correlation_id (corr-ABC) porque pertenecen al mismo flujo. Pero cada uno tiene un message_id diferente y un causation_id que apunta al mensaje que lo originó.

Principio de Diseño Subyacente

El principio es identidad de conversación propagada: un identificador único se genera al inicio de un flujo y se propaga en todos los mensajes derivados de ese flujo, independientemente de cuántos servicios, canales o transformaciones intervengan. Este principio es la base del distributed tracing, la saga orchestration y la auditoría de flujos de negocio.

Problema Estructural que Resuelve

Sin Correlation Identifier, la única forma de vincular mensajes relacionados es por inspección de contenido: comparar campos del body de la petición con campos del body de la respuesta (por ejemplo, buscar el mismo customer_id). Este enfoque es:

  • Frágil: depende de la estructura interna del mensaje, que puede cambiar.
  • Ambiguo: si hay dos peticiones para el mismo customer_id, la inspección de contenido no distingue cuál es cuál.
  • Ineficiente: requiere parsear el body de cada mensaje para buscar coincidencias.
  • Insuficiente: no funciona para mensajes cuyo contenido no tiene campos naturales de correlación.

Correlation Identifier proporciona un mecanismo genérico, externo al contenido, y libre de ambigüedad para vincular mensajes relacionados.

Contexto en el que Emerge

Correlation Identifier emerge en cualquier escenario donde:

  • Se implementa Request-Reply con peticiones concurrentes.
  • Se orquesta una saga con múltiples pasos y participantes.
  • Se necesita distributed tracing a través de múltiples servicios y canales.
  • Se implementa un pipeline de procesamiento con múltiples etapas.
  • Se necesita auditoría de un flujo de negocio de principio a fin.

Relación con Sistemas Distribuidos

En la teoría de sistemas distribuidos, Correlation Identifier implementa un mecanismo de logical clock compartido a nivel de conversación. Así como un vector clock permite ordenar eventos en un sistema distribuido, el Correlation Identifier permite agrupar eventos (mensajes) que pertenecen a la misma "historia causal". La combinación de correlation_id + causation_id reconstruye el grafo de causalidad completo de un flujo.

En el contexto de observabilidad, el correlation_id es funcionalmente equivalente al trace_id de OpenTelemetry: un identificador que agrupa todos los spans (operaciones) de una transacción distribuida. De hecho, muchos equipos usan el trace_id de OpenTelemetry como correlation_id de sus mensajes, unificando correlación de messaging con distributed tracing.


4. Problema que Resuelve

El Problema Antes del Patrón

Sin Correlation Identifier, un sistema que envía múltiples peticiones concurrentes y recibe respuestas en un canal compartido no puede determinar con certeza a qué petición corresponde cada respuesta:

Requestor envía:
  Petición A (customer_id=100, query=balance)     → Request Channel
  Petición B (customer_id=200, query=balance)     → Request Channel
  Petición C (customer_id=100, query=last_payment) → Request Channel

Requestor recibe en Reply Channel:
  Respuesta X: { balance: 5000 }
  Respuesta Y: { balance: 12000 }
  Respuesta Z: { last_payment: "2026-04-01" }

¿La respuesta X corresponde a la petición A o B? Ambas son consultas de balance. Sin un identificador de correlación, la única pista es el contenido, pero si el requestor no conoce los saldos de antemano, no puede distinguir cuál es cuál. La respuesta Z parece corresponder a la petición C por el tipo de dato, pero esto es frágil y no escala.

Síntomas del Problema

  • Respuestas vinculadas a peticiones incorrectas, produciendo datos erróneos que se propagan silenciosamente.
  • Implementaciones ad-hoc de correlación basadas en campos del body ("matching por customer_id + query_type") que son frágiles y no genéricas.
  • Imposibilidad de tener peticiones concurrentes: el requestor se ve forzado a enviar una petición y esperar la respuesta antes de enviar la siguiente (serialización artificial).
  • Imposibilidad de trazar un flujo de negocio de principio a fin: los mensajes relacionados no pueden vincularse porque no hay identificador común.
  • Logs y métricas desconectados: los logs del requestor y del replier no pueden correlacionarse porque no hay identificador compartido.

Impacto Operativo y Arquitectónico

Sin Correlation Identifier:

  • Debugging es una pesadilla: cuando un flujo falla, no hay forma de rastrear todos los mensajes relacionados a través de múltiples servicios y canales.
  • Auditoría es imposible: los reguladores exigen poder reconstruir el flujo completo de una transacción (quién solicitó qué, quién respondió qué, en qué orden). Sin correlación, esto requiere minería de datos manual.
  • Sagas son inviables: un saga orchestrator que envía comandos a múltiples servicios y espera confirmaciones no puede vincular las confirmaciones con los comandos sin correlación.
  • Monitoreo de latencia es impreciso: no se puede medir la latencia end-to-end de un flujo si no se pueden vincular el primer y el último mensaje.

Riesgos Si No Se Implementa Correctamente

  • Correlación duplicada: si el correlation_id no es único (por colisión de IDs o reutilización), dos flujos diferentes se mezclan y las respuestas se vinculan a peticiones equivocadas.
  • Correlación perdida: si un intermediario (router, translator, aggregator) no propaga el correlation_id de entrada en sus mensajes de salida, se rompe la cadena de correlación.
  • Correlación abusada: usar un solo correlation_id para todo el ciclo de vida de una entidad de negocio (ej. el ID del pedido como correlation_id) mezcla múltiples flujos independientes sobre la misma entidad.

Ejemplos Reales

  • Seguros: un sistema de procesamiento de reclamos inicia una saga que involucra verificación de póliza, evaluación de daños, aprobación de gerencia y liquidación de pago. Cada paso es un mensaje asíncrono. El correlation_id (= claim_id) vincula todos los mensajes de la saga para auditoría y monitoreo.
  • Telecomunicaciones: un sistema de portabilidad numérica envía solicitudes a múltiples operadores simultáneamente. Cada solicitud tiene un correlation_id que permite vincular la respuesta de cada operador con la solicitud original, incluso cuando las respuestas llegan desordenadas.
  • E-commerce: un servicio de checkout envía concurrentemente una petición de validación de tarjeta, una consulta de inventario y una consulta de impuestos. Las tres respuestas llegan al mismo servicio, cada una con su correlation_id, permitiendo completar el checkout correctamente.

5. Contexto de Aplicación

Cuándo Usarlo

  • En toda implementación de Request-Reply con peticiones concurrentes.
  • En sagas (orchestration o choreography) para vincular todos los pasos del flujo.
  • En pipelines de procesamiento multi-etapa para rastrear un item a través de todas las etapas.
  • En distributed tracing para vincular spans de diferentes servicios.
  • En auditoría de flujos de negocio para reconstruir la secuencia completa de eventos.
  • En deduplicación: el correlation_id combinado con el paso del flujo permite detectar mensajes duplicados.

Cuándo No Usarlo

  • En comunicación unidireccional fire-and-forget donde no hay respuesta ni flujo multi-paso.
  • Cuando los mensajes son completamente independientes y no tienen relación causal entre sí (ej. eventos de telemetría de diferentes dispositivos).
  • Cuando ya existe un identificador natural de correlación en el dominio de negocio que cumple los mismos requisitos (unicidad, propagación) — en este caso, se puede usar ese identificador como correlation_id sin introducir uno nuevo.

Precondiciones

  • El sistema de mensajería soporta headers/properties en los mensajes.
  • Todos los participantes del flujo (requestors, repliers, intermediarios) implementan la lógica de leer y propagar el correlation_id.
  • Existe una fuente de identificadores únicos (UUID generator, ULID, snowflake).

Restricciones

  • El correlation_id debe ser único dentro del alcance temporal y funcional del sistema. Un UUID v4 tiene probabilidad de colisión despreciable; un identificador secuencial puede colisionar entre instancias.
  • Todos los intermediarios (routers, translators, enrichers) deben propagar el correlation_id. Si un intermediario no lo hace, se rompe la cadena.
  • El correlation_id no debe contener información sensible (PII, tokens) porque aparece en logs, traces y headers de messaging.

Supuestos Arquitectónicos

  • Los mensajes pueden transportar metadata (headers) además del payload.
  • Existe un acuerdo (contrato de integración) sobre el nombre del header que contiene el correlation_id.
  • Los sistemas de logging y monitoring indexan por correlation_id para permitir búsquedas transversales.

6. Fuerzas Arquitectónicas

Simplicidad vs. Trazabilidad

Incluir un correlation_id en cada mensaje añade complejidad (generación del ID, propagación, logging). Pero sin él, la trazabilidad de flujos distribuidos es prácticamente inexistente. La fuerza gravitacional hacia la observabilidad en arquitecturas modernas hace que el costo de no tener correlación sea mucho mayor que el costo de implementarla.

Unicidad vs. Performance

Generar un UUID v4 para cada petición tiene un costo computacional mínimo pero no despreciable en sistemas de ultra-alto throughput (millones de mensajes por segundo). Alternativas como ULID (ordenable por tiempo) o snowflake (generación distribuida sin coordinación) ofrecen trade-offs diferentes entre unicidad, ordenamiento y performance.

Granularidad vs. Utilidad

¿Qué alcance tiene el correlation_id? Si es demasiado amplio (ej. todos los mensajes de un pedido durante meses), acumula cientos de mensajes y pierde utilidad para debugging de un flujo específico. Si es demasiado estrecho (ej. solo la petición y su respuesta directa), no permite rastrear flujos multi-paso. La granularidad correcta es un flujo lógico de negocio: una consulta de saldo, un procesamiento de reclamo, un checkout completo.

Estandarización vs. Flexibilidad

¿Qué nombre tiene el header? correlation_id, correlationId, X-Correlation-ID, trace_id, request_id? Cada plataforma y cada equipo tiene su propia convención. La estandarización (un solo nombre en toda la organización) maximiza la interoperabilidad; la flexibilidad permite que cada equipo use su convención preferida. El W3C Trace Context define un estándar para traceparent, pero no todos los sistemas lo adoptan.

Propagación vs. Seguridad

El correlation_id se propaga a través de todos los servicios, logs, traces y posiblemente respuestas al cliente. Si el ID contiene información derivable (ej. un hash del customer_id), podría exponer información. Los correlation_id deben ser opacos (no derivables del contenido de negocio).


7. Estructura Conceptual del Patrón

Actores o Componentes Involucrados

  1. Requestor: genera el correlation_id y lo incluye en la petición.
  2. Replier: lee el correlation_id de la petición y lo copia en la respuesta.
  3. Intermediarios (routers, translators, enrichers): propagan el correlation_id de los mensajes de entrada a los mensajes de salida.
  4. Sistema de logging/tracing: indexa logs y traces por correlation_id para búsquedas transversales.

Flujo Lógico

flowchart TD
    A([Requestor]) --> B[Generar correlation_id\núnico - UUID v4]
    B --> C[Incluir correlation_id\ncomo header del mensaje]
    C --> D[(Request Channel)]
    C --> E[(Mapa de pendientes\ncorrelation_id - petición)]
    D --> F([Replier])
    F --> G[Leer correlation_id\ndel header]
    G --> H[Registrar en logs\npara trazabilidad]
    H --> I[Procesar la petición]
    I --> J[Construir respuesta con\nel mismo correlation_id]
    J --> K[(Reply Channel)]
    K --> L[Requestor recibe respuesta]
    L --> M[Leer correlation_id\nde la respuesta]
    M --> N[Buscar en mapa de\npeticiones pendientes]
    N --> O[Vincular respuesta\ncon petición original]
    O --> P[Eliminar del mapa\nde pendientes]
    P --> Q([Fin])

Responsabilidades

Componente Responsabilidad
Requestor Generar correlation_id único, incluirlo en la petición, mantener mapa de peticiones pendientes, vincular respuesta con petición
Replier Leer correlation_id de la petición, copiarlo en la respuesta
Intermediarios Propagar correlation_id de mensajes de entrada a mensajes de salida
Logging/Tracing Indexar por correlation_id para búsquedas transversales

Decisiones de Diseño Clave

  1. Formato del identificador: UUID v4 (aleatorio, no ordenable), ULID (ordenable por tiempo), snowflake (distribuido, compacto), o identificador de negocio (claim_id, order_id).
  2. Quién genera el correlation_id: el requestor inicial (patrón estándar) o un servicio central de generación de IDs.
  3. Nombre del header: correlation_id, correlationId, X-Correlation-ID, o alineado con OpenTelemetry (traceparent).
  4. Alcance de la correlación: solo request-reply (dos mensajes) o todo un flujo de saga (N mensajes).
  5. Propagación automática vs. manual: ¿el framework propaga automáticamente el correlation_id o cada servicio debe hacerlo explícitamente?

8. Ejemplo Arquitectónico Detallado

Dominio: Seguros — Procesamiento de Reclamo con Correlación Multi-Servicio

Contexto del Negocio

Una compañía de seguros procesa reclamos de accidentes vehiculares. El procesamiento de un reclamo involucra múltiples servicios que operan asincrónicamente: verificación de póliza, evaluación de daños, detección de fraude, aprobación y liquidación. El flujo completo puede tardar entre minutos (caso simple) y días (caso con investigación de fraude).

Cada reclamo genera docenas de mensajes a través de múltiples servicios y canales. La compañía necesita:

  1. Poder rastrear todos los mensajes relacionados con un reclamo específico.
  2. Medir la latencia end-to-end del procesamiento de reclamos.
  3. Cumplir requisitos regulatorios de auditoría: reconstruir la secuencia completa de eventos de cualquier reclamo.
  4. Detectar reclamos "atascados" (donde un paso no avanzó).

Sistemas Involucrados

  1. Claims Portal: portal web donde el asegurado reporta el reclamo.
  2. Claims Orchestrator: saga orchestrator que coordina el flujo.
  3. Policy Service: verifica vigencia y cobertura de la póliza.
  4. Damage Assessment Service: evalúa el monto del daño (puede involucrar un perito).
  5. Fraud Detection Service: analiza el reclamo con modelos de ML para detectar fraude.
  6. Approval Service: gestiona la aprobación (automática o manual según monto).
  7. Payment Service: ejecuta la liquidación del pago al asegurado.
  8. Apache Kafka: plataforma de messaging.

Diseño de Correlación

Cada reclamo genera un flujo que produce múltiples mensajes. Se usa una jerarquía de identificadores:

Identificador Alcance Ejemplo
message_id Único por mensaje msg-8a7b6c5d-4e3f-2a1b-0c9d-8e7f6a5b4c3d
correlation_id Mismo para todo el flujo del reclamo corr-claim-2026-04-07-001847
causation_id message_id del mensaje que causó este msg-1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d

Flujo de Mensajes con Correlación

Paso Mensaje message_id correlation_id causation_id Canal
1 Claim Submitted msg-001 corr-claim-001847 claims.submitted
2 Verify Policy (cmd) msg-002 corr-claim-001847 msg-001 policy.verify.cmd
3 Policy Verified (reply) msg-003 corr-claim-001847 msg-002 claims.orchestrator.reply
4 Assess Damage (cmd) msg-004 corr-claim-001847 msg-003 damage.assess.cmd
5 Damage Assessed (reply) msg-005 corr-claim-001847 msg-004 claims.orchestrator.reply
6 Check Fraud (cmd) msg-006 corr-claim-001847 msg-005 fraud.check.cmd
7 Fraud Check Clear (reply) msg-007 corr-claim-001847 msg-006 claims.orchestrator.reply
8 Approve Claim (cmd) msg-008 corr-claim-001847 msg-007 approval.request.cmd
9 Claim Approved (reply) msg-009 corr-claim-001847 msg-008 claims.orchestrator.reply
10 Process Payment (cmd) msg-010 corr-claim-001847 msg-009 payment.process.cmd
11 Payment Completed (reply) msg-011 corr-claim-001847 msg-010 claims.orchestrator.reply
12 Claim Resolved (event) msg-012 corr-claim-001847 msg-011 claims.resolved

Los 12 mensajes comparten el mismo correlation_id: corr-claim-001847. Buscando por este ID en los logs de cualquier servicio, se obtiene la historia completa del reclamo.

Riesgos y Mitigaciones

Riesgo Mitigación
Un servicio no propaga el correlation_id Validación automatizada: test de integración que verifica propagación; middleware que rechaza mensajes sin correlation_id
Colisión de correlation_id Usar UUID v4 (probabilidad de colisión: 2^-122); o incluir un prefijo con timestamp (ULID)
Flujo "atascado" sin avanzar Timer en el orchestrator por paso; alerta si un paso excede su SLA; dashboard de reclamos por estado
Auditoría requiere reconstruir el flujo Todos los mensajes se indexan por correlation_id en un event store o data lake

9. Desarrollo Paso a Paso del Ejemplo

Paso 1: Generación del Correlation Identifier

El asegurado reporta un accidente en el Claims Portal. El portal genera la petición inicial:

{
  "claimant": { "policy_number": "POL-2024-789456", "name": "María García" },
  "incident": {
    "date": "2026-04-05",
    "type": "vehicle_collision",
    "description": "Colisión lateral en intersección",
    "estimated_damage_usd": 3500
  },
  "evidence": ["photo-001.jpg", "photo-002.jpg", "police-report-4521.pdf"]
}

Headers:

message_id: "msg-001-a1b2c3d4"
correlation_id: "corr-claim-2026-04-07-001847"    ← GENERADO AQUÍ
causation_id: null                                  ← es el primer mensaje
timestamp: "2026-04-07T10:30:15.847Z"

El correlation_id se genera en este momento y se propaga en todos los mensajes subsiguientes del flujo. Se elige un formato con prefijo semántico (corr-claim-) para facilitar la búsqueda en logs.

Paso 2: Orquestación con Propagación de Correlación

El Claims Orchestrator consume el evento Claim Submitted y comienza la saga. Cada comando que envía a los servicios participantes copia el correlation_id y establece el causation_id:

Comando al Policy Service:

message_id: "msg-002-e5f6a7b8"
correlation_id: "corr-claim-2026-04-07-001847"    ← COPIADO del mensaje original
causation_id: "msg-001-a1b2c3d4"                  ← message_id del mensaje que lo causó

El Policy Service verifica la póliza y responde:

message_id: "msg-003-c9d0e1f2"
correlation_id: "corr-claim-2026-04-07-001847"    ← COPIADO del comando recibido
causation_id: "msg-002-e5f6a7b8"                  ← message_id del comando

Paso 3: Correlación en el Orchestrator

El Claims Orchestrator escucha en claims.orchestrator.reply. Recibe la respuesta del Policy Service. Para vincular la respuesta con el paso correcto de la saga:

  1. Lee el correlation_id: "corr-claim-2026-04-07-001847".
  2. Busca en su registro de sagas activas: encuentra la saga del reclamo 001847.
  3. Lee el causation_id: "msg-002-e5f6a7b8" → identifica que es la respuesta al comando de verificación de póliza.
  4. Actualiza el estado de la saga: paso "policy verification" completado.
  5. Determina el siguiente paso: enviar comando de evaluación de daños.

Sin el correlation_id, el orchestrator no sabría a qué reclamo pertenece la respuesta. Sin el causation_id, no sabría a qué paso de la saga corresponde.

Paso 4: Detección de Fraude con Correlación

El Fraud Detection Service recibe el comando con correlation_id: "corr-claim-2026-04-07-001847". Su procesamiento involucra múltiples sub-pasos internos (consulta a bases externas, ejecución de modelo ML, scoring). Cada sub-paso registra logs con el correlation_id:

[2026-04-07T10:30:45.123Z] [correlation_id=corr-claim-2026-04-07-001847] Fraud check initiated
[2026-04-07T10:30:45.456Z] [correlation_id=corr-claim-2026-04-07-001847] External DB query: no prior claims
[2026-04-07T10:30:46.789Z] [correlation_id=corr-claim-2026-04-07-001847] ML model score: 0.12 (low risk)
[2026-04-07T10:30:46.890Z] [correlation_id=corr-claim-2026-04-07-001847] Fraud check result: CLEAR

Cuando un operador investiga el reclamo 001847, puede buscar correlation_id=corr-claim-2026-04-07-001847 en el sistema de logs centralizado y obtener todos los logs de todos los servicios involucrados, en orden cronológico.

Paso 5: Reclamo Atascado

Otro reclamo (corr-claim-2026-04-07-001523) envió un comando de evaluación de daños hace 48 horas y no ha recibido respuesta. El orchestrator tiene un timer por paso. El timer expira y genera una alerta:

[ALERT] Saga step timeout
  correlation_id: corr-claim-2026-04-07-001523
  step: damage_assessment
  command_message_id: msg-047-x1y2z3
  sent_at: 2026-04-05T14:22:10Z
  timeout_after: 48h
  action: escalate to supervisor

El supervisor busca el correlation_id en los logs y encuentra que el Damage Assessment Service recibió el comando pero el perito asignado no ha registrado su evaluación. El correlation_id permite vincular la alerta con el flujo completo del reclamo.


10. Diagrama Técnico del Patrón

Código Python con diagrams

Diagrama General

Diagrama AWS

Diagrama Azure

Ver / Copiar código de los diagramas
from diagrams import Diagram, Cluster, Edge
from diagrams.onprem.queue import Kafka
from diagrams.onprem.compute import Server
from diagrams.onprem.database import PostgreSQL
from diagrams.onprem.monitoring import Grafana
from diagrams.elastic.elasticsearch import Elasticsearch

with Diagram("Correlation Identifier - Insurance Claims Saga", show=False, direction="TB"):

    with Cluster("Claims Ingestion"):
        portal = Server("Claims\nPortal")

    with Cluster("Kafka Topics"):
        submitted = Kafka("claims.submitted")
        policy_cmd = Kafka("policy.verify.cmd")
        damage_cmd = Kafka("damage.assess.cmd")
        fraud_cmd = Kafka("fraud.check.cmd")
        approval_cmd = Kafka("approval.request.cmd")
        payment_cmd = Kafka("payment.process.cmd")
        orch_reply = Kafka("claims.orchestrator\n.reply")
        resolved = Kafka("claims.resolved")

    with Cluster("Saga Orchestrator"):
        orchestrator = Server("Claims\nOrchestrator")
        saga_db = PostgreSQL("Saga State\n(correlation_id\nas PK)")

    with Cluster("Domain Services"):
        policy_svc = Server("Policy\nService")
        damage_svc = Server("Damage\nAssessment")
        fraud_svc = Server("Fraud\nDetection")
        approval_svc = Server("Approval\nService")
        payment_svc = Server("Payment\nService")

    with Cluster("Observability"):
        elk = Elasticsearch("Log Index\n(by correlation_id)")
        grafana = Grafana("Saga\nDashboard")

    # Claim submission
    portal >> Edge(label="corr-id generated") >> submitted

    # Orchestrator consumes and produces
    submitted >> orchestrator
    orchestrator >> saga_db
    orchestrator >> policy_cmd
    orchestrator >> damage_cmd
    orchestrator >> fraud_cmd
    orchestrator >> approval_cmd
    orchestrator >> payment_cmd
    orchestrator >> resolved

    # Services reply with same correlation_id
    policy_cmd >> policy_svc
    damage_cmd >> damage_svc
    fraud_cmd >> fraud_svc
    approval_cmd >> approval_svc
    payment_cmd >> payment_svc

    policy_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    damage_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    fraud_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    approval_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    payment_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply

    orch_reply >> orchestrator

    # Observability indexed by correlation_id
    orchestrator >> Edge(style="dotted") >> elk
    policy_svc >> Edge(style="dotted") >> elk
    fraud_svc >> Edge(style="dotted") >> elk
    elk >> grafana
from diagrams import Diagram, Cluster, Edge
from diagrams.aws.compute import Lambda
from diagrams.aws.database import Dynamodb
from diagrams.aws.integration import SQS, StepFunctions
from diagrams.aws.management import Cloudwatch
from diagrams.aws.devtools import XRay


with Diagram("Correlation Identifier - Insurance Claims Saga (AWS)", show=False, direction="TB"):

    with Cluster("Claims Ingestion"):
        portal = Lambda("Claims\nPortal")

    with Cluster("SQS Command Queues"):
        submitted = SQS("claims.submitted")
        policy_cmd = SQS("policy.verify.cmd")
        damage_cmd = SQS("damage.assess.cmd")
        fraud_cmd = SQS("fraud.check.cmd")
        approval_cmd = SQS("approval.request.cmd")
        payment_cmd = SQS("payment.process.cmd")
        orch_reply = SQS("claims.orchestrator\n.reply")
        resolved = SQS("claims.resolved")

    with Cluster("Saga Orchestrator"):
        orchestrator = StepFunctions("Claims\nOrchestrator\n(Step Functions)")
        saga_db = Dynamodb("Dynamodb\n(correlation_id\nas PK)")

    with Cluster("Domain Services"):
        policy_svc = Lambda("Policy\nService")
        damage_svc = Lambda("Damage\nAssessment")
        fraud_svc = Lambda("Fraud\nDetection")
        approval_svc = Lambda("Approval\nService")
        payment_svc = Lambda("Payment\nService")

    with Cluster("Observability"):
        xray = XRay("X-Ray Traces\n(by correlation_id)")
        cw_dash = Cloudwatch("CloudWatch\nSaga Dashboard")

    # Claim submission
    portal >> Edge(label="corr-id generated") >> submitted

    # Orchestrator consumes and produces
    submitted >> orchestrator
    orchestrator >> saga_db
    orchestrator >> policy_cmd
    orchestrator >> damage_cmd
    orchestrator >> fraud_cmd
    orchestrator >> approval_cmd
    orchestrator >> payment_cmd
    orchestrator >> resolved

    # Services reply with same correlation_id
    policy_cmd >> policy_svc
    damage_cmd >> damage_svc
    fraud_cmd >> fraud_svc
    approval_cmd >> approval_svc
    payment_cmd >> payment_svc

    policy_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    damage_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    fraud_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    approval_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply
    payment_svc >> Edge(style="dashed", label="corr-id\ncopied") >> orch_reply

    orch_reply >> orchestrator

    # Observability indexed by correlation_id
    orchestrator >> Edge(style="dotted") >> xray
    policy_svc >> Edge(style="dotted") >> xray
    fraud_svc >> Edge(style="dotted") >> xray
    xray >> cw_dash
from diagrams import Diagram, Cluster, Edge
from diagrams.azure.compute import FunctionApps
from diagrams.azure.database import CosmosDb
from diagrams.azure.devops import ApplicationInsights
from diagrams.azure.integration import ServiceBus
from diagrams.azure.analytics import LogAnalyticsWorkspaces


with Diagram("Correlation Identifier - Insurance Claims Saga (Azure)", show=False, direction="TB"):

    with Cluster("Claims Ingestion"):
        portal = FunctionApps("Claims\nPortal")

    with Cluster("Service Bus Queues (CorrelationId)"):
        submitted = ServiceBus("claims.submitted\n(Queue)")
        policy_cmd = ServiceBus("policy.verify.cmd\n(Queue)")
        damage_cmd = ServiceBus("damage.assess.cmd\n(Queue)")
        fraud_cmd = ServiceBus("fraud.check.cmd\n(Queue)")
        approval_cmd = ServiceBus("approval.request.cmd\n(Queue)")
        payment_cmd = ServiceBus("payment.process.cmd\n(Queue)")
        orch_reply = ServiceBus("claims.orchestrator\n.reply (Queue)")
        resolved = ServiceBus("claims.resolved\n(Queue)")

    with Cluster("Durable Functions Orchestrator"):
        orchestrator = FunctionApps("Claims\nOrchestrator\n(Durable Functions)")
        saga_db = CosmosDb("Saga State\n(CorrelationId\nas PK)")

    with Cluster("Domain Services"):
        policy_svc = FunctionApps("Policy\nService")
        damage_svc = FunctionApps("Damage\nAssessment")
        fraud_svc = FunctionApps("Fraud\nDetection")
        approval_svc = FunctionApps("Approval\nService")
        payment_svc = FunctionApps("Payment\nService")

    with Cluster("Observability"):
        app_insights = ApplicationInsights("Application Insights\n(by CorrelationId)")
        log_analytics = LogAnalyticsWorkspaces("Log Analytics\nWorkspace")

    # Claim submission
    portal >> Edge(label="CorrelationId\ngenerated") >> submitted

    # Orchestrator consumes and produces
    submitted >> orchestrator
    orchestrator >> saga_db
    orchestrator >> policy_cmd
    orchestrator >> damage_cmd
    orchestrator >> fraud_cmd
    orchestrator >> approval_cmd
    orchestrator >> payment_cmd
    orchestrator >> resolved

    # Services reply with same CorrelationId
    policy_cmd >> policy_svc
    damage_cmd >> damage_svc
    fraud_cmd >> fraud_svc
    approval_cmd >> approval_svc
    payment_cmd >> payment_svc

    policy_svc >> Edge(style="dashed", label="CorrelationId\ncopied") >> orch_reply
    damage_svc >> Edge(style="dashed", label="CorrelationId\ncopied") >> orch_reply
    fraud_svc >> Edge(style="dashed", label="CorrelationId\ncopied") >> orch_reply
    approval_svc >> Edge(style="dashed", label="CorrelationId\ncopied") >> orch_reply
    payment_svc >> Edge(style="dashed", label="CorrelationId\ncopied") >> orch_reply

    orch_reply >> orchestrator

    # Observability indexed by CorrelationId
    orchestrator >> Edge(style="dotted") >> app_insights
    policy_svc >> Edge(style="dotted") >> app_insights
    fraud_svc >> Edge(style="dotted") >> app_insights
    app_insights >> log_analytics

Explicación del Diagrama

El diagrama muestra cómo el Correlation Identifier (corr-id) fluye a través de toda la saga de procesamiento de reclamos:

  1. El Claims Portal genera el correlation_id al crear el reclamo y lo incluye en el primer mensaje (claims.submitted).
  2. El Claims Orchestrator consume el evento, almacena el estado de la saga indexado por correlation_id, y envía comandos a cada servicio con el mismo correlation_id.
  3. Cada Domain Service recibe el comando, procesa y responde al canal claims.orchestrator.reply copiando el correlation_id.
  4. El Orchestrator recibe las respuestas, las vincula con la saga correcta usando el correlation_id, y avanza al siguiente paso.
  5. Todos los servicios envían sus logs a Elasticsearch indexados por correlation_id, permitiendo búsquedas transversales.
  6. Grafana muestra dashboards de sagas en progreso, latencia por paso y reclamos atascados.

Correspondencia Patrón ↔ Diagrama

Concepto del Patrón Componente del Diagrama
Correlation Identifier Header correlation_id presente en todos los mensajes de la saga
Generación del ID Claims Portal (primer mensaje)
Propagación del ID Cada servicio copia el correlation_id de entrada en su respuesta
Vinculación por ID Claims Orchestrator busca por correlation_id en su saga state
Trazabilidad por ID Elasticsearch indexa logs por correlation_id

11. Beneficios

Impacto Técnico

  • Correlación determinista: cada respuesta se vincula con su petición original sin ambigüedad, independientemente del orden de llegada o la concurrencia.
  • Distributed tracing: el correlation_id permite rastrear un flujo a través de múltiples servicios, canales y bases de datos. Una búsqueda por correlation_id en los logs centralizados devuelve la historia completa del flujo.
  • Saga management: el orchestrator puede gestionar miles de sagas concurrentes, cada una identificada por su correlation_id, sin confundir pasos de diferentes sagas.
  • Deduplicación: la combinación de correlation_id + paso de la saga permite detectar mensajes duplicados (ej. dos respuestas del mismo servicio para el mismo paso de la misma saga).

Impacto Organizacional

  • Debugging cross-team: cuando un flujo falla, el equipo de soporte comparte el correlation_id con los equipos de cada servicio involucrado. Cada equipo busca el ID en sus logs y contribuye su perspectiva. El ID es el lenguaje común de troubleshooting.
  • SLA measurement: la latencia de cada reclamo (desde Claim Submitted hasta Claim Resolved) se mide vinculando el primer y último mensaje por correlation_id.

Impacto Operacional

  • Alertas contextuales: cuando se detecta un reclamo atascado, la alerta incluye el correlation_id, que permite al operador acceder inmediatamente al contexto completo.
  • Auditoría automatizada: los auditores pueden solicitar el registro completo de un reclamo proporcionando el correlation_id. El sistema genera automáticamente un timeline con todos los mensajes, decisiones y respuestas.
  • Monitoreo de flujo: dashboards que muestran flujos por estado (in-progress, completed, stuck, failed) agrupados por correlation_id.

12. Desventajas y Riesgos

Complejidad Añadida

  • Propagación obligatoria: todos los servicios del flujo deben leer y propagar el correlation_id. Si un servicio no lo hace (por bug, oversight o incompatibilidad), se rompe la cadena de correlación para todos los mensajes posteriores.
  • Infraestructura de logging: para que el correlation_id sea útil, los logs de todos los servicios deben centralizarse e indexarse por este campo. Sin logging centralizado, el beneficio se reduce drásticamente.
  • Complejidad en intermediarios: routers, translators, splitters y aggregators deben propagar el correlation_id de los mensajes de entrada a los de salida. En splitters, ¿todos los fragments comparten el mismo correlation_id? En aggregators, ¿qué correlation_id tiene el mensaje agregado?

Riesgos de Mal Uso

  • Confundir message_id con correlation_id: usar el message_id de la petición como correlation_id de la respuesta funciona para request-reply simple, pero no para flujos multi-paso (porque cada mensaje del flujo tendría un correlation_id diferente).
  • Reutilizar correlation_id entre flujos: asignar el mismo correlation_id a dos flujos independientes (ej. usando un ID secuencial que se reinicia) mezcla sus mensajes en logs y traces.
  • Correlation_id demasiado amplio: usar un ID de entidad de negocio (customer_id, order_id) como correlation_id agrupa todos los flujos de esa entidad bajo el mismo ID, dificultando aislar un flujo específico.

Costos de Operación

  • Storage de logs indexados: indexar logs por correlation_id requiere un campo adicional en cada log entry y un índice adicional en el sistema de logging (Elasticsearch, CloudWatch, etc.).
  • Overhead de headers: cada mensaje transporta headers adicionales (correlation_id, causation_id, message_id), incrementando el tamaño de los mensajes (generalmente unos pocos bytes, despreciable en la mayoría de casos).

Anti-Patterns

  • Correlation without logging: incluir correlation_id en los mensajes pero no en los logs. El ID viaja por el sistema pero no se registra en ningún lugar consultable, anulando su utilidad para debugging.
  • Manual correlation: depender de que cada desarrollador recuerde incluir el correlation_id en cada mensaje, en lugar de implementarlo como middleware automático.

13. Relación con Otros Patrones

Patrones Complementarios

  • Request-Reply (este capítulo): Correlation Identifier es un componente esencial de Request-Reply. Sin él, las respuestas no pueden vincularse con las peticiones.
  • Return Address (este capítulo): Return Address indica dónde enviar la respuesta; Correlation Identifier indica a qué petición corresponde la respuesta. Ambos son necesarios para Request-Reply completo.
  • Message Expiration (este capítulo): cuando una petición tiene un correlation_id y un TTL, las peticiones expiradas pueden limpiarse del mapa de peticiones pendientes del requestor.

Patrones que Suelen Aparecer Antes o Después

  • Antes: Command Message — los comandos de una saga incluyen correlation_id para vincular las respuestas.
  • Después: Aggregator — un aggregator que recolecta múltiples mensajes para producir un resultado combinado usa el correlation_id para agrupar los mensajes que deben agregarse.

Combinaciones Comunes

  • Correlation Identifier + Saga Orchestrator: el orchestrator usa el correlation_id como clave primaria del estado de la saga.
  • Correlation Identifier + Distributed Tracing (OpenTelemetry): el correlation_id se mapea al trace_id de OpenTelemetry, unificando correlación de messaging con distributed tracing.
  • Correlation Identifier + Dead Letter Channel: los mensajes en dead-letter incluyen el correlation_id, permitiendo vincularlos con el flujo original que falló.
  • Correlation Identifier + Wire Tap: los mensajes interceptados por wire tap incluyen el correlation_id, permitiendo auditoría sin afectar el flujo.

Diferencias con Patrones Similares

  • vs. Message Sequence: Message Sequence numera los fragmentos de un mensaje grande (sequence number); Correlation Identifier vincula mensajes de un mismo flujo lógico. Pueden combinarse: los fragmentos de un mismo flujo comparten correlation_id y cada uno tiene su sequence_number.
  • vs. Claim Check: Claim Check usa un identificador para recuperar datos de un store externo; Correlation Identifier usa un identificador para vincular mensajes relacionados. Diferentes propósitos para un mecanismo superficialmente similar.

14. Relevancia Actual del Patrón

Evaluación: Relevancia Alta

Argumentación

Correlation Identifier no solo sigue vigente — su importancia ha crecido exponencialmente con la adopción de microservicios, event-driven architectures y observability-first design. En la era monolítica, la correlación era implícita (todo ocurría en el mismo proceso). En arquitecturas distribuidas, la correlación explícita es un requisito de supervivencia operacional.

Cómo Se Implementa Hoy

Plataforma / Estándar Implementación
JMS JMSCorrelationID — header estándar de la especificación
AMQP 0-9-1 (RabbitMQ) correlation_id — propiedad del mensaje
Azure Service Bus CorrelationId — propiedad del sistema
Apache Kafka kafka_correlationId — header por convención (Spring Kafka)
AWS SQS/SNS CorrelationId — message attribute por convención
OpenTelemetry trace_id + span_id — estándar W3C Trace Context
HTTP X-Correlation-ID o X-Request-ID — header por convención
gRPC x-correlation-id — metadata por convención

Convergencia con OpenTelemetry

La tendencia moderna es unificar el correlation_id de messaging con el trace_id de OpenTelemetry:

Mensaje Kafka Header:
  traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
  correlation_id: "4bf92f3577b34da6a3ce929d0e0e4736"  (= trace_id)

Esto permite que los traces de OpenTelemetry (visibles en Jaeger, Zipkin, Azure Monitor) y los logs de messaging (visibles en Elasticsearch, Splunk) se vinculen automáticamente.

Qué Parte Sigue Siendo Esencial

  • El concepto de un identificador único que viaja con el mensaje a través de todo el flujo.
  • La tríada message_id / correlation_id / causation_id como modelo de identidad de mensajes.
  • La propagación automática del correlation_id por middleware y frameworks.
  • La indexación de logs y traces por correlation_id para observabilidad transversal.

15. Implementación en Arquitecturas Modernas

Spring Boot + Kafka (Java)

// Middleware que propaga correlation_id automáticamente
@Component
public class CorrelationInterceptor implements ProducerInterceptor<String, String> {
    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        String correlationId = CorrelationContext.getCurrentCorrelationId();
        if (correlationId != null) {
            record.headers().add("correlation_id", correlationId.getBytes());
        }
        return record;
    }
}

Spring Kafka y Spring Cloud Stream propagan el correlation_id automáticamente si se configura correctamente, eliminando la necesidad de propagación manual en cada servicio.

Azure Service Bus (.NET)

// Procesamiento con propagación automática de CorrelationId
[ServiceBusAccount("claims")]
public class ClaimProcessor {
    public async Task ProcessClaim(
        [ServiceBusTrigger("policy.verify.cmd")] ServiceBusReceivedMessage message,
        [ServiceBus("claims.orchestrator.reply")] IAsyncCollector<ServiceBusMessage> replies
    ) {
        var result = await VerifyPolicy(message.Body);
        var reply = new ServiceBusMessage(result) {
            CorrelationId = message.CorrelationId,  // PROPAGACIÓN
            ApplicationProperties = {
                ["causation_id"] = message.MessageId
            }
        };
        await replies.AddAsync(reply);
    }
}

OpenTelemetry Integration

# Crear span con correlation_id como trace context
from opentelemetry import trace
from opentelemetry.context import attach, set_value

tracer = trace.get_tracer("claims-service")

def process_message(message):
    correlation_id = message.headers.get("correlation_id")
    # Crear span vinculado al trace del correlation_id
    with tracer.start_as_current_span(
        "process_claim",
        attributes={
            "correlation_id": correlation_id,
            "message_id": message.headers.get("message_id"),
            "causation_id": message.headers.get("causation_id"),
        }
    ) as span:
        # Toda la lógica dentro de este span se vincula al trace
        result = verify_policy(message.body)
        return result

Saga State Management

-- Estado de saga indexado por correlation_id
CREATE TABLE saga_state (
    correlation_id VARCHAR(64) PRIMARY KEY,
    saga_type VARCHAR(50) NOT NULL,
    current_step VARCHAR(50) NOT NULL,
    status VARCHAR(20) NOT NULL,  -- ACTIVE, COMPLETED, COMPENSATING, FAILED
    payload JSONB,
    created_at TIMESTAMPTZ NOT NULL,
    updated_at TIMESTAMPTZ NOT NULL,
    timeout_at TIMESTAMPTZ
);

CREATE INDEX idx_saga_status ON saga_state(status, timeout_at);
-- Permite encontrar sagas atascadas: WHERE status='ACTIVE' AND timeout_at < NOW()

16. Consideraciones de Gobierno y Operación

Observabilidad

  • Indexación obligatoria: todos los logs de todos los servicios deben incluir el correlation_id como campo estructurado (no embebido en el texto). Esto permite búsquedas eficientes.
  • Cross-service timeline: herramientas como Jaeger o Zipkin visualizan el flujo completo de una transacción vinculando spans por trace_id (que puede ser el correlation_id).
  • Métricas por correlation_id: latencia end-to-end del flujo, número de pasos completados, número de reintentos.

Monitoreo

  • Sagas atascadas: consultar periódicamente la tabla de saga state para detectar sagas con status ACTIVE cuyo timeout_at ha pasado.
  • Propagation gaps: detectar mensajes que no tienen correlation_id (indica un servicio que no lo propaga). Alertar cuando se detecten mensajes sin correlación en canales que deberían tenerla.
  • Correlation cardinality: monitorear el número de correlation_id únicos activos para dimensionar el saga state store y los índices de logs.

Estandarización

  • Definir en la organización un único nombre para el header: correlation_id (o alineado con W3C: traceparent).
  • Definir el formato del identificador: UUID v4, ULID, o formato custom con prefijo semántico.
  • Definir qué servicio genera el correlation_id (el primer servicio del flujo, el API gateway, el portal).
  • Documentar la política de propagación: todo intermediario debe copiar el correlation_id de entrada en sus mensajes de salida.

Seguridad

  • El correlation_id no debe contener PII ni información sensible derivable.
  • El correlation_id puede usarse para auditoría regulatoria: conservar la asociación entre correlation_id y entidad de negocio (claim_id, order_id) en un store seguro.
  • En entornos multi-tenant, el correlation_id debe incluir un discriminador de tenant o validarse contra el tenant del mensaje.

Lifecycle Management

  • Los correlation_id de flujos completados pueden archivarse después de un período de retención.
  • Los logs indexados por correlation_id tienen retención definida por política (30, 60, 90 días según el regulador).
  • La tabla de saga state debe tener un proceso de limpieza para sagas completadas, conservando un resumen para auditoría.

17. Errores Comunes

No Propagar el Correlation Identifier en Intermediarios

El error más frecuente y más dañino. Un servicio intermedio (router, translator, enricher) consume un mensaje con correlation_id, produce un nuevo mensaje, y no copia el correlation_id. El resultado: se rompe la cadena de correlación, y todos los mensajes posteriores no pueden vincularse con el flujo original. Prevención: implementar la propagación como middleware automático (interceptor, filter), no como código manual en cada servicio.

Confundir message_id con correlation_id

Usar el message_id de la petición como correlation_id de todo el flujo. Esto funciona para request-reply simple (dos mensajes), pero falla en flujos multi-paso: cada paso tiene un message_id diferente, así que si se usa message_id como clave de correlación, cada paso tendría una clave diferente. Solución: el correlation_id se genera una vez al inicio del flujo y se propaga sin cambios en todos los mensajes. El message_id es único por mensaje; el correlation_id es compartido por el flujo.

Correlation Identifier Demasiado Amplio

Usar un identificador de entidad de negocio duradero (customer_id, policy_number) como correlation_id. El resultado: todos los flujos relacionados con esa entidad (múltiples reclamos del mismo cliente, múltiples consultas sobre la misma póliza) se agrupan bajo el mismo correlation_id, haciendo imposible aislar un flujo específico. El correlation_id debe identificar un flujo, no una entidad.

No Incluir correlation_id en los Logs

El correlation_id viaja en los headers de los mensajes pero no se registra en los logs de los servicios. Esto anula el beneficio de trazabilidad: los mensajes están correlacionados, pero los logs no. Cada log entry debe incluir el correlation_id como campo estructurado para que las búsquedas transversales funcionen.

Generación de correlation_id con Colisiones

Usar un generador de IDs que produce colisiones (ej. timestamp truncado, contador por instancia sin discriminador). Dos flujos diferentes reciben el mismo correlation_id, y sus mensajes se mezclan en logs, traces y saga state. Solución: usar UUID v4 o ULID, cuya probabilidad de colisión es estadísticamente despreciable.

No Definir la Distinción correlation_id vs. causation_id

Usar solo correlation_id sin causation_id. Esto permite agrupar mensajes del mismo flujo pero no reconstruir el orden causal (qué mensaje causó qué otro mensaje). Para sagas complejas con bifurcaciones (pasos paralelos), el causation_id es necesario para reconstruir el grafo de causalidad.


18. Conclusión Técnica

Correlation Identifier es el patrón que convierte una colección de mensajes dispersos en un flujo coherente y rastreable. Sin correlación, los mensajes de un sistema distribuido son hojas sueltas; con correlación, son páginas de un mismo libro.

Cuándo aporta valor: en todo flujo que involucra más de un mensaje relacionado — Request-Reply, sagas, pipelines, workflows. El valor crece exponencialmente con el número de servicios y mensajes involucrados: en un flujo de 2 mensajes, la correlación es útil; en un flujo de 12 mensajes a través de 6 servicios (como la saga de reclamos del ejemplo), es absolutamente indispensable.

Cuándo evita problemas importantes: cuando se necesita diagnosticar un problema en un flujo distribuido. Sin correlation_id, el equipo de soporte debe reconstruir manualmente el flujo buscando coincidencias de timestamps, payloads y secuencias — un proceso que puede tomar horas. Con correlation_id, una búsqueda en los logs centralizados devuelve la historia completa en segundos.

Cuándo no conviene adoptarlo: en mensajes completamente independientes sin relación causal (ej. eventos de telemetría de dispositivos diferentes). Incluso en estos casos, un message_id es útil para deduplicación, pero el correlation_id no tiene propósito si no hay mensajes relacionados.

Recomendación para arquitectos: implemente la tríada completa — message_id, correlation_id, causation_id — como headers obligatorios en todos los mensajes del sistema. Implemente la propagación como middleware automático, no como código manual en cada servicio. Alinee el correlation_id con el trace_id de OpenTelemetry para unificar correlación de messaging con distributed tracing. Indexe todos los logs por correlation_id y construya dashboards que muestren flujos en progreso, completados, atascados y fallidos. El Correlation Identifier es la columna vertebral de la observabilidad en una arquitectura event-driven: sin él, se opera a ciegas.