Event-Driven Architecture with NATS · Parte 6

NATS vs Kafka vs AWS SQS: un framework de decisión para ingenieros

Una comparación honesta de semántica de entrega, persistencia, carga operativa, fan-out, replay, portabilidad y costo, con tablas de selección basadas en síntomas en lugar de guerras de benchmarks.

20 min de lectura

Ya leíste cómo encajan los subjects de NATS, los streams de JetStream, los mirrors de KV, las proyecciones CQRS y la observabilidad en las partes 1–5 de esta serie. También viste dónde se ubica un bus de mensajes en una arquitectura en crecimiento en Las piezas del diseño de software moderno y eficaz. La pregunta que queda es directa: ¿cuándo es NATS el bus adecuado y cuándo conviene recurrir a Kafka o AWS SQS?

Este artículo es un framework de decisión, no una competencia en la que solo puede haber un ganador. Las afirmaciones que siguen se basan en documentación oficial. Cuando una fila refleja criterio de ingeniería —por ejemplo, la carga operativa típica de un equipo pequeño— se indica que es una inferencia.

Investigación basada en docs.nats.io, la documentación de Apache Kafka y la documentación de AWS SQS el July 6, 2026.

TL;DR

  • Core NATS ofrece pub/sub en memoria con entrega como máximo una vez, request/reply y queue groups: es excelente para mensajería interna en vivo cuando los suscriptores están conectados.
  • JetStream (integrado en nats-server) agrega streams durables, replay, KV y semántica de entrega al menos una vez o exactamente una vez mediante configuración explícita: la respuesta de NATS al desacoplamiento temporal sin adoptar una plataforma independiente.
  • Kafka es un log de eventos distribuido: los topics están particionados, se retienen y permiten replay por diseño; el procesamiento de streams (Kafka Streams, Connect y su ecosistema) es la principal razón por la que los equipos lo adoptan.
  • SQS es una cola punto a punto administrada en AWS: no hay que operar un clúster, las colas estándar ofrecen entrega al menos una vez y las FIFO ofrecen procesamiento exactamente una vez cuando necesitas orden; no es un log ni ofrece pub/sub nativo.
  • Elige según los síntomas (consumidores desconectados, ventana de replay, forma del fan-out, tolerancia a la dependencia de un proveedor cloud y quién opera el clúster), no según clasificaciones de latencia sin fuentes.

Qué aprenderás aquí

  • En qué se diferencian NATS (Core + JetStream), Kafka y SQS en ocho dimensiones de ingeniería
  • Cuándo gana NATS, incluidos los patrones de la parte 1 (request/reply, queue groups), la parte 2 (streams/consumidores), la parte 3 (KV) y la parte 4 (infraestructura interna de conexión)
  • Cuándo gana Kafka: retención de logs, particiones y procesamiento de streams
  • Cuándo gana SQS: desacoplamiento nativo de AWS sin operar brokers
  • Un flujo de decisión en ASCII y tablas de síntoma → opción adecuada que puedes pegar en un ADR

Contexto de la serie: Parte 1 · Parte 2 · Parte 3 · Parte 4 · Parte 5

Tres primitivas diferentes

Antes de la tabla comparativa, aclaremos qué es cada sistema:

SistemaAbstracción principalModelo mental
Core NATSPub/sub basado en subjectsEnrutamiento sin esperar respuesta; los suscriptores deben estar conectados
JetStreamStream durable + consumidorCaptura, replay y entrega basada en ack sobre el transporte de NATS
KafkaLog de confirmación particionadoTopics de solo escritura al final; los consumidores registran offsets; la retención es un parámetro de primer nivel
SQSCola alojadaEl productor encola; un grupo de consumidores procesa; el mensaje se elimina después del ack
  Core NATS          JetStream                 Kafka                    SQS
  ─────────          ─────────                 ─────                    ───
  Pub ──► Sub        Pub ──► Stream ──► Cons  Pub ──► Topic (log)      Pub ──► Queue ──► Worker
  (live only)        (persist + replay)        ──► many consumers       (point-to-point)
                                               (offsets, retention)

SQS no es un sustituto directo de Kafka ni del pub/sub de NATS. AWS documenta SQS como un mecanismo de desacoplamiento mediante colas; para hacer fan-out a muchos suscriptores, normalmente se combina SNS → SQS (descripción general de SQS).

Dimensiones de comparación

Semántica de entrega

