Volver a proyectos

TaskFlow Pro

Caso de estudio · Proyecto autodirigido

Tableros sincronizados, automatizaciones asíncronas y observabilidad integrada.

Una plataforma de gestión de tareas donde los cambios se propagan entre usuarios mediante WebSocket, las automatizaciones se procesan en workers separados y tres capas de observabilidad registran lo que ocurre en el sistema.

Explorar código

Demo pública no disponible

Tres procesos, un sistema
  1. Next.js + Socket.io

    Un custom server comparte HTTP y WebSocket en el mismo proceso.

  2. Workers BullMQ

    Proceso separado que consume colas de Redis de forma asíncrona.

  3. Infraestructura Docker

    PostgreSQL, Redis, Nginx, Prometheus y Grafana.

Arquitectura documentada en ARCHITECTURE.md. El servidor y los workers son procesos independientes que se comunican a través de Redis.

Sincronizar equipos sin recargar la página

Una plataforma de gestión de tareas necesita que los cambios lleguen a todos los miembros del equipo en tiempo real. Además, las automatizaciones —tareas obsoletas, vencimientos próximos, condiciones externas— deben procesarse sin bloquear la interfaz ni depender de que el usuario tenga la aplicación abierta.

El documento de arquitectura plantea un servidor HTTP compartido para Next.js y Socket.io, con workers BullMQ en un proceso separado para las automatizaciones. La observabilidad se distribuye en tres capas: logs estructurados, métricas y error tracking.

Mi aportación

Implementé la sincronización de tableros, el motor de automatización, la observabilidad y los controles de autenticación y autorización; añadí pruebas unitarias y end-to-end del flujo crítico.

Un proceso HTTP y un proceso worker

server.ts crea un servidor HTTP que monta Next.js para las rutas web y Socket.io para las conexiones WebSocket. Las API routes publican eventos en Redis; el servidor Socket.io los retransmite a los clientes suscritos. Los workers BullMQ corren en un proceso separado y consumen tres colas: automatizaciones, notificaciones y limpieza.

Flujo de un cambio en el tablero
  1. Usuario

    Mueve una tarea en el tablero

  2. API Route

    Persiste en PostgreSQL vía Prisma

  3. Redis Pub/Sub

    Publica el evento al canal del tablero

  4. Socket.io

    Retransmite a los clientes conectados

Flujo de una automatización

  1. Scheduler

    Evalúa triggers cada 5 minutos

  2. BullMQ

    Encola el job en Redis

  3. Worker

    Ejecuta acciones y registra resultados

  4. Redis Pub/Sub

    Publica el cambio al tablero

Las API routes no tienen acceso directo al servidor Socket.io. Redis Pub/Sub actúa como puente: cualquier proceso puede publicar y el servidor WS retransmite.

Modelo de datos

User, Session y OAuthAccount gestionan la identidad. Team agrupa miembros y es propietario de los Boards. Cada Board tiene Columns con Tasks. Automation, AutomationAction y AutomationRun representan las recetas y su historial de ejecución. AuditLog registra operaciones críticas.

De la API route al navegador, sin recargar

Socket.io necesita un servidor HTTP persistente. Las API routes de Next.js son funciones stateless que no pueden mantener conexiones WebSocket. La solución es un custom server que comparte el proceso HTTP entre ambos.

  1. API Route

    Llama a publishToBoard()

  2. Redis

    Publica al canal board:{id}

  3. Socket.io

    Suscriptor permanente con psubscribe

  4. Clientes

    Reciben el evento en la room del tablero

Autenticación en la conexión WebSocket

El middleware de Socket.io extrae el access token del handshake (auth o cookie), verifica su validez con JWT y comprueba la blacklist en Redis antes de permitir la conexión.

Los eventos perdidos no se recuperan

El cliente Socket.io reconecta automáticamente, pero no existe un mecanismo de catch-up que recupere los eventos emitidos durante una desconexión. El documento de arquitectura lo identifica como decisión pendiente.

Tres capas para procesar automatizaciones

