← Volver al blog
🪵 Arquitectura12 min de lectura·

Entendiendo Kafka: qué resuelve y cómo cambia tu arquitectura

Cuando alguien dice «necesitamos Kafka», la mitad de la sala asiente y la otra mitad finge que sabe de qué se habla. Y es entendible: Kafka se explica casi siempre con jerga —«broker», «topic», «offset», «streaming»— que no dice nada si no entiendes primero el problema que vino a resolver. La idea incómoda es esta: Kafka no es «una cola de mensajes más rápida». Es un cambio en cómo tus sistemas se hablan entre sí. En vez de que un servicio llame a otro y espere respuesta, los servicios publican hechos que ocurrieron y quien quiera se entera cuando pueda. Ese giro —de «pedir» a «enterarse»— es lo que cambia la arquitectura entera. Vamos de a poco.

1. El problema: el espagueti de las llamadas directas

Empecemos por el dolor, no por la solución.

Imagina un e-commerce. Cuando alguien compra, tienen que pasar varias cosas: cobrar, descontar stock, enviar un email, notificar a logística, actualizar analítica, sumar puntos de fidelidad. La forma «natural» de programarlo es que el servicio de Pedidos llame a cada uno:

Servicio dePedidosPagosStockEmailLogísticaAnalíticaFidelidadPedidos conoce y depende de los 6 — agregar uno nuevo obliga a modificar Pedidos
Acoplamiento en estrella: Pedidos conoce y depende de todos. Agregar un servicio nuevo obliga a modificar Pedidos.

Esto funciona… hasta que no. Los problemas aparecen solos:

  • Acoplamiento: Pedidos tiene que conocer a los 6 servicios, sus URLs, sus formatos. Si agregas uno nuevo (detección de fraude, por ejemplo), tienes que modificar Pedidos.
  • Fragilidad: si el servicio de Email está caído, ¿falla toda la compra? ¿Reintenta? ¿Cuántas veces? Cada llamada síncrona es un punto de falla.
  • Lentitud: si Pedidos espera respuesta de los 6, la compra es tan lenta como el servicio más lento.
  • Se pierde el dato: cuando Analítica estuvo caída media hora, esos eventos de compra se perdieron. No hay forma de «volver a reproducirlos».

El patrón se repite: N servicios que se llaman entre sí producen un grafo de dependencias que crece de forma cuadrática y se vuelve imposible de razonar. A esto se le suele llamar, con cariño, «arquitectura espagueti».

💡 La pregunta clave

¿Y si en vez de que Pedidos le avise a cada uno, simplemente anunciara al mundo que ocurrió una compra, y cada servicio interesado se enterara por su cuenta? Eso es exactamente lo que habilita Kafka.

2. La idea central: un registro de hechos (el log)

Kafka, en su corazón, es sorprendentemente simple de imaginar: es un cuaderno de bitácora (log) al que solo se le agregan líneas al final, y que muchos pueden leer a su propio ritmo.

Cada línea es un evento: «el pedido 1234 se pagó», «el usuario 88 se registró», «el sensor 5 marcó 40 °C». Los eventos se escriben una vez y se leen muchas veces, cada lector llevando su propia marca de «por dónde voy».

Productor(Servicio de Pedidos)Topic: pedidos (log — solo se agrega al final →)off 0pedido 1001off 1pedido 1002off 2pedido 1003off 3pedido 1004escribe al finalConsumidor: Emailva por el offset 3Consumidor: Analíticava por el offset 1leen a su ritmo — leer no borra
El log: los productores escriben al final; cada consumidor lee a su ritmo y recuerda su offset. Leer no borra el evento.

Tres propiedades que hacen la diferencia frente a una cola tradicional:

  • No se borra al leer. En una cola clásica (RabbitMQ, SQS), cuando consumes un mensaje, desaparece. En Kafka el evento permanece (según la retención configurada: 7 días, 30, o «para siempre»). Leerlo no lo consume.
  • Cada lector tiene su propia posición (offset). Email puede ir por el evento 1000 mientras Analítica recién procesa el 200. No se estorban.
  • Se puede «rebobinar». ¿Un servicio nuevo necesita procesar todo el histórico? Empieza desde el offset 0. ¿Un bug corrompió datos? Reprocesas desde ayer. Esto se llama replay y es imposible en una cola que borra al leer.

La analogía que sí funciona

Una cola de mensajes tradicional es como una lista de tareas en un pizarrón: haces la tarea y la borras. Kafka es como el diario del pueblo: publica los hechos, y cada vecino lo lee cuando quiere, subraya hasta dónde llegó, y puede volver a leer números anteriores. El diario no se «gasta» porque alguien lo leyó.

3. La arquitectura: topics, particiones, brokers y consumer groups

Ahora sí, la jerga —pero ya con intuición detrás.

