Skip to content

Background Jobs - Architecture Decision Records

Sistema: Background Jobs System Fecha: 2026-02-05 Estado: Aprobado

Documentos relacionados:


Índice de ADRs

Este directorio contiene las decisiones arquitectónicas clave para el sistema de Background Jobs de Sistema Bautista.

ADR-001: Ejecución con exec() + CLI Worker

Estado: ✅ Aprobado Área: Ejecución de Jobs

Decisión: Usar exec() + proceso PHP CLI para ejecutar jobs en background.

Resumen: Evaluamos múltiples opciones para ejecutar jobs de larga duración sin bloquear requests HTTP. Seleccionamos exec() + CLI worker por su simplicidad, compatibilidad universal y bajo overhead operacional. Descartamos pcntl_fork, ReactPHP/Amphp y Swoole/RoadRunner por limitaciones de compatibilidad, complejidad o requisito de cambio de runtime.

Impacto:

  • Request HTTP retorna inmediatamente (< 200ms)
  • Jobs pueden ejecutar 30+ minutos sin timeouts
  • Sin dependencias externas
  • Límite de ~50-100 jobs concurrentes

ADR-002: Tablas por Schema (Multi-Tenant)

Estado: ✅ Aprobado Área: Multi-Tenancy, Seguridad

Decisión: Crear tabla background_jobs en CADA schema de sucursal (LEVEL_SUCURSAL).

Resumen: Sistema Bautista usa PostgreSQL schema-based multi-tenancy. La tabla background_jobs debe respetar este aislamiento para prevenir violaciones de seguridad. Seleccionamos LEVEL_SUCURSAL (tabla por schema) sobre tabla única con filtros o Row Level Security para garantizar aislamiento por diseño.

Impacto:

  • Aislamiento completo entre sucursales
  • Consistente con arquitectura existente
  • Sin riesgo de data leakage por queries incorrectas
  • Queries consolidadas requieren UNION cross-schema

Relacionado: ADR-005


ADR-003: Polling HTTP → SSE (Fases)

Estado: ✅ Aprobado (Fase 1 implementándose) Área: Frontend, UX, Notificaciones

Decisión: Implementar notificación de jobs en 2 fases:

  1. Fase 1 (MVP): Polling HTTP cada 2 segundos
  2. Fase 2 (Optimización): Server-Sent Events + PostgreSQL NOTIFY

Resumen: Evaluamos 4 opciones de notificación (polling, SSE, WebSockets, Push). Seleccionamos enfoque por fases: polling para MVP rápido (2-3 semanas), SSE para optimización futura (latencia < 200ms). Descartamos WebSockets (complejidad sin beneficio) y Push Notifications (limitaciones desktop).

Impacto:

  • Fase 1: MVP funcional en 2-3 semanas, latencia 2-5s
  • Fase 2: Latencia near real-time 50-200ms
  • Progressive enhancement sin breaking changes

ADR-004: Wrapper Pattern (NO Refactoring)

Estado: ✅ Aprobado Área: Arquitectura, Services, Handlers

Decisión: Usar Wrapper Pattern para agregar ejecución asíncrona a services existentes SIN modificarlos.

Resumen: Queremos ejecutar servicios existentes (ej: FacturaService::batchInvoice()) en background sin refactorizarlos. Seleccionamos Wrapper Pattern: handler recibe service como dependencia, reconstruye DTO y delega ejecución. Descartamos refactorizar service (riesgo de bugs) o invertir dependencias (viola arquitectura).

Impacto:

  • CERO impacto en código legacy
  • Service funciona en modo sync (controller) y async (handler)
  • Rollback instantáneo con feature flag
  • Leve duplicación (construcción de DTO)

ADR-005: Schema Isolation en Background (CRÍTICO)

Estado: ✅ Aprobado Área: Multi-Tenancy, Seguridad Severity: 🔴 CRÍTICO

Decisión: Guardar schema en job y configurar search_path ANTES de ejecutar handler.