El motor de automatizaciones separa la evaluación de triggers, el encolado y la ejecución. Las automatizaciones activas se evalúan periódicamente sin depender de eventos inmediatos del usuario.

Capa 1 — Evaluación de triggers

El scheduler recorre las automatizaciones activas cada 5 minutos. Para cada trigger periódico (TASK_STALE, TASK_DUE_SOON, WEATHER, SCHEDULE) evalúa la condición consultando PostgreSQL o una API externa. Antes de disparar, comprueba si ya existe una ejecución reciente para evitar duplicados.

Capa 2 — Cola persistente

Cuando un trigger se cumple, triggerAutomation() encola un job en BullMQ. La cola garantiza reintentos con backoff exponencial si el worker falla, persistencia si el proceso se detiene, e historial de ejecuciones.

Capa 3 — Ejecución de acciones

El worker de automatizaciones ejecuta cada acción en orden, registra logs detallados y actualiza el AutomationRun con el resultado. Las acciones pueden publicar cambios a Redis Pub/Sub para que lleguen al tablero en tiempo real.

  1. Scheduler

    Evalúa TASK_STALE, detecta tareas obsoletas

  2. triggerAutomation()

    Encola en BullMQ

  3. automationProcessor

    Ejecuta acciones y registra logs

  4. AutomationRun

    Resultado persistido en PostgreSQL

Workers configurados

Automatizaciones
concurrency: 5
Notificaciones
concurrency: 10
Limpieza
concurrency: 1

Configuración del código en workers/index.ts. No se ejecutaron pruebas de carga.

El scheduler requiere instancia única

El scheduler debe correr en una sola instancia para evitar ejecuciones duplicadas. El documento de arquitectura identifica la coordinación con Redis locks como decisión pendiente para escalado horizontal.

Logs, métricas y error tracking

La observabilidad se distribuye en tres capas con herramientas especializadas para cada función. No se trata de una lista de tecnologías: cada capa responde a un tipo de pregunta diferente sobre el sistema.

Logs, métricas y error tracking

Logs estructurados · Pino

Todos los logs son JSON con campos consistentes: module, userId, jobId. Los secrets y passwords se redactan automáticamente con la configuración de Pino. En producción se pueden enviar a cualquier servicio de log aggregation.

Métricas de sistema y negocio · Prometheus + Grafana

Dos tipos de métricas: infraestructura (latencia HTTP p95, jobs en cola, conexiones WebSocket activas) y negocio (automatizaciones ejecutadas por tipo, tareas completadas). El código registra activeWebSocketConnections con inc/dec en las conexiones de Socket.io.

Error tracking · Sentry

Captura errores con contexto: stack trace, usuario, request. Las alertas de Grafana cubren degradación de rendimiento; Sentry cubre errores específicos.

Dashboards no verificados

Se verificó la integración de prom-client en el código del servidor Socket.io y la presencia de @sentry/nextjs en las dependencias. Los dashboards de Grafana y las alertas configuradas no se revisaron durante esta auditoría.

Autenticación, autorización y protección

El sistema implementa varias capas de seguridad documentadas en la arquitectura y verificadas en el código del repositorio.

JWT con rotación de refresh tokens

Access tokens con expiración de 15 minutos. Cada uso del refresh token invalida el anterior en la base de datos y emite uno nuevo. Si un atacante usa el token robado, el siguiente intento del usuario legítimo detecta la invalidación.

Blacklist de tokens en Redis

Para el logout inmediato, el access token se agrega a una blacklist con TTL igual al tiempo de expiración restante. Cada request verifica la blacklist: una operación GET en Redis por request autenticado.

Cookies httpOnly y rate limiting

Los tokens se almacenan en cookies httpOnly, Secure, SameSite=Lax. Rate limiting: 10 intentos de login por IP cada 15 minutos en Redis; 30 req/min general en Nginx.

Webhooks HMAC-SHA256

Verificación de webhooks entrantes con crypto.timingSafeEqual para prevenir timing attacks.

Inicio de sesión federado

Se verificó la existencia del flujo OAuth de GitHub en el repositorio. El documento de arquitectura no menciona Google como proveedor implementado en esta versión.

RBAC no auditado

