🎯 Meta: construir UNA aplicación completa usando todo el libro: base de datos, API, frontend, auth, tiempo real, tests, Docker, CI/CD y despliegue con observabilidad. No hay conceptos nuevos: hay integración, que es donde se aprende de verdad y donde se ve si los capítulos se quedaron o pasaron de largo.
Duración realista: 3-6 semanas a ritmo de estudio. Hecho con calma y bien, este proyecto es tu portfolio: vale más que diez certificados.
22.1 · El proyecto: "Cantina" — pedidos para un local real
Un sistema de pedidos para una cafetería/food-truck. Lo elegimos porque toca TODO sin ser un clon aburrido de tienda:
- Clientes ven la carta, piden desde el móvil, siguen su pedido en vivo.
- Cocina ve la cola de pedidos en tiempo real y los avanza de estado.
- Admin gestiona carta, precios, stock y ve métricas del día.
┌───────────── Frontend (elige: React/Next/Vue/HTMX) ─────────────┐
│ /carta /pedido/:id (SSE en vivo) /cocina /admin │
└──────────────────────────────┬────────────────────────────────┘
│ HTTPS (Nginx, cap. 13)
┌──────────────────────────────▼────────────────────────────────┐
│ API (elige tu backend: 03-08) + WebSocket/SSE (21) │
│ Auth por sesión/JWT (20) · validación · rate limit │
└───────┬──────────────────┬──────────────────┬─────────────────┘
PostgreSQL Redis Worker
(01-02, H) (caché+pubsub+colas, B) (tickets, emails)Reglas de negocio (las que dan juego arquitectónico):
- Un pedido pasa por:
carrito → pagado → en_preparacion → listo → entregado(ocancelado). Las transiciones ilegales se rechazan (máquina de estados en el dominio, cap. 11). - El stock se descuenta al pagar, no al añadir al carrito; sin stock → error claro. Dos personas comprando la última empanada = concurrencia real (apéndice H:
SELECT … FOR UPDATEo update condicional). - La cocina solo ve pedidos
pagado+; el cliente solo ve LOS SUYOS (ownership, cap. 20.7). - Cancelar solo antes de
en_preparacion, y devuelve el stock (transacción, cap. 01).
22.2 · Los checkpoints (en orden, sin saltarse ninguno)
Cada checkpoint termina con algo demostrable. Si no puedes enseñarlo funcionando, no está terminado.
✅ Checkpoint 0 — Cimientos (caps. E, 00)
□ Repo git con main protegida, ramas feature/*, Conventional Commits.
□ README con: qué es, cómo levantarlo (UN comando), decisiones tomadas.
□ docker compose up levanta PostgreSQL + Redis vacíos.
□ Elección escrita (3 líneas por decisión): backend, frontend, sesión-vs-JWT.
No hay elección mala; hay elección sin justificar.✅ Checkpoint 1 — Modelo de datos (caps. 01-02, H)
□ Esquema: usuarios, productos, pedidos, lineas_pedido, pagos + migraciones versionadas.
□ Restricciones EN LA BD: CHECK (precio > 0), CHECK de estados válidos, FKs, UNIQUE.
□ Índices razonados: pedidos por (user_id, creado_en), productos por categoría.
□ Seed realista: 30 productos, 5 usuarios, 50 pedidos históricos.
□ Consultas de negocio escritas y EXPLAINadas: ventas del día, top productos,
tiempo medio de preparación (window functions del cap. 02).✅ Checkpoint 2 — API core (caps. 03-08, 10, 11)
□ CRUD de carta (público el GET, admin el resto) + pedidos con la máquina de estados.
□ Arquitectura por capas de verdad: controller → service/dominio → repositorio.
La regla "cancelar devuelve stock" vive en el dominio, NO en el controller.
□ Validación de entrada en el borde; errores homogéneos (RFC 9457 problem+json).
□ La compra concurrente de la última unidad: test que lanza 2 transacciones y
demuestra que solo una gana (ap. H). Este test es el corazón del proyecto.
□ OpenAPI/Swagger publicado en /docs.✅ Checkpoint 3 — Auth (cap. 20)
□ Registro/login (argon2id) + roles cliente/cocina/admin.
□ Sesión httpOnly O access+refresh rotativo — la que justificaste en el checkpoint 0.
□ Ownership: GET /pedidos/:id → 403 si no es tuyo (con test).
□ Rate limit en /login. Login con Google/GitHub opcional pero recomendado.✅ Checkpoint 4 — Frontend (caps. 15-19)
□ /carta pública con búsqueda y filtros; carrito local persistente.
□ Checkout → pago simulado → redirección a /pedido/:id.
□ Rutas protegidas por rol (/cocina, /admin) + reflejo de permisos en la UI.
□ Los 4 estados (cargando/error/vacío/éxito) en CADA vista con datos.
□ Formularios con validación en cliente Y servidor mostrando los errores del backend.
□ Lighthouse ≥ 90 en Performance y Accessibility en /carta.✅ Checkpoint 5 — Tiempo real (cap. 21)
□ /pedido/:id se actualiza solo por SSE cuando cocina avanza el estado.
□ /cocina recibe pedidos nuevos al instante (SSE o WS) con sonido/aviso.
□ Reconexión visible y recuperación de eventos perdidos (Last-Event-ID).
□ Todo probado A TRAVÉS de Nginx, no contra el puerto del backend.✅ Checkpoint 6 — Asíncrono y caché (ap. B)
□ Al pagar: job en cola genera el ticket (PDF o texto) y "envía" el email (log).
La petición HTTP NO espera al worker.
□ Caché cache-aside de la carta (TTL 60 s) con invalidación al editarla el admin.
□ Demuestra la diferencia con una prueba de carga simple (ab/k6): carta con y sin caché.✅ Checkpoint 7 — Calidad (caps. 09, 10)
□ Pirámide real: unit (dominio: máquina de estados, stock), integración (API+BD con
testcontainers o BD efímera), e2e (Playwright: pedir → cocina → entregado).
□ El flujo crítico completo cubierto: pagar descuenta stock, cancelar lo devuelve.
□ Cobertura del dominio > 80% (y entender por qué NO perseguimos el 100% global).
□ Linter + formatter en pre-commit.✅ Checkpoint 8 — Producción (caps. 12-14, F, G opcional)
□ Dockerfiles multi-stage (API y front) + compose de producción.
□ Nginx: TLS (Let's Encrypt), reverse proxy, assets estáticos con caché (ap. L),
gzip/brotli, timeouts correctos para SSE/WS.
□ CI en GitHub Actions: lint + tests + build de imágenes en cada PR; deploy al VPS
en merge a main (CD del cap. 14).
□ Observabilidad mínima: logs estructurados con request-id, /salud, métricas
Prometheus (pedidos_creados_total, latencia p95) y 1 dashboard Grafana (ap. F).
□ Backup diario de PostgreSQL probado con una restauración REAL (cap. 14).
□ Opcional: manifests de Kubernetes con kind (ap. G) como ejercicio.✅ Checkpoint 9 — El extra que te diferencia (elige UNO y hazlo bien)
□ PWA instalable para la cocina con cola offline (ap. N), o
□ "Sugerencias del chef" con un LLM en streaming (ap. O), o
□ Monorepo con tipos compartidos back↔front (ap. K), o
□ Accesibilidad AA auditada de todo el flujo de compra (ap. M).22.3 · Cómo trabajar (esto también es el temario)
- Una feature = una rama = un PR, aunque trabajes solo. Revísate a ti mismo al día siguiente: es un superpoder infravalorado.
- Vertical, no horizontal: no hagas "toda la BD, luego toda la API, luego todo el front". Haz una feature completa de punta a punta (carta pública primero), y repite. Tendrás algo demostrable desde la semana 1.
- Cuando te atasques, escribe el problema en
notas.mdantes de buscar la solución. La mitad de las veces, formularlo lo resuelve. - Registra las decisiones (ADRs de 5 líneas: contexto → decisión → consecuencias). Tu yo de dentro de 3 meses no recordará por qué elegiste sesiones sobre JWT.
- Termina. Un proyecto al 100% con menos features vale infinitamente más que uno al 70% con todas. Recorta alcance, nunca calidad.
22.4 · Errores que veo en (casi) todos los proyectos finales
| Error | Antídoto |
|---|---|
| Empezar por el frontend "para ver algo" | Checkpoint 1 y 2 primero: los datos mandan |
| Lógica de negocio en controllers | La máquina de estados vive en el dominio y se testea sin HTTP |
| "Los tests los hago al final" | Al final nunca llega; escribe el test de concurrencia en el CP2 |
| Auth casera "temporal" | Temporal = para siempre; hazla bien en el CP3 |
| Ignorar los estados de error en la UI | El demo falla justo ahí, siempre |
| Docker solo "en mi máquina" | El CI construye la imagen desde el día del CP8… o antes |
| Optimizar sin medir | EXPLAIN, Lighthouse y k6 primero; caché después |
✅ Entrega final
Tu proyecto está terminado cuando puedas hacer esta demo de 10 minutos sin tocar nada fuera del navegador y una terminal:
1. git clone + docker compose up → todo levanta con seed.
2. Cliente móvil: navega la carta, pide, paga, ve su pedido avanzar EN VIVO.
3. Cocina (otra ventana): recibe el pedido al instante, lo avanza.
4. Admin: edita un precio (la caché se invalida), mira el dashboard del día.
5. Enseñas: el test de concurrencia del stock pasando, el pipeline de CI verde,
el dashboard de Grafana con las peticiones de la demo que acabas de hacer,
y una restauración de backup en una BD limpia.💡 Si solo tienes 2 semanas…
Versión mínima digna: checkpoints 0-5 con HTMX como frontend (te ahorras la SPA entera y el capítulo 16 cubre todo lo que necesitas), cola de tickets como log simple, y despliegue en un solo VPS con compose + Caddy (TLS automático). Recorta el checkpoint 9, no los tests.
🧠 Autoevaluación final del libro
Descontarlo antes bloquearía stock de carritos abandonados. Al pagar, dos transacciones concurrentes pueden leer "queda 1": se resuelve con bloqueo pesimista (SELECT … FOR UPDATE) o update condicional (UPDATE … SET stock = stock - 1 WHERE stock >= 1 verificando filas afectadas) dentro de la transacción del pago (ap. H).
En el dominio (entidad/servicio de dominio, cap. 11), no en el controller ni en el frontend: así se aplica venga de donde venga la orden (API, admin, un job) y se testea sin HTTP. El frontend además la refleja ocultando el botón (UX), y la BD puede reforzarla con un CHECK.
Medir → EXPLAIN de la consulta → índice si falta → si sigue siendo caro, caché cache-aside con invalidación al editar (ap. B). Nunca "meto Redis" antes de saber qué es lento.
Un backup sin restauración probada es una esperanza, no un plan. La restauración demuestra que el fichero sirve, que conoces el procedimiento y cuánto tarda (tu RTO real).
Siguiente: 23-pensar-como-ingeniero.md — ya sabes construir. La Parte VI trata de decidir: trade-offs, diseño de sistemas, ADRs y casos de estudio reales. Es la parte que solo tiene sentido ahora, cuando ya te has peleado con código de verdad.