Technical documentation
Decisiones arquitectónicas
ADR-001 — Kafka como bus central
Decisión: Todo el pipeline de ingesta pasa por Kafka (no HTTP directo a PostgreSQL).
Motivo: Desacoplamiento productor/consumidor, backpressure natural, replay de eventos, Dead Letter Queue para fallos.
Trade-off: Mayor complejidad operacional; el Routing Consumer requiere semáforos explícitos para evitar condiciones de carrera en commit de offsets.
ADR-002 — Union-Find para correlación de alertas
Decisión: El Correlation Engine usa Union-Find (algoritmo de conjuntos disjuntos) con complejidad O(N · α(N)).
Motivo: Las alertas relacionadas forman grupos naturales. Union-Find es cuasi-lineal, determinístico y fácil de serializar.
Trade-off: Requiere que las estrategias de correlación sean definidas explícitamente; no descubre correlaciones implícitas sin reglas.
ADR-003 — Persistencia políglota
Decisión: 5 motores de almacenamiento distintos para distintos tipos de dato:
- PostgreSQL: fuente de verdad relacional (incidentes, alertas, configuración)
- Neo4j: topología como grafo (relaciones entre recursos)
- OpenSearch: logs y eventos para búsqueda full-text
- Redis: cache TTL y broker Celery
- MinIO: objetos binarios (modelos ML, secretos EOE, artefactos de diagnóstico)
Motivo: Cada motor optimizado para su tipo de dato. Un solo PostgreSQL para todo no escala en búsqueda full-text ni en consultas de grafos.
Trade-off: Mayor complejidad operacional. Se mitiga con Docker Compose.
ADR-004 — Multi-tenant by design
Decisión: tenant_id como campo obligatorio en cada modelo ORM y filtro obligatorio en cada query. Extraído del JWT, nunca pasado como parámetro de request.
Motivo: Aislamiento total de datos entre tenants desde el inicio, sin retrofitting posterior.
Trade-off: Cualquier query que olvide el filtro tenant_id es un bug de seguridad. Se mitiga con el TenantContextMiddleware y revisión en PRs.
ADR-005 — Versiones de frontend fijas
Decisión: React 18, Vite 5, TypeScript 5.8, Tailwind 3 — fijos en los tres frontends.
Motivo: react-diff-viewer-continued (usado en el EOE Flow Designer para comparar versiones de flujos) solo soporta React ≤18. Los tres frontends deben estar alineados para evitar superficies de bugs no reproducibles.
Trade-off: No recibir mejoras de React 19 ni Tailwind 4.
ADR-006 — EOE independiente del backend core
Decisión: El Orchestration Engine (EOE) es un microservicio FastAPI separado (eoe-api, puerto 8002 interno) con su propio schema de base de datos y su propia capa de auth.
Motivo: Aislamiento de fallos (un flujo mal configurado no afecta la ingesta), equipos distintos pueden desarrollarlo independientemente, posibilidad de escalado horizontal separado.
Trade-off: Requiere un proxy nginx dedicado y una clave EOE_INTERNAL_API_KEY para la comunicación interna.
ADR-007 — CSS tokens en vez de clases Tailwind con alpha
Decisión: Todos los colores con alpha y valores de tema se definen como variables CSS en frontend/src/index.css. Las clases ECharts/Recharts usan cssVar() / chartTheme() de frontend/src/theme.ts.
Motivo: El selector [class*="bg-violet-5"][class*="\/2"] da falsa coincidencia con border-violet-500/20. Los overrides de light/dark mode requieren selectores CSS controlados.
Trade-off: Requiere disciplina de equipo — no quemar hex en .tsx.
ADR-008 — Celery con QueuePool vs NullPool según el contexto
Decisión: La API FastAPI usa QueuePool de SQLAlchemy; las tareas Celery y los Consumers Kafka usan NullPool.
Motivo: Celery crea procesos fork — compartir conexiones del pool entre procesos padre/hijo provoca SSL SYSCALL error y EOF detected. NullPool crea una conexión nueva por tarea y la cierra al terminar.
Trade-off: Mayor overhead de conexión en tareas Celery de alta frecuencia.