Resumen: Worker CLI NO tiene request HTTP ni X-Schema header. Si no configuramos schema, worker ejecuta en schema DEFAULT = violación crítica de multi-tenancy. Seleccionamos guardar schema en payload de job y configurar search_path antes de ejecutar. Mitigaciones obligatorias: exception si schema faltante, tests de integración multi-tenant, code review checklist.

Impacto:

  • Aislamiento garantizado por diseño
  • Handler transparente (no necesita código multi-tenant)
  • Tests detectan violación de aislamiento
  • Exception si schema faltante (fail-safe)

Relacionado: ADR-002


ADR-006: Two-Phase CLI Bootstrap

Estado: ✅ Aceptado Área: CLI Workers, DI Container, Contexto de Ejecución

Decisión: Usar bootstrap en dos fases para el worker CLI: Fase 1 lee job con PDO mínimo y extrae _context, Fase 2 bootstrapea DI container completo con Payload sintético.

Resumen: El worker CLI no tiene JWT. BatchInvoicingOrchestrator depende de Payload (vía AuditService y ArcaClientFactory). Solución: JobController almacena datos del JWT en _context dentro del payload del job. El worker lee _context con PDO mínimo (Fase 1) y construye un Payload sintético para inyectar en el DI container (Fase 2). Descartamos pasar credenciales como argumentos CLI (visible en ps aux) y almacenar JWT en BD (riesgo de seguridad).

Impacto:

  • Workers completamente autosuficientes (no necesitan JWT)
  • Contexto viaja con el job payload
  • Sin modificaciones a servicios existentes (consistente con ADR-004)

Relacionado: ADR-004, ADR-005


ADR-007: Tab-Scoped Job Bus via window.BautistaJobBus for Legacy PHP Bridge

Estado: ✅ Aceptado Área: Frontend, Legacy Bridge, Notificaciones

Decisión: Introducir window.BautistaJobBus — pub/sub tab-scoped inicializado en main.tsx a nivel de módulo (antes de que monte cualquier componente React) para proveer feedback inmediato a vistas PHP legacy.

Resumen: El JobNotificationsContext usa polling de 30 segundos, demasiado lento para flujos legacy interactivos. Se crea un bus pub/sub en memoria asignado a window.BautistaJobBus en scope de módulo en main.tsx, garantizando disponibilidad antes de DOMContentLoaded. Un IIFE vanilla (job-status-listener.js) en páginas legacy se suscribe al bus y dispara showAlertToast. El user ID se lee de <meta name="bautista-user-id"> para filtrar eventos. Descartamos BroadcastChannel (problemas en iframe contexts legacy), SharedWorker (requiere HTTPS) y polling-only (latencia inaceptable de 30s).

Impacto:

  • Feedback near real-time en vistas PHP legacy
  • Sin race condition con listener script (bus garantizado antes de DOMContentLoaded)
  • Solo tab-scoped; cross-tab sigue cubierto por polling de JobNotificationsContext

Relacionado: ADR-003


Referencias Cruzadas

Por Área

Multi-Tenancy & Seguridad:

Ejecución & Performance:

Frontend & UX:

Lectura Recomendada

Para implementar un nuevo job handler:

  1. ADR-004: Wrapper Pattern - Patrón de implementación
  2. ADR-005: Schema Isolation - Seguridad multi-tenant (CRITICO)
  3. ADR-006: Two-Phase CLI Bootstrap - Contexto CLI sin JWT

Para entender arquitectura general:

  1. ADR-001: Ejecución con exec() - Cómo se ejecutan jobs
  2. ADR-002: Tablas por Schema - Modelo de datos
  3. ADR-003: Polling → SSE - Notificaciones frontend React
  4. ADR-006: Two-Phase CLI Bootstrap - Bootstrap CLI
  5. ADR-007: Tab-Scoped Job Bus - Notificaciones legacy PHP

Última Actualización: 2026-02-23 Próxima Revisión: Después de implementar Fase 1 (recopilar feedback y ajustar ADRs)