








ComplyDQ — Sitio institucional
Sitio de compliance DOT con herramientas interactivas de evaluación
- Rol
- Full-stack Engineer
- Tipo de trabajo
- Colaboración con equipo
- Industria
- Transporte • SaaS de compliance
Resumen
El desafío
Enlaces
Privado — proyecto de cliente
Arquitectura del sistema
Un sitio de marketing orientado a contenido, construido casi por completo con React Server Components. La decisión central es que prácticamente todas las páginas se renderizan de forma estática, sin dependencias de datos en runtime, y la única superficie dinámica —el blog— queda aislada detrás de un cliente ISR que falla de forma controlada. La interactividad se limita a hojas de cliente acotadas y con límites explícitos, de modo que los layouts de página, la metadata y los datos estructurados siguen renderizándose en el servidor mientras las calculadoras y los toggles hidratan de forma independiente.
Arquitectura de la aplicación
- Next.js 16 App Router con 15 rutas, todas totalmente estáticas en build salvo el blog.
- 14 componentes de cliente limitados a las hojas interactivas; todo lo demás es Server Component.
- El índice del blog, el detalle de cada post y el sitemap comparten una ventana de revalidación ISR de 60 segundos.
- El contenido y los datos de dominio viven en módulos tipados, no embebidos en el JSX.
- Output standalone, para que la imagen de runtime incluya sólo las dependencias que realmente resuelve.
- Constantes con una única fuente de verdad: precios, montos de multas y navegación se definen una vez y todos los consumidores los importan.
Funcionalidades de la plataforma
- Evaluación de preparación para auditorías del DOT con puntaje ponderado, manejo de selección múltiple excluyente, resultados por niveles y detección de brechas a partir de un umbral definido.
- Calculadora de ROI que estima horas administrativas y costo anuales, equivalente en FTE, ROI neto y período de repago en meses; excluye las multas a propósito para que el resultado sea conservador.
- Checklist del DQ File que cubre los ítems de 49 CFR Part 391, agrupados por tipo de conductor y con notas de retención.
- Calculadora de precios en vivo que usa las mismas constantes de facturación reales de la plataforma.
- Biblioteca de 26 preguntas frecuentes con búsqueda, con todas las respuestas renderizadas en el servidor.
- Blog alimentado por el CMS con posts destacados, categorías, autores y control de noindex por post.
Modelo de datos
- Sin base de datos ni persistencia en este repositorio; sin auth, sin API routes, sin server actions.
- Una única fuente externa de sólo lectura: Strapi 5 REST, consultado sin token.
- Las estructuras crudas del CMS se normalizan a un view model, así la UI nunca toca la forma de la API.
- Cada consulta se filtra por tenant según el slug del proyecto —tanto en el detalle como en el listado— para evitar lecturas cruzadas entre tenants adivinando URLs.
- El estado de la evaluación, del ROI y del checklist es estado efímero de React; no se envía ni se guarda nada.
SEO y datos estructurados
- JSON-LD @graph
- Organization
- SoftwareApplication
- BlogPosting
- FAQPage
- BreadcrumbList
- Canonical
- OpenGraph
- robots.txt dinámico
- Sitemap con ISR
- Desindexación con 410
Infraestructura y deploy
Self-hosted en un VPS compartido que corre unos veinte sitios, lo que determina buena parte de la configuración: todo recurso que pueda chocar entre entornos o con los vecinos —puertos, nombres de proyecto de Compose, nombres de contenedores, bloques upstream de nginx, nombres de archivo de los server blocks— tiene un namespace explícito, y el modo de falla que produciría cada colisión está documentado en el mismo lugar donde se tomó la decisión.
Pipeline de despliegue
- Bitbucket Pipelines: los pull requests y main corren install, build, type-check y lint.
- Los despliegues a producción se disparan por tag y requieren aprobación manual, así que pushear un tag por sí solo no alcanza para publicar.
- El paso de verificación se vuelve a correr sobre el tag en lugar de confiar en la corrida anterior de la rama, porque un tag puede apuntar a cualquier commit.
- El rollback consiste en volver a correr el pipeline del tag anterior; cada tag fija un commit exacto.
Contenedores y runtime
- Build de Docker multi-stage sobre node:22-alpine, corriendo con un usuario no root.
- Las variables de entorno públicas se pasan como build args, porque Next las inyecta en el build en lugar de leerlas en runtime.
- Healthcheck de Compose con fetch nativo, con un start period que no cuenta para los reintentos.
- Slot y puerto inyectados por entorno, para que ambas versiones convivan durante el swap.
nginx y edge
- Fragmentos upstream por entorno que se reescriben en cada despliegue, con el circuit breaker desactivado en un upstream de un solo backend y conexiones keepalive en pool.
- Redirección del dominio raíz a www; HSTS, una allowlist de CSP por origen, protecciones contra framing y sniffing, y una Permissions-Policy que deniega 18 funcionalidades.
- Los headers de seguridad se vuelven a declarar dentro de los location blocks anidados, porque declarar un solo header en un bloque descarta los del padre.
- La superficie legacy se resuelve en el edge: 410 para el mapa de slugs de spam y las rutas legacy de WordPress, 301 para las nueve páginas legítimas.
- Precalentamiento post-despliegue de hasta 40 assets con hash, porque las claves de caché son la URL completa.
Configuración y salvaguardas
- La identidad del entorno sale de la URL pública del sitio, no del modo de build: ambos entornos compilan en modo producción.
- El despliegue verifica la indexabilidad en ambos sentidos y avisa de forma visible en lugar de abortar, porque la corrección vive en nginx y no en el build.
- El despliegue vuelve a verificar que los headers de caché de los chunks no sean immutable, porque ese bloque vive en la configuración del servidor, fuera del repositorio.
Construido con
Superficies de la aplicación
- 15 rutas: Home, Features, un hub de Resources con tres páginas de herramientas, índice y detalle del Blog, Partners, About, Contact, FAQs, Privacy Policy, Terms of Service y un 404.
- Tres herramientas interactivas de dominio —evaluación, calculadora de ROI y checklist— más una calculadora de precios en vivo.
- Design system de unos 45 componentes con CSS Modules basados en tokens, tema claro y oscuro, layout responsive, skip-link y soporte de reduced-motion.
Integraciones y SEO
- Cliente de Strapi acotado por tenant, con sanitización del lado del servidor y degradación controlada.
- Capa completa de SEO y datos estructurados: JSON-LD por página, robots dinámico, sitemap con ISR, canonicals y tarjetas para redes sociales.
Infraestructura y documentación
- Recursos de migración: un generador de sitemap a nginx para el mapa de desindexación, la tabla de mapeo de redirecciones y una justificación escrita del enfoque.
- Infraestructura como código: Dockerfile, Compose, dos server blocks de nginx, dos fragmentos upstream, el script de despliegue blue-green y el pipeline de CI.
- Un README operativo de unas 420 líneas que cubre ambos entornos, runbooks de puesta en marcha y baja, la preparación única del servidor y post-mortems de incidentes con comandos para reproducirlos.