Topics y particiones

Un topic es un canal con nombre: pedidos, pagos, clicks. Es el «cuaderno» del que hablábamos.

Pero un solo cuaderno no escala: si todo se escribe en un único log, un solo servidor tiene que aguantar toda la carga. Por eso Kafka parte cada topic en particiones. Cada partición es un log independiente y ordenado, y pueden vivir en máquinas distintas.

Productorkey = user_idTopic: pedidosPartición 0 │ e │ e │ e │ e │ →Partición 1 │ e │ e │ e │ →Partición 2 │ e │ e │ e │ e │ e │ →el orden está garantizado dentro de cada partición
Un topic se divide en particiones. La «key» del evento decide a qué partición va; el orden se garantiza dentro de cada partición.

Detalle importante: el orden solo está garantizado dentro de una partición, no en el topic completo. Por eso se usa una key (por ejemplo, user_id): todos los eventos del mismo usuario caen en la misma partición y se procesan en orden. Eventos de usuarios distintos pueden ir en paralelo.

Brokers y el clúster

Un broker es un servidor Kafka. Un clúster es un grupo de brokers que se reparten las particiones. Cada partición se replica en varios brokers (típicamente 3): uno es el líder (recibe escrituras) y los otros son réplicas (copias de respaldo). Si el líder muere, una réplica toma el mando. Así Kafka sobrevive a la caída de máquinas sin perder datos.

Clúster KafkaBroker 1P0 (líder)P1 (réplica)Broker 2P1 (líder)P2 (réplica)Broker 3P2 (líder)P0 (réplica)cada partición tiene un líder (recibe escrituras) y réplicas de respaldo — si el líder cae, una réplica toma el mando
Las particiones se reparten y replican entre brokers. Cada una tiene un líder y réplicas de respaldo.

Consumer groups: el escalado del lado que lee

Acá está una de las piezas más elegantes. Un consumer group es un conjunto de instancias del mismo servicio que se reparten las particiones para procesar en paralelo.

Regla de oro: Kafka asigna cada partición a UN solo consumidor dentro del grupo. Si tienes 3 particiones y 3 instancias, cada una toma una. Si agregas una 4ª instancia, esa se queda sin trabajo (no hay partición para ella). Si una instancia muere, Kafka reasigna su partición a otra viva (esto se llama rebalanceo).

Topic: pedidosPartición 0Partición 1Partición 2Consumer group: EmailInst. AInst. BInst. C1 partición por instancia (reparto)Consumer group: AnalíticaInst. únicalee las 3 particionescada grupo recibe TODOS los eventos, de forma independiente
El grupo «Email» reparte 3 particiones entre 3 instancias. El grupo «Analítica» (1 instancia) lee las 3. Ambos grupos reciben TODOS los eventos, de forma independiente.

Esto resuelve dos necesidades a la vez:

  • Distintos servicios (grupos distintos) reciben cada uno una copia completa del stream. Email y Analítica ven todos los pedidos.
  • Dentro de un servicio, varias instancias (mismo grupo) se reparten la carga para escalar horizontalmente.

4. Cómo se implementa: un vistazo al código

No hace falta escribir un servidor: se produce y se consume con librerías. En Python (con confluent-kafka):

Productor (Servicio de Pedidos):

from confluent_kafka import Producer
import json, time

producer = Producer({"bootstrap.servers": "kafka:9092"})

def publicar_pedido(pedido):
    producer.produce(
        topic="pedidos",
        key=str(pedido["user_id"]),   # misma key => misma partición => orden por usuario
        value=json.dumps({
            "evento": "pedido_pagado",
            "pedido_id": pedido["id"],
            "monto": pedido["monto"],
            "ts": time.time(),
        }),
    )
    producer.flush()

Consumidor (Servicio de Email):

from confluent_kafka import Consumer
import json

consumer = Consumer({
    "bootstrap.servers": "kafka:9092",
    "group.id": "email-service",       # el consumer group
    "auto.offset.reset": "earliest",   # si es nuevo, empieza desde el principio
})
consumer.subscribe(["pedidos"])

while True:
    msg = consumer.poll(1.0)
    if msg is None:
        continue
    evento = json.loads(msg.value())
    enviar_email_confirmacion(evento)  # hace su trabajo
    consumer.commit(msg)               # marca su offset: "ya procesé hasta acá"

Lo esencial: Pedidos no sabe que Email existe. Solo publica un hecho. Email decide suscribirse. Si mañana agregas un servicio de Fraude, escribes otro consumidor con group.id = "fraude-service" y no tocas Pedidos.

⚠️ Detalle que muerde: «al menos una vez»