DimensiónCore NATSJetStreamKafkaSQS (estándar)SQS (FIFO)
Garantía predeterminadaComo máximo una vez, mejor esfuerzo (¿Qué es NATS?)Al menos una vez; la retención de tipo work-queue puede buscar el consumo exactamente una vez por mensaje (JetStream)Al menos una vez por defecto; un productor idempotente + transacciones permiten entrega exactamente una vez dentro de Kafka cuando se configuran (diseño de Kafka — semántica)Al menos una vez (tipos de cola de SQS)Procesamiento exactamente una vez con deduplicación (tipos de cola de SQS)
Suscriptor desconectadoSe pierde el mensajeReplay desde el streamLectura desde el log retenidoEl mensaje espera en la cola (hasta el límite de retención)Igual
Expectativa de idempotenciaDiseña siempre para ellaDiseña siempre para ellaNecesaria en rutas de entrega al menos una vezNecesaria (puede haber duplicados en colas estándar)Menor riesgo de duplicados; valida de todos modos

Nota de ingeniería: «Exactamente una vez» suele significar «exactamente una vez bajo los modelos de falla documentados». La documentación de Kafka aclara que la entrega de extremo a extremo exactamente una vez hacia sistemas externos requiere la cooperación de esos sistemas.

Modelo de persistencia

DimensiónCore NATSJetStreamKafkaSQS
AlmacenamientoSolo en memoria; nunca se escribe en disco (¿Qué es NATS?)Streams en archivo o memoria; límites de retención (max_age, max_bytes, política de descarte)Segmentos de log en los discos de los brokers; retención por topicAlmacenamiento redundante entre AZs; retención predeterminada de 4 días, máximo de 14 días (descripción general de SQS)
Archivo a largo plazoNoLimitado por los límites del streamSí: semanas/meses son habituales; según la documentación de diseño de Kafka, «almacenar datos durante mucho tiempo es perfectamente aceptable»No: el consumo elimina los mensajes; no es un log analítico
OrdenSin orden globalOrden por subject dentro de un streamOrden total por particiónColas FIFO: orden dentro de cada grupo de mensajes

Complejidad operativa

DimensiónNATS (autoalojado)Kafka (autoalojado)SQS
Proceso que operarUn solo binario nats-server; JetStream habilitado en la configuraciónClúster con varios brokers; ZooKeeper está obsoleto: modo KRaft en versiones modernasNinguno: API totalmente administrada
Modelo de escaladoClúster + replicación RAFT de JetStream (1–5 réplicas por stream)Particiones, brokers, ISR y rebalanceoAutomático; límites de tasa de la API por tipo de cola
Aspectos de operación continuaTLS, cuentas/JWT, ajuste de streams/consumidores, respaldo del almacenamiento de JetStreamPlanificación de particiones, lag de consumidores, disco de brokers, operación de conectores y actualizacionesIAM, redrive de DLQ, ajuste del visibility timeout y aumentos de cuota
Inferencia: carga para un equipo pequeñoMenor para Core NATS; moderada con JetStream HAMayor: asigna la responsabilidad a alguien con enfoque de plataformaMenor superficie operativa si ya estás en AWS

La documentación de NATS describe nats-server como de «menos de 20 MB» (¿Qué es NATS?). Eso habla de su tamaño de despliegue, no de su rendimiento.

Fan-out vs cola

PatrónCore NATSJetStreamKafkaSQS
Fan-out pub/subNativo: cada suscriptor recibe una copiaNativo: varios consumidores en un streamNativo: varios grupos de consumidores por topicNo nativo: usa fan-out de SNS hacia varias colas
Cola de trabajo / consumidores en competenciaQueue groupsRetención work-queue + consumidores de colaGrupo de consumidores con asignación de particionesUn consumidor recibe cada mensaje en curso (visibility timeout)
RPC request/replyPatrón de inbox integrado (parte 1)Posible, pero no es su caso idealPoco habitual: no es la API principalNo está soportado

Replay

CapacidadCore NATSJetStreamKafkaSQS
Replay posteriorNoSí: por secuencia, tiempo, último mensaje y velocidad de replay (JetStream)Sí: el consumidor restablece el offset; los eventos no se eliminan al leerlos (introducción a Kafka)No hay un replay significativo: los mensajes se consumen y se eliminan
Varias posiciones de lectura independientesN/AVarios consumidores por streamVarios grupos de consumidoresN/A (semántica de cola)
Reprocesar lentamente el tráfico de producciónNoPolítica de replay originalRestablecer offsets / crear un grupo de consumidoresNo aplica

Perfil de latencia (solo cualitativo)

La documentación oficial describe mecanismos, no clasificaciones de latencia entre proveedores. Razona de forma cualitativa:

