Skip to content

🎯 Qué construyes: una plataforma donde instructores publican cursos con vídeo y los alumnos los compran y consumen. Frontend real, pagos reales (en modo prueba), despliegue real.

Prerrequisitos: capítulos 12-22, apéndices L y P. Duración: 4-6 semanas.

🧠 Por qué este proyecto. Es el primero donde el despliegue forma parte del problema, no es un apéndice. Vídeo pesado, pagos que no pueden fallar, contenido que hay que proteger y un frontend que debe ser rápido. Tocas todas las decisiones que hacen difícil un producto real.


Alcance

Dentro: catálogo público, registro, compra, reproducción de vídeo protegida, progreso del alumno, panel del instructor, despliegue con CI/CD.

Fuera (no-objetivos explícitos — capítulo 25): app móvil, chat en vivo, certificados verificables, subtítulos automáticos, sistema de afiliados. Escribe esta lista en tu README: la disciplina de decir que no es parte del ejercicio.


Requisitos funcionales

Público

  • Catálogo con búsqueda, filtros por categoría, nivel y precio, y paginación.
  • Ficha de curso: temario, instructor, valoraciones, primera lección gratuita de muestra.
  • SEO: renderizado en servidor o estático, metadatos Open Graph, sitemap.

Alumno

  • Registro/login, incluyendo OAuth con Google (cap. 20).
  • Compra con Stripe en modo prueba (apéndice P).
  • "Mis cursos", reproductor con seguimiento de progreso (retoma donde lo dejó).
  • Marcar lecciones completadas; barra de progreso del curso.
  • Valorar y reseñar cursos que haya comprado (solo esos).

Instructor

  • Panel: crear curso, secciones y lecciones.
  • Subida de vídeo (caso 5 del cap. 26): URL prefirmada, procesado asíncrono, estados subiendoprocesandolisto.
  • Publicar/despublicar. Ver ventas e ingresos.

Administración

  • Aprobar cursos antes de su publicación.
  • Ver métricas globales.

Requisitos no funcionales

#Requisito
NF1El vídeo NO es accesible sin haber comprado — y no basta con esconder la URL
NF2El pago es idempotente: doble clic o webhook duplicado no cobra dos veces
NF3La compra se confirma por webhook de Stripe, nunca por la redirección del navegador
NF4El catálogo carga en < 1,5 s con conexión 4G simulada (Lighthouse ≥ 90)
NF5Imágenes optimizadas (WebP/AVIF), lazy loading, tamaños responsive
NF6Accesibilidad: navegable por teclado, contraste AA, reproductor con controles accesibles
NF7CI: en cada PR corren lint, tests y build; sin verde no se mergea
NF8CD: merge a main despliega automáticamente
NF9Secretos en el gestor de secretos del proveedor, nunca en el repositorio
NF10Healthcheck, logs estructurados y alerta si la tasa de 5xx supera el 1%
NF11Backups automáticos diarios de la base de datos, con restauración probada

⚠️ NF1 es el requisito técnicamente interesante del proyecto. Poner el vídeo en una carpeta con un nombre difícil de adivinar no es protección: en cuanto un alumno comparte la URL, se acabó. La solución real son URLs firmadas con caducidad corta (minutos) generadas por tu backend tras comprobar la compra, servidas por el CDN. Si además usas HLS, cada fragmento se firma por separado y la protección es aún más granular.

⚠️ NF3 es el que más gente hace mal. Confirmar la compra cuando el navegador vuelve a /gracias es un agujero: el usuario puede cerrar el navegador antes (paga y no recibe el curso) o navegar directamente a esa URL (recibe el curso sin pagar). La única fuente de verdad es el webhook firmado del proveedor de pagos, verificando la firma. La redirección solo sirve para enseñar un "gracias" bonito.


Arquitectura de referencia

mermaid
graph TD
    U[Usuario] --> CDN[CDN · estáticos y vídeo]
    U --> FE[Frontend · Next.js SSR]
    FE --> API[API backend]
    API --> PG[(PostgreSQL)]
    API --> R[(Redis · sesiones y caché)]
    API --> Q[(Cola)]
    Q --> W[Workers: transcodificar,<br/>emails, miniaturas]
    API --> S3[(Object storage)]
    W --> S3
    S3 --> CDN
    ST[Stripe] -->|webhook firmado| API
    API --> OBS[Logs · métricas · alertas]

Fases

Fase 1 · Esqueleto desplegado (semana 1) Monorepo, Docker Compose local, "hola mundo" de front y back, desplegado en producción el primer día con CI/CD. Parece exagerado; es lo que evita que el despliegue sea un drama en la semana 6.

Fase 2 · Catálogo y cuentas (semana 2) Modelo de datos, cursos, secciones, lecciones. Registro, login, OAuth. Catálogo público con SEO.

Fase 3 · Vídeo (semana 3) Subida con URL prefirmada, cola de procesado, transcodificación, reproductor. La parte más arriesgada: hazla pronto.

Fase 4 · Pagos (semana 4) Stripe Checkout, webhooks con verificación de firma, idempotencia, acceso condicionado a la compra, reembolsos.

Fase 5 · Experiencia (semana 5) Progreso, reseñas, panel del instructor, optimización de rendimiento y accesibilidad.

Fase 6 · Producción (semana 6) Observabilidad, alertas, backups con restauración probada, documentación, ADRs.


Criterios de aceptación

□ La URL pública funciona y un desconocido puede registrarse y comprar (modo prueba de Stripe)
□ Copiar la URL del vídeo y abrirla en incógnito → acceso denegado
□ Completar un pago y cerrar el navegador ANTES de la redirección → el curso aparece igual
□ Reenviar el mismo webhook de Stripe dos veces → un solo acceso, un solo cobro
□ Un alumno no puede reseñar un curso que no ha comprado
□ Lighthouse ≥ 90 en rendimiento y accesibilidad en la página de catálogo
□ Toda la app es navegable solo con teclado
□ Un PR con un test roto NO se puede mergear
□ Merge a main despliega solo, y se puede revertir
□ docker compose up levanta el entorno completo en local
□ Restauraste un backup en una base limpia y la app funcionó
□ No hay ni un secreto en git log -p

Trampas conocidas

TrampaPor qué dueleQué hacer
Subir el vídeo a través de tu APIBloquea workers durante minutosURL prefirmada (cap. 26.5)
Guardar el progreso en cada segundo de reproducciónMiles de escrituras por alumnoGuardar cada 15-30 s y al pausar/salir
Confiar en la redirección de StripeCompras perdidas y accesos regaladosSolo el webhook firmado
Servir el vídeo desde tu servidorAncho de banda carísimo y lentoCDN, siempre
Dejar el despliegue para el finalDescubres los problemas sin tiempoDespliega en la fase 1
Contraseñas de Stripe en el .env del repoFiltración de claves realesGestor de secretos + .env.example

Extensiones

  1. HLS con calidades adaptativas — transcodificar a varios bitrates y servir un .m3u8.
  2. Recomendaciones — "porque compraste X", empezando por lo simple (misma categoría) antes de pensar en nada sofisticado.
  3. Cupones y descuentos — con todas sus trampas: caducidad, uso único, límite global.
  4. Modo offline (PWA, apéndice N) — descargar lecciones para ver sin conexión.
  5. Multi-idioma (i18n) en la interfaz y en las fichas de curso.
  6. Panel de analítica para el instructor — tasa de finalización por lección, para detectar dónde abandonan los alumnos.

Siguiente: 04-diseno-de-sistema.md