Por defecto, si un consumidor procesa un evento pero muere antes de hacer commit del offset, al reiniciar lo procesará otra vez. Kafka garantiza «at least once» por defecto. Por eso tus consumidores deben ser idempotentes: procesar dos veces el mismo evento no debe cobrar dos veces. Hay modos «exactly once» pero tienen costo y complejidad.

5. Cómo cambia la arquitectura: de «pedir» a «enterarse»

Volvamos al e-commerce del inicio, ahora con Kafka en medio.

Servicio dePedidosTopic:pedidospublica: pedido_pagadoPagosStockEmailLogísticaAnalíticaFraude(nuevo — Pedidosni se entera)
Con Kafka en medio: Pedidos publica un hecho y cada servicio se suscribe por su cuenta. Agregar uno nuevo (Fraude) no toca a nadie.

El cambio de mentalidad es profundo:

Antes (llamadas directas)Después (eventos)
Pedidos ordena a cada servicio qué hacerPedidos anuncia que algo pasó
Acoplamiento: Pedidos conoce a todosDesacoplamiento: nadie conoce a nadie, solo al topic
Síncrono: espera a que todos respondanAsíncrono: publica y sigue
Si un servicio cae, la compra fallaSi un servicio cae, el evento lo espera en el log
Agregar un servicio = modificar PedidosAgregar un servicio = suscribir un consumidor nuevo
El dato se pierde si nadie lo recibióEl dato queda en el log y se puede reprocesar

Esto se conoce como arquitectura orientada a eventos (event-driven). El log de Kafka se vuelve la fuente de la verdad: en vez de que el estado viva escondido en la base de datos de cada servicio, el flujo de hechos es lo primario, y los estados se derivan de él (un patrón relacionado es event sourcing).

💡 No es magia gratis

El desacoplamiento tiene un precio: la lógica deja de estar en un solo lugar y se reparte entre productores y consumidores. Depurar «por qué no se envió el email» implica seguir un evento a través de varios servicios y offsets, no leer una función. La observabilidad (trazas, métricas de lag por consumer group) deja de ser opcional.

6. ¿Cuándo Kafka SÍ y cuándo NO?

Kafka es potente, pero no es la respuesta a todo. Un árbol de decisión honesto:

¿Varios servicios reaccionanal mismo hecho?Llamada HTTP directao cola simple bastaNo (1 a 1)¿Necesitas reprocesar oreleer el histórico?RabbitMQ / SQS(cola tradicional, más simple)No, volumen bajo¿Alto volumen ostreaming continuo?Kafka encaja:log duradero, replay, escala horizontal
Kafka brilla con múltiples consumidores, necesidad de replay y alto volumen; para lo simple, hay opciones más livianas.

Kafka encaja cuando…

  • Varios servicios necesitan reaccionar al mismo evento (fan-out).
  • Necesitas reproducir/reprocesar el histórico (nuevos consumidores, corrección de bugs, entrenar modelos).
  • Volumen alto y sostenido (millones de eventos, telemetría, clicks, IoT).
  • Quieres desacoplar servicios de verdad y que sobrevivan a caídas mutuas.

Kafka es sobre-ingeniería cuando…

  • Es una comunicación simple 1 a 1 entre dos servicios → una llamada HTTP o una cola simple.
  • El volumen es bajo y no necesitas replay → RabbitMQ o SQS son más fáciles de operar.
  • No tienes equipo para operar un clúster (aunque los servicios gestionados —Amazon MSK, Confluent Cloud— reducen mucho ese costo).

🚫 El anti-patrón clásico

Meter Kafka «porque es lo que se usa» en un sistema de 3 servicios y 100 eventos al día. Terminas operando un clúster distribuido para resolver un problema que una tabla y un cron resolvían. La complejidad de Kafka se paga; que valga la pena depende del problema.

7. Cierre: el cambio de chip

Si te llevas una sola idea: Kafka no es una tubería más rápida, es un cambio de quién manda a quién. Pasas de un mundo donde un servicio orquesta y ordena, a uno donde los servicios publican hechos y reaccionan de forma autónoma. Eso te da desacoplamiento, resiliencia y la capacidad de rebobinar el tiempo —a cambio de más piezas móviles y más disciplina en observabilidad.

Para un dev, significa dejar de pensar «¿a quién tengo que llamar?» y empezar a pensar «¿qué hecho estoy anunciando y quién querría enterarse?». Para un arquitecto, significa que el diagrama deja de ser un grafo de flechas entre servicios y se vuelve un río de eventos del que todos beben. Ese río, bien diseñado, es lo que permite que un sistema crezca sin convertirse en espagueti.

Para seguir leyendo

💡 Nota de transparencia

Artículo educativo introductorio sobre Apache Kafka y arquitectura orientada a eventos. Una implementación real puede variar en versiones, topología y garantías configuradas. Redactado con asistencia de IA y revisado por un humano.