Retos de Arquitectura

Desafíos avanzados que combinan múltiples conceptos — diseña soluciones completas para escenarios complejos del mundo real.

Objetivo

Los retos de esta sección combinan múltiples conceptos de arquitectura en escenarios complejos. A diferencia de los laboratorios anteriores (que se enfocan en un tema), aquí deberás integrar patrones, resiliencia, seguridad y operaciones en soluciones completas.

Cada reto tiene un nivel de dificultad y un tiempo estimado. Intenta resolverlos antes de ver las pistas.


Reto 1: Migración sin downtime

Nivel: Intermedio | Tiempo estimado: 30-45 minutos

Escenario

Tu empresa tiene un monolito que maneja autenticación, catálogo y órdenes. Necesitas extraer el servicio de órdenes como microservicio sin downtime. El monolito procesa 500 órdenes por hora y no puede detenerse.

Requisitos

  1. Cero downtime durante la migración
  2. Capacidad de rollback en cualquier momento
  3. Los datos históricos de órdenes deben estar disponibles en el nuevo servicio
  4. Los reportes existentes deben seguir funcionando

Tu tarea

Diseña un plan de migración paso a paso. Considera:

  • ¿Cómo migras los datos sin detener el monolito?
  • ¿Cómo rediriges el tráfico gradualmente?
  • ¿Cómo verificas que el nuevo servicio funciona correctamente?
  • ¿Cuál es tu plan de rollback?

Pistas

Pista 1: Patrón de migración

Usa el patrón Strangler Fig:

  1. Crea el nuevo servicio de órdenes
  2. Redirige las lecturas primero (menos riesgo)
  3. Luego redirige las escrituras
  4. Finalmente, elimina el código de órdenes del monolito
Pista 2: Migración de datos

Usa Change Data Capture (CDC):

  1. Haz un dump inicial de las órdenes históricas al nuevo servicio
  2. Configura CDC para replicar cambios en tiempo real del monolito al nuevo servicio
  3. Cuando ambos están sincronizados, redirige el tráfico
Pista 3: Verificación

Usa el patrón Shadow Traffic:

  1. Envía una copia de cada request al nuevo servicio (sin afectar al usuario)
  2. Compara las respuestas del monolito y del nuevo servicio
  3. Cuando las respuestas coinciden en un 99.9%, redirige el tráfico real

Solución de referencia

Ver solución completa

Fase 1 — Preparación (semana 1-2):

  • Crear el nuevo servicio de órdenes con la misma API
  • Configurar CDC desde la tabla de órdenes del monolito
  • Migrar datos históricos con un script batch

Fase 2 — Verificación (semana 3):

  • Activar shadow traffic: cada request al monolito se duplica al nuevo servicio
  • Comparar respuestas automáticamente y registrar discrepancias
  • Corregir bugs hasta que las respuestas coincidan

Fase 3 — Migración gradual (semana 4):

  • Redirigir 10% del tráfico de lectura al nuevo servicio
  • Monitorear latencia, errores y consistencia de datos
  • Incrementar gradualmente: 25% → 50% → 75% → 100%

Fase 4 — Escrituras (semana 5):

  • Redirigir escrituras al nuevo servicio
  • El nuevo servicio publica eventos que el monolito consume (para reportes legacy)
  • Monitorear intensivamente durante 48 horas

Fase 5 — Limpieza (semana 6):

  • Eliminar el código de órdenes del monolito
  • Desactivar CDC
  • Actualizar reportes para usar la nueva fuente de datos

Rollback en cualquier fase: Revertir la configuración del proxy/load balancer para enviar tráfico al monolito.


Reto 2: Sistema de pagos multi-proveedor

Nivel: Avanzado | Tiempo estimado: 45-60 minutos

Escenario

Diseña un servicio de pagos que soporte múltiples proveedores (Stripe, PayPal, transferencia bancaria) con los siguientes requisitos:

  1. Idempotencia: un pago no puede procesarse dos veces
  2. Resiliencia: si un proveedor falla, intentar con otro automáticamente
  3. Auditoría: cada intento de pago debe quedar registrado
  4. Conciliación: poder verificar que los cobros coinciden con las órdenes
  5. PCI compliance: no almacenar datos de tarjeta en tu sistema

Tu tarea

Diseña:

  1. La API del servicio de pagos
  2. El flujo interno de procesamiento
  3. El modelo de datos
  4. Las estrategias de resiliencia
  5. El flujo de conciliación

Pistas

Pista 1: Idempotencia

Usa una idempotency key generada por el cliente:

POST /payments
Headers: Idempotency-Key: uuid-unique-per-payment
Body: { orderId, amount, currency, provider }

Antes de procesar, verifica si ya existe un pago con esa key. Si existe, retorna el resultado anterior sin procesar de nuevo.

Pista 2: Modelo de datos
Payment {
  id: UUID
  idempotencyKey: string (unique)
  orderId: string
  amount: decimal
  currency: string
  status: PENDING | PROCESSING | COMPLETED | FAILED | REFUNDED
  provider: STRIPE | PAYPAL | BANK_TRANSFER
  providerTransactionId: string?
  attempts: PaymentAttempt[]
  createdAt: timestamp
  updatedAt: timestamp
}

