Appearance
Background Jobs - Architecture Decision Records
Sistema: Background Jobs System Fecha: 2026-02-05 Estado: Aprobado
Documentos relacionados:
- Architecture: Background Jobs - Visión arquitectónica general
- Backend: Background Jobs System - Implementación técnica
Í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:
- Fase 1 (MVP): Polling HTTP cada 2 segundos
- 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)
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:
- ADR-004: Wrapper Pattern - Patrón de implementación
- ADR-005: Schema Isolation - Seguridad multi-tenant (CRITICO)
- ADR-006: Two-Phase CLI Bootstrap - Contexto CLI sin JWT
Para entender arquitectura general:
- ADR-001: Ejecución con exec() - Cómo se ejecutan jobs
- ADR-002: Tablas por Schema - Modelo de datos
- ADR-003: Polling → SSE - Notificaciones frontend React
- ADR-006: Two-Phase CLI Bootstrap - Bootstrap CLI
- 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)