Pub/sub es un patrón de mensajería donde los publicadores envían mensajes a topics en un broker, y cada suscriptor del topic recibe una copia. Todo su valor está en lo que las partes no saben: los publicadores nunca descubren quién escucha, los suscriptores nunca descubren quién envió — la indirección del broker los desacopla en el espacio (sistemas distintos), en el tiempo (los suscriptores pueden estar offline) y en la sincronización (el publish retorna de inmediato). Agrega suscriptores sin tocar a los publicadores; ese es el superpoder discreto del patrón.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| El elenco | Publicador → mensaje → topic (tema) → broker → suscriptores (cada uno recibe una copia) |
| vs. una cola | Una cola distribuye el trabajo a un consumidor; pub/sub lo duplica para todos |
| vs. observer | Observer es in-process y síncrono; pub/sub tiene broker y es asíncrono |
| La postura por defecto | Entrega at-least-once + handlers idempotentes |
| La letra chica | Sin respuestas, sin orden por defecto, sin transacciones atómicas a través del broker |
El patrón en código
Toda API de pub/sub se reduce a dos verbos, sea cual sea el broker:
// Suscriptores — registran interés en un topic
broker.subscribe('orders.paid', (msg) => fulfill(msg)); // servicio A
broker.subscribe('orders.paid', (msg) => notifyUser(msg)); // servicio B — su propia copia
// Publicador — dispara y olvida
broker.publish('orders.paid', { orderId: 'o-1187' });
// El publicador nunca sabe quién lo recibió — ni si alguien lo hizo.
La misma forma en el edge del cliente, donde una suscripción de live query es el topic y una escritura en la base de datos es el publish:
// JavaScript / Node.js — Back4app JS SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
const topic = new Parse.Query('Event');
topic.equalTo('topic', 'orders');
const sub = await topic.subscribe();
sub.on('create', (event) => handle(event.get('payload')));
// Publisher — fire and forget; no knowledge of subscribers
const event = new Parse.Object('Event');
event.set('topic', 'orders');
event.set('payload', { orderId: 'o-1187', status: 'paid' });
await event.save(); // Flutter / Dart — Back4app Flutter SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
final liveQuery = LiveQuery();
final topic = QueryBuilder<ParseObject>(ParseObject('Event'))
..whereEqualTo('topic', 'orders');
final sub = await liveQuery.client.subscribe(topic);
sub.on(LiveQueryEvent.create, (event) => handle(event.get('payload')));
// Publisher — fire and forget; no knowledge of subscribers
final event = ParseObject('Event')
..set('topic', 'orders')
..set('payload', {'orderId': 'o-1187', 'status': 'paid'});
await event.save(); // iOS / Swift — Back4app Swift SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
let topic = Event.query("topic" == "orders")
let sub = try await topic.subscribe()
sub.handleEvent { _, e in
if case .created(let event) = e { handle(event.payload) }
}
// Publisher — fire and forget; no knowledge of subscribers
var event = Event()
event.topic = "orders"
event.payload = ["orderId": "o-1187", "status": "paid"]
try await event.save() // Android / Kotlin — Back4app Android SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
val client = ParseLiveQueryClient.Factory.getClient()
val topic = ParseQuery.getQuery<ParseObject>("Event")
topic.whereEqualTo("topic", "orders")
val sub = client.subscribe(topic)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, event ->
handle(event.getJSONObject("payload"))
}
// Publisher — fire and forget; no knowledge of subscribers
val event = ParseObject("Event")
event.put("topic", "orders")
event.put("payload", JSONObject(mapOf("orderId" to "o-1187", "status" to "paid")))
event.save() Cómo funciona pub/sub
El broker hace cuatro trabajos: aceptar publishes y retornar de inmediato; emparejar cada mensaje con las suscripciones (por nombre de topic, jerárquicamente — orders.* — o por atributos de contenido); entregar una copia independiente por suscriptor, con retry según su nivel de garantía; y retener mensajes para suscriptores offline donde la durabilidad esté configurada — el desacoplamiento en el tiempo que deja a un servicio hacer deploy al mediodía y ponerse al día con lo que se perdió. Una nota de vocabulario que vale su frase: un mensaje es el sobre; un evento — “esto ocurrió, en pasado” — es lo que pub/sub suele cargar, y si carga solo el hecho o el estado completo es la distinción de Fowler entre event notification y event-carried state transfer.
Pub/sub vs. colas de mensajes vs. patrón observer
Las dos confusiones, resueltas en una tabla:
| Pub/sub | Cola de mensajes | Observer | |
|---|---|---|---|
| Entrega | Cada suscriptor recibe una copia | Exactamente un worker consume cada mensaje | El sujeto llama a cada observador |
| Propósito | Transmitir eventos | Distribuir trabajo | Reaccionar a estado in-process |
| Acoplamiento | Ninguno — mediado por el broker | Ninguno — mediado por la cola | Referencias directas a objetos |
| Timing | Asíncrono | Asíncrono | Generalmente síncrono |
| Frontera | Entre procesos/sistemas | Entre workers | Dentro de un proceso |
| Forma canónica | 1 evento → N reacciones | N jobs → M workers | 1 objeto → sus listeners |
La frase que sobrevive a la reunión: una cola distribuye el trabajo; pub/sub lo duplica. Y observer no es un “pub/sub pequeño” — la indirección del broker es exactamente lo que le falta al observer, y por eso refactorizar un observer hacia pub/sub es un cambio arquitectónico, no un renombre. En la práctica los brokers ofrecen ambas formas: los consumer groups convierten un topic en una cola por grupo (la jugada característica de Kafka), los exchanges de fanout convierten un sistema de colas en pub/sub.
Garantías de entrega: at-most-once vs. at-least-once vs. exactly-once
| Garantía | Modo de falla | Costo | Tu handler debe | Elígela cuando |
|---|---|---|---|---|
| At-most-once | Los mensajes pueden desaparecer | El más barato y rápido | Tolerar huecos | Datos efímeros: presencia, cotizaciones, métricas |
| At-least-once | Llegan duplicados | Acks + retries | Ser idempotente | El default para eventos de negocio |
| Exactly-once | Restringido, complejo | Dedup + coordinación | Aun así ser idempotente | Caminos estrechos donde el broker lo soporta |
Los niveles de QoS 0/1/2 de MQTT mapean exactamente a estos tres — una confirmación elegante de que la tricotomía es fundamental, no marketing de proveedor. La verdad de ingeniería: exactly-once de extremo a extremo es inalcanzable en el caso general (el problema de los Dos Generales con gafete de broker); los sistemas que lo anuncian combinan entrega at-least-once con deduplicación. Por eso el contrato real del patrón está escrito en tu handler: at-least-once + idempotencia — deduplica por ID de mensaje, usa upserts con clave en la entidad o verifica la versión antes de aplicar — convierte los duplicados de bugs en no-ops.
Ordenamiento, dead letters y diseño de topics
Tres realidades operativas que los diagramas introductorios se saltan. Ordenamiento: el fan-out hacia consumidores paralelos no tiene orden global; los brokers lo restauran por partición o clave de ordenamiento al precio del paralelismo — diseña handlers que apliquen eventos por ID y versión de la entidad, no por secuencia de llegada, y el problema casi se disuelve. Mensajes venenosos: un mensaje cuyo procesamiento siempre lanza error reintentará para siempre bajo at-least-once; limita los intentos de entrega y enruta las fallas a una cola de dead-letter para triaje, o un evento malo atasca el topic. Diseño de topics: nombra jerárquicamente (orders.paid, orders.refunded, comodín orders.*), mantén la granularidad en el nivel donde los suscriptores de verdad difieren y evoluciona los esquemas de mensaje de forma aditiva — los suscriptores ignoran campos desconocidos; las rupturas reciben un nuevo topic versionado (orders.v2) con ventana de migración, exactamente como una versión de API.
Casos de uso comunes
- Integración de microservicios — servicios de equipos distintos reaccionando a los eventos de los demás sin llamadas directas ni calendarios de deploy compartidos.
- Funciones en tiempo real en el cliente — fan-out de chat, dashboards en vivo, presencia: pub/sub sobre WebSockets en el edge.
- Telemetría de IoT — miles de dispositivos publicando por MQTT; consumidores desde alertas hasta analytics suscribiéndose de forma independiente.
- Invalidación de caché y replicación — una escritura publicada, cada caché y réplica notificadas.
- Pipelines de procesamiento paralelo — un evento disparando fulfillment, notificación y analytics a la vez, cada uno a su propio ritmo.
¿Deberías usar pub/sub? Matriz de decisión
| Pub/sub encaja cuando… | Prefiere otra cosa cuando… |
|---|---|
| Un evento interesa a varios consumidores independientes | Un solo consumidor: una cola, o simplemente una llamada |
| Productores y consumidores despliegan por separado | Quien llama necesita la respuesta ya → request-response |
| La consistencia eventual es aceptable | Se exige atomicidad entre las partes → transacciones/Saga |
| Los suscriptores entran y salen con el tiempo | El orden global estricto es innegociable |
| La carga con picos necesita buffer entre sistemas | El sistema entero cabe en un proceso → observer |
Limitaciones y trade-offs
- El debug atraviesa un broker. “¿Quién consumió esto y por qué falló?” exige IDs de correlación y tracing; el desacoplamiento que libera a los equipos también esconde la causalidad.
- Sin respuestas, por diseño. Los flujos que necesitan respuesta deben modelarla como mensajes adicionales — latencia extra y máquinas de estado donde antes bastaba una llamada de función.
- El broker es infraestructura. Disponibilidad, capacidad, seguridad y políticas de retención de la capa de mensajería se vuelven trabajo de plataforma; un broker caído es un multiplicador de outages.
- La ceguera del publicador corta por ambos lados. Los publicadores no observan la salud de los suscriptores; un consumidor que falla en silencio pierde datos, a menos que exista monitoreo de dead-letter.
- Las garantías cuestan throughput. Las claves de ordenamiento serializan, exactly-once coordina, la durabilidad persiste — cada refuerzo de semántica gasta performance; compra solo lo que los datos necesitan.
Pub/Sub en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. El patrón aparece aquí en el edge del cliente: las Live Queries son pub/sub donde la query es el topic — los suscriptores registran un predicado, cualquier escritura que coincida se publica a cada suscriptor por una flota gestionada de WebSockets, con permisos verificados por suscriptor y fan-out por cuenta de la plataforma, como en las pestañas de código de arriba. Las reacciones server-side se componen igual: los triggers de Cloud Code se suscriben a cambios de datos (afterSave como suscripción a un topic en espíritu), y las funciones programadas drenan el trabajo publicado como filas. Para streaming pesado entre servicios todavía buscarías un broker dedicado como Kafka o RabbitMQ — pero, para los casos comunes de producto, el transmitir-al-cambiar ya viene con el backend.
Preguntas frecuentes
¿Qué es pub/sub en términos simples?
Un patrón de mensajería donde los remitentes (publicadores) publican mensajes en topics con nombre en un broker, y cada componente que se suscribió a ese topic recibe su propia copia — publicadores y suscriptores nunca saben unos de otros. Piensa en la radio: la estación transmite en una frecuencia; quien sintonice, la escucha.
¿Cuál es la diferencia entre pub/sub y una cola de mensajes?
Una cola distribuye el trabajo; pub/sub lo duplica. En una cola, cada mensaje lo consume exactamente un worker — la herramienta para repartir jobs entre un pool. En pub/sub, cada suscriptor del topic recibe una copia — la herramienta para transmitir eventos a interesados independientes.
¿Cuál es la diferencia entre pub/sub y el patrón observer?
Observer es in-process: el sujeto guarda referencias directas a sus observadores y normalmente los llama de forma síncrona. Pub/sub inserta un broker entre las partes, dejándolas totalmente desacopladas, típicamente asíncronas y capaces de vivir en procesos, lenguajes y despliegues distintos. Uno es un patrón de diseño dentro de una app; el otro es una arquitectura entre sistemas.
¿Qué es un topic en pub/sub?
Un canal lógico con nombre que categoriza mensajes: los publicadores se dirigen al topic, y el broker entrega cada mensaje a todos los suscriptores de ese topic. El filtrado puede ser por topic (suscribirse por nombre, a menudo jerárquicamente — orders.*) o por contenido, donde el broker compara atributos del mensaje.
¿Qué garantías de entrega ofrece pub/sub?
Tres niveles. At-most-once: dispara y olvida — rápido, puede perder mensajes. At-least-once: reintenta hasta confirmar — el default común, produce duplicados ocasionales. Exactly-once: deduplicación más coordinación — caro, restringido e imposible de garantizar de extremo a extremo en el caso general. La postura práctica: entrega at-least-once con handlers idempotentes.
¿Pub/sub garantiza el orden de los mensajes?
No por defecto — los mensajes se abren hacia suscriptores paralelos, y el orden global pelea contra el paralelismo. Los brokers pueden ordenar dentro de una partición o clave de ordenamiento, a costa de throughput. El diseño robusto asume orden solo donde se configuró explícitamente y escribe handlers que toleran llegadas fuera de orden.
¿Kafka es pub/sub?
Sí, con un matiz: Apache Kafka implementa la semántica de publicación-suscripción sobre un commit log particionado, durable y con replay. Dentro de un consumer group se comporta como cola (cada mensaje va a un miembro); entre groups, como pub/sub (cada group recibe una copia) — por eso suele llamarse event streaming.
¿Cuándo no deberías usar pub/sub?
Cuando quien llama necesita una respuesta inmediata (request-response), cuando las operaciones deben ser atómicas entre productor y consumidores (un broker no entra en una transacción — mira el patrón Saga), cuando el orden global estricto es obligatorio, o cuando el sistema es tan pequeño que un broker agrega más superficie operativa de la que quita.