FactorNATS CoreJetStreamKafkaSQS
RecorridoEnrutamiento por subject, en memoriaPersistir → replicar → entregarEscritura y lectura del log orientadas a lotesAPI HTTPS + almacenamiento administrado
Caso ideal típico (inferencia)Tráfico en vivo del plano de control, RPCEventos internos durables con retención moderadaPipelines de alto volumen con procesamiento por lotesTransferencia asíncrona de trabajos entre servicios de AWS
Qué no hacerAfirmar «X ms más rápido que Y» sin tus propios benchmarks en tu redIgualIgualIgual

En SQS, el polling frente al long polling y el visibility timeout afectan directamente la demora observada de extremo a extremo (ciclo de vida del mensaje): ajústalos antes de cambiar de broker.

Multi-cloud / portabilidad

DimensiónNATSKafkaSQS
Ejecución en cualquier entornoSí: binario en servidores físicos, VMs, K8s y edge (¿Qué es NATS?)Sí: autoadministrado o administrado por un proveedor (MSK, Confluent, Aiven, etc.)Solo AWS
Ofertas administradasSynadia NGS, varias nubesMuchasNativo de AWS
Acoplamiento al protocoloProtocolo de comunicación de NATSProtocolo de Kafka + amplio ecosistemaAWS SDK / API HTTP
Inferencia: costo de salidaBajo si es autoalojado; mayor si depende de un solo proveedor de NATS administradoModerado: el ecosistema genera dependenciaAlto: integración más profunda y migración directa más difícil

Modelo de costos (matiza cada afirmación)

Factor de costoNATS (autoalojado)Kafka (autoalojado)SQS
FacturaciónInfraestructura que pagas (CPU, disco, red): el servidor OSS no tiene licencia por mensajeIgual + a menudo mayor uso de disco para retenciónPrecio por solicitud; payload de 1 MiB = hasta 16 unidades de solicitud (precios de SQS)
Nivel gratuitoN/A (pagas tu infraestructura cloud)N/A1 millón de solicitudes/mes (precios de SQS)
Costos ocultosRéplicas HA, monitoreo, guardiasVMs para conectores, schema registry, clústeres más grandesKMS, transferencia entre regiones, cliente extendido de S3 para payloads grandes
InferenciaRentable con un volumen interno estable en K8s existenteRentable cuando la retención del log reemplaza otros pipelines de almacenamientoRentable para código de integración de AWS con picos de tráfico si el número de solicitudes se mantiene acotado

No extrapoles cuál es la opción «más barata» solo a partir de los precios de lista: modela las solicitudes, el tamaño del payload, los GB-mes de retención y el tiempo de ingeniería.

Cuándo gana NATS

NATS encaja cuando la mensajería es infraestructura interna —de servicio a servicio, de edge a cloud, plano de control— y quieres un runtime liviano que abarque tanto el pub/sub de Core como la durabilidad de JetStream.

SíntomaCómo encaja NATSReferencia de la serie / documentación
Los servicios necesitan RPC sin usar HTTP en todas partesRequest/reply de CoreParte 1
Los workers deben compartir la carga de un subjectQueue groups o streams work-queue de JetStreamParte 1, parte 2
Los suscriptores se desconectan brevemente; los eventos deben sobrevivirStreams de JetStream + consumidores durablesParte 2
Último estado de una entidad + notificaciones de cambioswatch de JetStream KVParte 3
Postgres es la fuente de verdad; el bus conecta con el buscador/warehouseCDC → KV → subjects de indexaciónParte 4
Mismo contexto de seguridad desde edge hasta cloudCuentas/JWT de NATS 2.0; un solo binario¿Qué es NATS?
Quieres persistencia sin un equipo independiente de «plataforma de streaming»JetStream integrado en nats-serverJetStream
  Internal mesh style (NATS sweet spot)

  API ──request/reply──► Authz svc
    │
    └──publish──► orders.created ──► [billing worker]
                              └──► [analytics projector]
         JetStream stream (optional durability)
              └──► KV mirror ──watch──► indexer

Cuándo NATS es una opción predeterminada menos conveniente (inferencia):

  • Analítica de varios equipos sobre meses de historial inmutable con herramientas SQL sobre streams
  • El estándar de la organización ya es Kafka Connect + Flink + schema registry
  • El equipo no tiene ninguna disposición para operar un broker de mensajes: SQS/Lambda puede ganar solo por razones operativas

Cuándo gana Kafka

Kafka encaja cuando el log en sí es el producto: ingesta de alto volumen, muchos consumidores posteriores a distintas velocidades, retención prolongada y procesamiento de streams.