PaymentAttempt {
  id: UUID
  paymentId: UUID
  provider: string
  status: SUCCESS | FAILED | TIMEOUT
  errorCode: string?
  errorMessage: string?
  responseTime: number
  attemptedAt: timestamp
}
Pista 3: Estrategia de fallback

Define un orden de prioridad de proveedores por tipo de pago:

  1. Tarjeta de crédito: Stripe → PayPal
  2. Tarjeta de débito: Stripe → PayPal
  3. Transferencia: solo banco

Si el proveedor primario falla con error recuperable (timeout, 503), intentar con el siguiente. Si falla con error no recuperable (tarjeta rechazada, fondos insuficientes), no reintentar.


Reto 3: Diseña un sistema de notificaciones

Nivel: Intermedio | Tiempo estimado: 30-45 minutos

Escenario

Diseña un servicio de notificaciones que soporte:

  • Canales: email, push, SMS, in-app
  • Prioridades: crítica (inmediata), alta (< 1 min), normal (< 5 min), baja (batch diario)
  • Preferencias: los usuarios pueden elegir qué canales recibir por tipo de notificación
  • Rate limiting: no enviar más de 10 notificaciones por hora por usuario
  • Templates: las notificaciones usan templates con variables dinámicas

Tu tarea

  1. Define la API para enviar notificaciones
  2. Diseña el flujo de procesamiento interno
  3. Define cómo manejas las preferencias del usuario
  4. Diseña la estrategia de rate limiting
  5. Define qué pasa cuando un canal falla

Consideraciones

  • ¿Usas una cola por prioridad o una cola con prioridades?
  • ¿Cómo manejas el batch diario de notificaciones de baja prioridad?
  • ¿Qué pasa si el servicio de email está caído pero el de push funciona?
  • ¿Cómo evitas enviar la misma notificación dos veces?
Ver arquitectura sugerida
[API de Notificaciones]


[Cola de prioridad]
    ├── Cola CRITICAL (procesamiento inmediato, workers dedicados)
    ├── Cola HIGH (procesamiento < 1 min)
    ├── Cola NORMAL (procesamiento < 5 min)
    └── Cola LOW (acumulación para batch)


[Notification Processor]

    ├── Verificar preferencias del usuario
    ├── Aplicar rate limiting
    ├── Resolver template con variables


[Channel Router]
    ├──► [Email Provider] (SendGrid/SES)
    ├──► [Push Provider] (Firebase/APNs)
    ├──► [SMS Provider] (Twilio)
    └──► [In-App Store] (WebSocket/DB)

Rate limiting: Usar Redis con un contador por usuario con TTL de 1 hora. Antes de enviar, verificar el contador. Si excede el límite, encolar para el siguiente período.

Deduplicación: Usar una clave compuesta (userId + notificationType + entityId) con TTL para evitar duplicados.


Reto 4: Observabilidad end-to-end

Nivel: Avanzado | Tiempo estimado: 45-60 minutos

Escenario

Tienes 8 microservicios en producción. Un usuario reporta que “a veces el checkout tarda mucho”. No puedes reproducir el problema consistentemente. Diseña una estrategia de observabilidad que te permita diagnosticar este tipo de problemas.

Tu tarea

  1. ¿Qué métricas recolectarías de cada servicio?
  2. ¿Cómo implementarías distributed tracing?
  3. ¿Qué alertas configurarías?
  4. ¿Cómo correlacionarías logs entre servicios?
  5. ¿Qué dashboard crearías para el equipo de guardia?

Criterios de éxito

Tu diseño debe permitir:

  • Identificar qué servicio causa la latencia en menos de 5 minutos
  • Ver el trace completo de un request del usuario
  • Detectar degradaciones antes de que los usuarios las reporten
  • Entender el impacto de un deploy en la latencia del sistema
Ver estrategia sugerida

Métricas (por servicio):

  • Latencia: p50, p90, p95, p99 por endpoint
  • Tasa de errores: 4xx y 5xx por endpoint
  • Throughput: requests por segundo
  • Saturación: CPU, memoria, conexiones de DB, pool de threads

Distributed tracing:

  • Generar un traceId en el API Gateway
  • Propagar via header X-Trace-Id a todos los servicios
  • Cada servicio registra spans con inicio, fin y metadata
  • Usar OpenTelemetry como estándar

Alertas:

  • Latencia p99 > 2x del baseline por más de 5 minutos
  • Tasa de errores 5xx > 1% por más de 2 minutos
  • Cola de mensajes creciendo por más de 10 minutos
  • CPU > 80% por más de 5 minutos

Dashboard de guardia:

  • Vista general: health de todos los servicios (verde/amarillo/rojo)
  • Vista de flujo: latencia por paso del checkout
  • Vista de traces: los 10 traces más lentos de la última hora
  • Vista de errores: errores agrupados por tipo y servicio

Reflexión

Después de completar los retos:

  1. ¿Cuál fue el reto más difícil? ¿Por qué?
  2. ¿En qué áreas sientes que necesitas más práctica?
  3. ¿Pudiste reutilizar patrones de secciones anteriores?
  4. ¿Qué reto se parece más a un problema real que enfrentas en tu trabajo?

Los retos están diseñados para simular la complejidad real de la arquitectura de software. No hay soluciones perfectas — hay soluciones bien razonadas.