Respuesta A: Anthropic Claude Sonnet 5
Diseño de un Sistema de Notificaciones en Tiempo Real para una Aplicación de Redes Sociales
1. Repaso de Requisitos y Estimación Preliminar
Estimación de Tráfico:
- 10M Usuarios Activos Diarios (DAU) × 20 notificaciones/día = 200M notificaciones/día
- Tasa promedio = 200M / 86.400s ≈ 2.315 notificaciones/segundo
- Tasa pico (5x) ≈ 11.500 notificaciones/segundo
- Con 100M DAU (futuro): promedio ~23.000/s, pico ~115.000/s
Estimación de Almacenamiento:
- Cada registro de notificación ~500 bytes (metadatos + carga útil)
- Almacenamiento diario: 200M × 500B = 100 GB/día → ~36 TB/año (antes de compres...
Mostrar respuesta completa ▼
Diseño de un Sistema de Notificaciones en Tiempo Real para una Aplicación de Redes Sociales
1. Repaso de Requisitos y Estimación Preliminar
Estimación de Tráfico:
- 10M Usuarios Activos Diarios (DAU) × 20 notificaciones/día = 200M notificaciones/día
- Tasa promedio = 200M / 86.400s ≈ 2.315 notificaciones/segundo
- Tasa pico (5x) ≈ 11.500 notificaciones/segundo
- Con 100M DAU (futuro): promedio ~23.000/s, pico ~115.000/s
Estimación de Almacenamiento:
- Cada registro de notificación ~500 bytes (metadatos + carga útil)
- Almacenamiento diario: 200M × 500B = 100 GB/día → ~36 TB/año (antes de compresión/archivo)
- El requisito de historial (últimas 100 por usuario) es un patrón de acceso "en caliente" con muchas lecturas, lo que favorece un diseño donde las notificaciones recientes sean baratas de obtener (por ejemplo, lista limitada por usuario) mientras que el historial completo va a almacenamiento frío.
Esto confirma que estamos ante un sistema con muchas escrituras, con mucho fan-out, sensible a la latencia que debe desacoplar la ingesta de la entrega.
2. Arquitectura de Alto Nivel
[Productores de Eventos] → [Bus de Eventos / Kafka] → [Servicio de Notificaciones (Consumidores)]
│
┌───────────────────────────┼───────────────────────────┐
▼ ▼ ▼
[Servicio de Preferencias] [Renderizado/Plantillas] [Limitador de Tasa/Deduplicación]
│ │ │
└───────────────┬────────────┴───────────────────────────┘
▼
[Distribuidor / Enrutador de Entrega]
┌─────────────┬─────────────┬─────────────┐
▼ ▼ ▼ ▼
[Servicio Push] [Gateway WebSocket/ [Servicio de Email] [Escritura en Tienda In-App - DynamoDB/Cassandra]
(FCM/APNs) SSE]
│ │
[Dispositivos Móviles][Clientes Conectados]
Flujo:
- Un sistema de origen (por ejemplo, Servicio de "Me gusta", Servicio de Comentarios, Servicio de Seguimiento, Servicio de Mensajería) emite un evento (por ejemplo,
user_liked_post) a un bus de mensajes duradero (Kafka). - El Orquestador de Notificaciones consume estos eventos, verifica las preferencias del usuario, aplica lógica de deduplicación/limitación de tasa/agrupación (por ejemplo, "A John y otras 5 personas les gustó tu publicación") y genera un objeto de notificación.
- El orquestador persiste la notificación (para el historial) y la envía a un Distribuidor de Entrega, que realiza el fan-out a los canales apropiados según la preferencia del usuario y el estado del dispositivo (en línea vs. fuera de línea).
- Los trabajadores de entrega manejan la transmisión real: proveedores de notificaciones push (FCM para Android, APNs para iOS), WebSocket/SSE para actualizaciones de insignias en tiempo real dentro de la aplicación y correo electrónico a través de un proveedor de correo electrónico transaccional.
3. Componentes Clave
3.1 Capa de Ingesta de Eventos — Apache Kafka
- Todos los servicios de origen publican eventos en temas de Kafka (particionados por
user_idpara preservar el orden por usuario). - Kafka proporciona durabilidad (factor de replicación 3), alto rendimiento y almacenamiento en búfer natural durante picos de tráfico — crítico ya que el tráfico pico es 5 veces el promedio.
- Temas:
notification.likes,notification.comments,notification.followers,notification.messages(o un solo tema con campo de tipo de evento, dependiendo de las necesidades de evolución del esquema).
¿Por qué Kafka en lugar de SQS/RabbitMQ? Kafka maneja un rendimiento muy alto con una sobrecarga baja por mensaje y admite la repetición (útil para reprocesar lotes fallidos o rellenar datos). SQS es más simple operacionalmente pero más difícil de escalar a >100k msg/s de manera rentable y no admite semánticas de repetición de grupos de consumidores de forma tan limpia.
3.2 Servicio Orquestador de Notificaciones
- Consumidor sin estado que lee de Kafka.
- Responsabilidades:
- Verificación de preferencias: Consulta una base de datos clave-valor rápida (Redis o DynamoDB) para obtener la configuración de notificaciones del usuario antes de procesar más.
- Deduplicación/Agrupación: Utiliza una ventana de agregación de corta duración (por ejemplo, conjuntos ordenados de Redis con TTL) para agrupar eventos similares (por ejemplo, varios "me gusta" en la misma publicación en 60 segundos se convierten en una notificación).
- Fan-out para seguidores: Para eventos como "nueva publicación de alguien a quien sigues", esto podría requerir un fan-out a millones de seguidores (problema de celebridades). Utiliza un modelo de fan-out híbrido:
- Fan-out en escritura para usuarios normales (envía a la fuente de notificaciones de cada seguidor inmediatamente).
- Fan-out en lectura para celebridades/cuentas con muchos seguidores (calcula en el momento de la lectura para evitar una tormenta de escrituras).
- Escalable horizontalmente — escala las instancias de consumidor según el número de particiones de Kafka y el rezago.
3.3 Almacén de Notificaciones (Capa de Persistencia)
- Almacén principal: Una base de datos NoSQL de columnas anchas como Apache Cassandra o DynamoDB, particionada por
user_id, agrupada/ordenada portimestamp(descendente).- Este modelo es ideal porque el patrón de acceso dominante es "obtener las últimas 100 notificaciones para el usuario X", lo cual es una consulta de rango simple en una partición — sin necesidad de uniones.
- Cassandra ofrece consistencia ajustable y escalabilidad horizontal mucho más allá de 100 millones de usuarios; DynamoDB ofrece una alternativa totalmente administrada con menos sobrecarga operativa (contrapartida: mayor costo a escala muy grande y riesgo de partición caliente para usuarios extremadamente activos a menos que las claves de partición estén saladas).
- TTL/Archivo: Conserva solo notificaciones recientes (por ejemplo, 30-90 días) en el almacén caliente; datos más antiguos archivados en almacenamiento más barato (S3 + Glacier) para cumplimiento/auditoría, con el límite "últimas 100" aplicado en el momento de la escritura (una lista limitada por usuario, o recortada mediante compactación periódica).
3.4 Distribuidor de Entrega
- Lee el objeto de notificación finalizado y determina los canales a usar según:
- Preferencias de canal del usuario (push/email/ambos/ninguno)
- Estado en línea del usuario (rastreado a través de un servicio de presencia respaldado por Redis, actualizado por WebSocket/heartbeat)
- Dirige a:
- Servicio de Notificaciones Push: Se integra con FCM (Android) y APNs (iOS). Se envuelve en una capa de abstracción interna para normalizar reintentos, formatos de carga útil y gestión de tokens de dispositivo (tokens almacenados en una tabla
user_devices, actualizados al iniciar la aplicación). - Entrega en Tiempo Real Dentro de la Aplicación: Para usuarios conectados activamente a través de WebSocket/SSE, envía directamente a una gateway de conexión (por ejemplo, un conjunto de servidores WebSocket detrás de un balanceador de carga, usando algo como Socket.IO o un servicio administrado como AWS API Gateway WebSockets). El mapeo conexión-servidor se rastrea en Redis para que cualquier nodo distribuidor pueda encontrar qué instancia de gateway tiene la conexión de un usuario.
- Servicio de Email: Para notificaciones menos sensibles al tiempo (por ejemplo, resumen semanal) o como respaldo para usuarios fuera de línea en ciertos tipos de notificaciones, se integra con un proveedor como Amazon SES o SendGrid, utilizando una cola separada de menor prioridad ya que los SLAs de correo electrónico son más relajados (segundos a minutos está bien).
- Servicio de Notificaciones Push: Se integra con FCM (Android) y APNs (iOS). Se envuelve en una capa de abstracción interna para normalizar reintentos, formatos de carga útil y gestión de tokens de dispositivo (tokens almacenados en una tabla
3.5 Servicio de Preferencias
- Servicio simple respaldado por una base de datos relacional (Postgres) o DynamoDB, almacenado en caché agresivamente en Redis (las preferencias cambian con poca frecuencia, se leen con mucha frecuencia — candidato perfecto para caché).
- Esquema:
user_id, notification_type, channel, enabled.
4. Modelo de Datos
Tabla de Notificaciones (Cassandra/DynamoDB)
Clave de Partición: user_id
Clave de Agrupación: notification_id (UUID basado en tiempo, ordenado descendentemente)
Atributos:
- type (like, comment, follow, message)
- actor_id (quién lo activó)
- actor_ids (array, para notificaciones agrupadas)
- target_object_id (post_id, comment_id, etc.)
- message_preview
- created_at
- read_status (booleano)
- delivered_channels (array: push, email, in-app)
Tabla de Preferencias de Usuario
Clave de Partición: user_id
Atributos: { likes: {push: true, email: false}, comments: {...}, follows: {...}, messages: {...} }
Tabla de Tokens de Dispositivo
Clave de Partición: user_id
Clave de Agrupación: device_id
Atributos: platform (ios/android), token, last_active
5. Garantía de Fiabilidad ("No se pierde ninguna notificación")
- Mensajería duradera: Kafka con factor de replicación ≥3 y
acks=allen los productores garantiza que los eventos no se pierdan antes de su procesamiento. - Procesamiento al menos una vez con idempotencia: Los consumidores pueden reprocesar en caso de fallo/reinicio, por lo que los IDs de notificación se generan de forma determinista (por ejemplo, hash del ID del evento de origen + tipo) para permitir escrituras idempotentes — evita notificaciones duplicadas en reintentos.
- Colas de mensajes fallidos (DLQ): Las entregas fallidas (por ejemplo, tiempo de espera del proveedor push) van a un tema DLQ con reintento de retroceso exponencial (por ejemplo, 3 reintentos con fluctuación), luego a revisión manual/alerta si aún fallan.
- Escribir la notificación en el almacén ANTES de intentar la entrega: Esto desacopla "la notificación existe" (durabilidad/historial) de "la notificación se entregó" (mejor esfuerzo en tiempo real). Incluso si la entrega push falla, el usuario la verá la próxima vez que abra la aplicación y consulte la API de notificaciones.
- Patrón Outbox en servicios de origen: Para evitar problemas de doble escritura (escritura en DB + publicación de eventos), utiliza el patrón outbox transaccional para que, cuando se registre un "me gusta" en la DB del servicio de origen, el evento se publique garantizado en Kafka a través de una herramienta de captura de datos de cambios (CDC) como Debezium.
6. Estrategia de Escalabilidad (10M → 100M DAU)
- Kafka: Aumentar el número de particiones (particionadas por hash del
user_id) — escala linealmente con más brokers/consumidores. - Orquestador y Distribuidor de Notificaciones: Sin estado, escalable horizontalmente detrás de grupos de consumidores; escala a través de HPA de Kubernetes según el rezago del consumidor de Kafka.
- Cassandra: Agregar nodos al anillo; el hashing consistente distribuye la carga automáticamente. Vigilar las particiones calientes por contenido viral/cuentas de celebridades — mitigar mediante agrupamiento (por ejemplo, dividir el fan-out de una celebridad en varias claves de partición).
- Gateway WebSocket: Escalar horizontalmente; usar sesiones pegajosas a través de un registro de conexiones en Redis para que los distribuidores sepan qué nodo de gateway posee qué conexión, independientemente de cuántos nodos de gateway existan.
- Proveedores Push/Email: Estos son servicios administrados por terceros (FCM, APNs, SES) que escalan independientemente; nuestra responsabilidad es la agrupación y la limitación de tasa para mantenernos dentro de las cuotas del proveedor.
- Caché: El almacenamiento en caché agresivo de Redis de preferencias y datos de presencia reduce la carga de la DB a medida que el número de usuarios crece 10 veces.
7. Compromisos Clave
| Decisión | Compromiso |
|---|---|
| Kafka vs. cola más simple (SQS) | Kafka agrega complejidad operativa (necesita experiencia dedicada en operaciones, gestión de ZooKeeper/KRaft) pero proporciona un rendimiento muy superior y capacidad de repetición necesarios a esta escala. |
| Cassandra vs. DynamoDB | Cassandra ofrece más control y potencialmente menor costo a escala muy grande pero requiere operaciones internas; DynamoDB está totalmente administrado (más rápido de construir, menos carga operativa) pero puede resultar caro a escala extrema y tiene restricciones más estrictas de tamaño de elemento/partición de rendimiento. |
| Fan-out en escritura vs. fan-out en lectura | El fan-out en el momento de la escritura proporciona menor latencia de lectura (ideal para el SLA de 2 segundos) pero corre el riesgo de una tormenta de escrituras "rebaño" para celebridades; el fan-out en lectura evita eso pero agrega latencia y cómputo en el momento de la lectura. El enfoque híbrido equilibra ambos pero agrega complejidad de diseño/código (dos rutas de código). |
| Almacenar historial completo vs. 100 notificaciones recientes en caliente + archivo | Reduce los costos de almacenamiento en caliente y mantiene las consultas rápidas, pero requiere una ruta de archivo/recuperación para cumplimiento o funciones de "cargar más", lo que agrega complejidad. |
| Entrega al menos una vez + idempotencia vs. exactamente una vez | Las semánticas de "exactamente una vez" en sistemas distribuidos son costosas/complejas (requiere consumidores transaccionales); "al menos una vez" + escrituras idempotentes logran la misma garantía práctica (sin notificaciones visibles duplicadas) a un costo operativo mucho menor. |
| Push en tiempo real para todo vs. agrupación/agregación | Agrupar notificaciones similares (por ejemplo, "10 personas le dieron me gusta a tu foto") reduce la fatiga de notificaciones y el volumen de entrega, mejorando tanto la experiencia del usuario como el costo, a expensas de una lógica de orquestación ligeramente más compleja y un pequeño retraso de almacenamiento en búfer (aún dentro del SLA de 2 segundos si la ventana es corta, por ejemplo, retención máxima de 5-10 segundos antes de vaciar). |
8. Monitoreo y Alertas
- Rastrea el rezago del consumidor de Kafka, la tasa de éxito/fallo de entrega por canal, la latencia de extremo a extremo P50/P95/P99 (ingesta de eventos → entrega al dispositivo) y la profundidad de la DLQ.
- Los paneles de SLA alertan al personal de guardia si la latencia P95 excede los 2 segundos o si el crecimiento de la DLQ indica un fallo de entrega sistémico (por ejemplo, interrupción de FCM), lo que activa la lógica de failover automático (por ejemplo, cola de espera + reintento con retroceso en lugar de descartar).
Resumen
El diseño desacopla la generación de notificaciones (basada en eventos, basada en Kafka, idempotente) de la entrega (distribuidor multicanal con reintento/retroceso por canal), respaldado por un almacén de columnas anchas optimizado para el patrón de acceso "historial reciente por usuario". Esta separación es lo que permite que el sistema cumpla con el SLA de latencia de 2 segundos garantizando la no pérdida de datos, y escala horizontalmente en cada nivel (ingesta, procesamiento, almacenamiento, entrega) para soportar 10 veces el crecimiento futuro con una sobrecarga operativa y de costos manejable.
Resultado
Votos ganadores
0 / 3
Puntuación media
Puntuación total
Comentario general
La Respuesta A es una propuesta de diseño de sistema sólida con estimaciones de capacidad precisas, una arquitectura lógica basada en eventos, el uso apropiado de Kafka, Cassandra/DynamoDB, Redis, WebSockets, APNs/FCM y proveedores de correo electrónico, y una buena cobertura de persistencia, preferencias, reintentos, DLQs, idempotencia, monitoreo y compensaciones. Sus principales debilidades son que algunas áreas son menos precisas de lo que podrían ser, como el límite exacto de latencia, la estrategia de HA/DR, los detalles de la ruta de lectura/API, la priorización de sobrecarga y los matices de las semánticas de entrega en torno a los proveedores externos.
Ver detalle de evaluación ▼
Calidad de la arquitectura
Peso 30%La Respuesta A presenta una arquitectura coherente con productores de eventos, Kafka, orquestación de notificaciones, verificación de preferencias, almacenamiento, distribuidores, entrega de WebSocket, proveedores de push y trabajadores de correo electrónico. El flujo es lógico y completo, aunque algunos detalles de la separación de comandos de canal/ruta de lectura/API son menos explícitos.
Integridad
Peso 20%La Respuesta A cubre los requisitos principales: tipos de notificación, entrega casi en tiempo real, canales de push/correo electrónico/en la aplicación, historial de los últimos 100, preferencias, escalabilidad, confiabilidad, costo, monitoreo y modelos de datos. Es algo más ligera en detalles de API, seguridad/privacidad, recuperación ante desastres y comportamiento operativo exacto durante sobrecargas o interrupciones de proveedores.
Análisis de compromisos
Peso 20%La Respuesta A incluye una tabla útil de compensaciones que cubre Kafka vs. SQS, Cassandra vs. DynamoDB, fan-out-on-write vs. fan-out-on-read, almacenamiento en caliente limitado vs. archivo, at-least-once vs. exactly-once, y entrega por lotes vs. en tiempo real. El razonamiento es sólido, aunque algunas compensaciones se resumen en lugar de estar estrechamente ligadas a las consecuencias operativas.
Escalabilidad y fiabilidad
Peso 20%La Respuesta A ofrece sólidos mecanismos de escalabilidad y confiabilidad: replicación y reproducción de Kafka, escrituras idempotentes, DLQs, reintentos, patrón outbox, escalado horizontal, particionamiento de Cassandra/DynamoDB, caché de Redis y escalado de WebSocket. Es menos detallada en la recuperación multirregión, la priorización de sobrecarga, la disciplina de offset del consumidor, los límites de entrega del proveedor y las semánticas exactas de SLO.
Claridad
Peso 10%La Respuesta A está claramente estructurada, es fácil de seguir y utiliza diagramas, viñetas, esquemas y una tabla concisa de compensaciones de manera efectiva. Comunica el diseño de manera eficiente con una ambigüedad mínima.
Puntuación total
Comentario general
La respuesta A es una propuesta de diseño pulida y bien estructurada que se leería bien en una entrevista. Clava las matemáticas de capacidad, proporciona un diagrama de arquitectura legible, modelos de datos concretos, una tabla de compensaciones clara y cubre el patrón outbox, la idempotencia, las DLQ y la escalabilidad horizontal en todos los niveles. Sus debilidades son la profundidad y la amplitud en algunas áreas: no hay diseño de API/ruta de lectura, no hay discusión sobre seguridad o privacidad, no hay topología de alta disponibilidad o recuperación ante desastres, no hay aislamiento de prioridad entre las clases de notificación y acepta sin críticas el SLA de extremo a extremo de 2 segundos sin notar que los proveedores de terceros lo hacen inaplicable. La sugerencia de fan-out-on-read también es un ajuste un tanto incómodo para una bandeja de entrada de notificaciones.
Ver detalle de evaluación ▼
Calidad de la arquitectura
Peso 30%Presenta una arquitectura en capas clara con un diagrama ASCII, bus de eventos (Kafka), orquestador, servicio de preferencias, despachador, puerta de enlace WebSocket, trabajadores push/email y un almacén de columnas anchas. El flujo es fácil de seguir y los componentes están bien delimitados. Pequeñas debilidades: la discusión de fan-out-on-read se aplica de forma algo incorrecta a las notificaciones (es un concepto de feed), y la ruta de API/lectura más la capa de API gateway apenas se abordan a pesar de ser señaladas en la política de evaluación.
Integridad
Peso 20%Cubre la estimación, los cuatro tipos de notificación, los canales push/email/in-app, el historial a través de listas limitadas más archivo, preferencias con esquema, tokens de dispositivo, fiabilidad, escalabilidad y monitorización. Ausente o escaso: diseño de API/ruta de lectura, seguridad y privacidad, recuperación ante desastres y estrategia multirregión, recuentos de no leídos y topología explícita de alta disponibilidad (distribución de AZ).
Análisis de compromisos
Peso 20%Una tabla de compensaciones dedicada cubre Kafka vs SQS, Cassandra vs DynamoDB, fan-out en escritura vs lectura, almacenamiento en caliente vs archivo, at-least-once vs exactly-once, y batching vs inmediatez. Cada entrada nombra tanto el beneficio como el costo, lo cual es claro y legible. Sin embargo, las compensaciones son en su mayoría convencionales y se exponen brevemente, con menos profundidad en los límites de consistencia, las garantías de ordenación o los matices de la definición de SLO.
Escalabilidad y fiabilidad
Peso 20%Sólido: particionamiento y replicación de Kafka, acks=all, at-least-once con IDs idempotentes deterministas, DLQ con retroceso exponencial y jitter, write-before-deliver, outbox transaccional con Debezium, HPA en lag del consumidor, expansión del anillo de Cassandra, salting de particiones calientes y registro de conexiones Redis. Carece de alta disponibilidad explícita multiaz/multirregión, procedimientos de DR/failover, aislamiento de contrapresión o prioridad entre clases de notificación, y semántica de commit de offset.
Claridad
Peso 10%Excelente legibilidad: secciones numeradas, un diagrama de arquitectura ASCII, bloques de código para el modelo de datos, una tabla de compensaciones y un resumen conciso al final. Fácil de hojear y rápido de comprender el diseño de un vistazo.
Puntuación total
Comentario general
La Respuesta A proporciona un diseño de sistema muy sólido y bien estructurado. Sus puntos fuertes clave son la claridad y la organización, utilizando un diagrama y una tabla para que los conceptos complejos sean fáciles de entender. Cubre todos los requisitos principales de la indicación, proponiendo una arquitectura lógica con opciones tecnológicas apropiadas y estrategias sólidas de escalabilidad y fiabilidad. Sin embargo, carece de la profundidad y amplitud de la Respuesta B, particularmente en áreas como el diseño de API, la seguridad y la planificación detallada de la recuperación ante desastres.
Ver detalle de evaluación ▼
Calidad de la arquitectura
Peso 30%La arquitectura propuesta es lógica, completa y muy adecuada para la tarea. Identifica claramente todos los componentes principales y sus interacciones, y la inclusión de un diagrama facilita enormemente la comprensión. El flujo desde los productores de eventos hasta los canales de entrega está bien definido.
Integridad
Peso 20%La respuesta aborda todos los requisitos funcionales y no funcionales especificados en la indicación. Cubre los aspectos principales del diseño, incluidas las estimaciones, los componentes, los modelos de datos y las estrategias de escalabilidad y fiabilidad.
Análisis de compromisos
Peso 20%La respuesta analiza claramente las compensaciones clave en una tabla dedicada, lo cual es muy efectivo. Proporciona un razonamiento sólido para opciones como Kafka sobre SQS y Cassandra sobre DynamoDB, lo que demuestra una buena comprensión de los principios involucrados.
Escalabilidad y fiabilidad
Peso 20%El diseño aborda eficazmente la escalabilidad y la fiabilidad. Propone técnicas estándar y efectivas como el escalado horizontal para servicios, la partición en Kafka y Cassandra, y el uso de DLQ y la idempotencia para la fiabilidad. Las estrategias son sólidas y están bien explicadas.
Claridad
Peso 10%La respuesta es excepcionalmente clara y está bien organizada. El uso de encabezados, un diagrama de flujo y una tabla de compensaciones hace que el diseño complejo sea fácil de seguir y asimilar. La redacción es directa y va al grano.