SíntomaCómo encaja KafkaReferencia de la documentación
Muchos equipos leen el mismo topic desde distintos offsetsGrupos de consumidores + log retenidoIntroducción a Kafka — topics
Event sourcing / auditoría con replay meses despuésRetención configurable; diseño orientado a discoDiseño de Kafka — persistencia
Joins, ventanas y agregaciones de streams en la capa del busAPI de Kafka StreamsAPIs de Kafka
Ingesta desde Postgres, S3, Elasticsearch mediante conectores administradosKafka ConnectAPIs de Kafka — Connect
Localidad por clave de partición (orden por usuario)Mapeo de clave → particiónDiseño de Kafka — productor
Entrega exactamente una vez entre topics de Kafka en un pipelineProductor transaccional + read_committedSemántica de Kafka
  Log-centric analytics (Kafka sweet spot)

  Producers ──► topic (partitions P0..Pn, replicated)
                    │
        ┌───────────┼───────────┬──────────────┐
        ▼           ▼           ▼              ▼
   Warehouse    Flink job   Search sink    Audit archive
   (Connect)    (Streams)   (Connect)      (long retention)

Compromisos que debes aceptar: cantidad de brokers, planificación de particiones, dashboards de lag de consumidores y disciplina para las actualizaciones. La documentación de diseño de Kafka apunta a «streams de eventos de alto volumen» y «cargas periódicas de datos desde sistemas desconectados», no a RPC interno minimalista.

Cuándo gana SQS

SQS encaja cuando las cargas de trabajo son nativas de AWS, tienen forma de cola y quieres que AWS se encargue de la disponibilidad.

SíntomaCómo encaja SQSReferencia de la documentación
Desacoplar workers de Lambda, ECS y EC2Cola estándar + mapeo de origen de eventosDescripción general de SQS
No hay equipo para operar Kafka/NATSTotalmente administradoDescripción general de SQS
Es necesario inspeccionar mensajes fallidosDead-letter queues + redriveDLQ de SQS
Orden estricto por entidadFIFO + ID de grupo de mensajesTipos de cola de SQS
Fan-out a muchos workers en AWSSNS → varias colas SQSSQS vs SNS
Retención medida en días, no en mesesRetención predeterminada de 4d, máximo de 14dDescripción general de SQS
  AWS glue (SQS sweet spot)

  API Gateway / ALB ──► SQS ──► Lambda / ECS worker
                      │
                      └── on failure ──► DLQ ──► redrive / alarm

Cuándo SQS es una opción predeterminada menos conveniente (inferencia):

  • Pub/sub nativo con decenas de suscriptores activos en el mismo subject
  • Replay desde offsets históricos arbitrarios para nuevos consumidores
  • Despliegue principal multi-cloud o local
  • Request/reply entre microservicios internos (usa HTTP/gRPC o NATS Core en su lugar)

Flujo de decisión

                        Start: async communication needed
                                      │
                                      ▼
                    ┌─────────────────────────────────────┐
                    │ Must run outside AWS or multi-cloud? │
                    └─────────────────────────────────────┘
                          │ yes                    │ no
                          ▼                        ▼
              ┌───────────────────┐      ┌──────────────────────┐
              │ Need log retention │      │ Queue + AWS services  │
              │ + stream ecosystem│      │ only?                 │
              └───────────────────┘      └──────────────────────┘
                 │ yes      │ no              │ yes      │ no
                 ▼          ▼                 ▼          ▼
            ┌────────┐  ┌─────────────────────────┐   ┌────────┐
            │ Kafka  │  │ Subscribers often offline │   │ SQS    │
            │        │  │ or need replay/KV/RPC?    │   │ (+SNS  │
            └────────┘  └─────────────────────────┘   │ if fan-│
                              │ yes      │ no        │ out)   │
                              ▼          ▼           └────────┘
                         ┌─────────┐  ┌──────────┐
                         │JetStream│  │Core NATS │
                         │(+ Core  │  │(live fan-│
                         │ as RPC) │  │out, RPC) │
                         └─────────┘  └──────────┘

Implementación concreta: guía de selección basada en síntomas

Usa esta sección como apéndice de un ADR. Cada fila relaciona un síntoma en producción con una opción predeterminada razonable y el primer parámetro de configuración que debes verificar.

Síntoma → opción adecuada (patrón de mensajería)