El CV menciona RBAC. El documento de arquitectura describe Teams y Members con roles. No se revisó en detalle la implementación del middleware de autorización por roles durante esta auditoría.

Rendimiento

Decisiones del código para responder rápido y sacar el trabajo pesado de la petición. Son características de la implementación, no mediciones: no hay benchmarks publicados.

  1. Caché del tablero en Redis

    GET /api/boards/:id guarda el tablero completo en board:{id} con withCache durante 5 minutos; crear, editar, borrar o mover una tarea invalida esa clave.

  2. Caché de usuario

    /api/auth/me lee el usuario de user:{id} (1 hora) antes de ir a PostgreSQL.

  3. Colas BullMQ fuera de la petición

    Automatizaciones y notificaciones se encolan y las procesa un worker aparte, con concurrencia 5 y 10, reintentos con backoff y retención limitada de jobs terminados.

  4. Reordenar con pocas consultas

    Mover una tarea corre en una transacción y desplaza las posiciones afectadas con un solo updateMany por rango, no una consulta por tarea.

  5. Consultas acotadas e indexadas

    Las relaciones se piden con select de los campos necesarios y _count en lugar de cargar comentarios; el esquema indexa columnId, position, assigneeId y dueDate, entre otras.

  6. Tiempo real escalable

    Socket.io usa el adaptador de Redis, y las API routes publican eventos por Redis Pub/Sub en lugar de hablar con el proceso de sockets.

Decisiones con un coste explícito

Custom server.ts compartido entre Next.js y Socket.io

Motivo
Las API routes de Next.js son stateless y no pueden mantener conexiones WebSocket persistentes. Un servidor HTTP compartido permite que ambos convivan en el mismo proceso y puerto.
Compromiso
Se pierde el despliegue serverless nativo de Next.js. El proceso debe mantenerse activo para las conexiones WebSocket.

Redis Pub/Sub como puente entre API routes y Socket.io

Motivo
Las API routes no tienen acceso directo al objeto io de Socket.io. Redis desacopla la publicación de eventos de su retransmisión y permite escalar horizontalmente.
Compromiso
Añade una dependencia de comunicación entre componentes del mismo proceso. Los eventos perdidos durante una desconexión del cliente no se recuperan.

BullMQ para automatizaciones y notificaciones

Motivo
Reutiliza Redis (ya presente para cache y Pub/Sub), ofrece reintentos con backoff exponencial y un API de TypeScript con genéricos para tipado fuerte.
Compromiso
El scheduler requiere instancia única o coordinación con locks para evitar ejecuciones duplicadas. Alternativas evaluadas: pg-boss y AWS SQS.

Rotación de refresh tokens con invalidación en base de datos

Motivo
Detectar el uso de un token robado: si el atacante lo usa primero, el siguiente intento del usuario legítimo invalida toda la sesión.
Compromiso
Una consulta a la base de datos por cada renovación de tokens. La blacklist de access tokens añade una operación Redis por request autenticado.

Flujos end-to-end verificados por Playwright

El repositorio contiene tests E2E con Playwright que recorren los flujos críticos del sistema. Requieren la aplicación, PostgreSQL, Redis y un seed de datos. Se revisó el código de las pruebas, sin ejecutarlas.

Autenticación
Registro, login, logout y persistencia de sesión. Recorre el formulario completo y verifica la redirección al dashboard.
Tableros Kanban
Creación de tablero, creación de tarea, drag and drop entre columnas y persistencia tras recarga.
Automatizaciones
Creación de una automatización con trigger TASK_MOVED y acción SEND_NOTIFICATION. Verifica que aparezca activa en la lista.
Sincronización multi-usuario
Dos contextos de navegador independientes (Alice y Bob). Alice crea una tarea; Bob la ve sin recargar, validando la propagación por WebSocket con timeout de 5 segundos.

Código de pruebas revisado · Ejecución no verificada

Lo verificable aquí es la implementación y la existencia de estas comprobaciones. No se publican porcentajes de cobertura, tiempos de ejecución ni resultados de producción sin evidencia reproducible.

Revisar la implementación