SíntomaOpción predeterminada razonablePrimer parámetro que revisar
«Facturación debe cobrar exactamente una vez por ID de pedido»Stream work-queue de JetStream o SQS FIFO con deduplicación o productor idempotente de Kafka + consumidor idempotenteClave de idempotencia en tu handler en cualquiera de los casos
«El indexador de búsqueda puede retrasarse 30s; la API lee de Postgres»JetStream o Kafka para el flujo de cambios; no Core NATS por sí soloTiempo de espera del ack / máximo de entregas del consumidor (parte 2)
«10 microservicios necesitan el mismo evento en vivo sin esperar respuesta»Pub/sub de Core NATSSuscriptores simples vs de cola (parte 1)
«Un equipo nuevo quiere ejecutar SQL sobre 90 días de clics»Kafka (+ sink hacia un warehouse)Bytes / tiempo de retención del topic
«Lambda procesa cargas de archivos durante la noche»SQS estándarVisibility timeout ≥ duración p99 del handler
«El servicio A llama al servicio B de forma síncrona internamente»Request/reply de Core NATSTimeout + manejo de ErrNoResponders
«Compartir la configuración más reciente por región»JetStream KV + watchNúmero de réplicas del bucket (parte 3)
«Entrega exactamente una vez del topic A al topic B»Transacciones de Kafkaisolation.level=read_committed
«Fan-out a 50 colas SQS en AWS»SNS → SQSPolítica de cola por suscriptor

Síntoma → opción adecuada (restricciones operativas)

SíntomaInclínate porPor qué (con respaldo en fuentes o inferencia)
«No tenemos un SRE para mensajería»SQS en AWSServicio administrado (descripción general de SQS): inferencia sobre el tamaño del equipo
«Ya operamos K8s y queremos un solo binario pequeño»NATSUn solo proceso de servidor (¿Qué es NATS?)
«Cumplimiento exige un log de auditoría inmutable durante 1 año»KafkaModelo de retención (introducción a Kafka)
«Picos de millones/segundo y la facturación de AWS es aceptable»SQS estándarEscala sin aprovisionamiento (descripción general de SQS)
«Dispositivos edge + cloud en el mismo bus»NATSModelo de despliegue (¿Qué es NATS?): inferencia para edge

Ejemplos mínimos de integración (prueba básica de la elección)

NATS JetStream — evento durable de pedido (del contexto de la serie):

nats stream add ORDERS \
  --subjects "orders.>" \
  --storage file \
  --retention limits \
  --max-age 7d \
  --replicas 3

Kafka — topic con retención explícita (ilustrativo):

# server.properties / topic config
log.retention.hours=168
min.insync.replicas=2

SQS — visibility timeout ajustado al handler (AWS CLI):

aws sqs set-queue-attributes \
  --queue-url "$QUEUE_URL" \
  --attributes VisibilityTimeout=300

Si no puedes explicar qué fila de las tablas anteriores corresponde a tu síntoma, aún no estás listo para elegir un broker: define primero la garantía de entrega y la ventana de retención.

Arquitecturas híbridas (comunes en la práctica)

Los sistemas reales combinan herramientas:

PatrónComposición
Entrada en AWS, bus internoALB → SQS → servicio puente → NATS JetStream
Analítica a partir de eventos internosNATS JetStream → (conector) → Kafka → warehouse
Migración de sistemas heredados a cloudNATS local ↔ superclúster ↔ NATS en cloud; SQS solo en el límite de AWS

Las soluciones híbridas agregan puntos de integración operativos: documenta quién se encarga de los reintentos, la DLQ y el esquema en cada límite.

Errores comunes

  1. Elegir Kafka para RPC interno. Request/reply no es el caso ideal de Kafka; usa HTTP/gRPC o NATS Core.
  2. Elegir SQS para pub/sub. Usa fan-out de SNS o un bus con subjects/topics nativos.
  3. Usar Core NATS para eventos de facturación. Entrega como máximo una vez + suscriptores desconectados = pérdida silenciosa (¿Qué es NATS?).
  4. Esperar replay de SQS. Una vez eliminado un mensaje, desaparece: no es un log.
  5. Ignorar la idempotencia. La entrega al menos una vez es la opción predeterminada en casi todas partes.
  6. Elegir según benchmarks. El marketing de los proveedores y los artículos de blog desactualizados no representan la topología de tu red.

Cómo encaja esto en la serie

ParteEnfoque
1Primitivas de Core NATS
2Durabilidad de JetStream
3KV como stream de cambios
4Proyecciones CQRS con NATS como infraestructura de conexión
5Observabilidad y SLOs
6 (este artículo)Cuándo usar NATS vs Kafka vs SQS

Para conocer los niveles de la arquitectura (outbox, proyecciones, búsqueda), consulta Las piezas del diseño de software moderno y eficaz.

Fuentes