Arquitectura
SoftSys Edu Analytics es una plataforma multi-tenant de analítica de aprendizaje para Moodle. Captura eventos vía un plugin nativo, los procesa en infraestructura edge serverless, mantiene un OLTP de baja latencia, materializa informes precomputados y los sirve a una SPA — todo bajo una superficie unificada de política que aísla rigurosamente los datos por tenant y rol.
Componentes
Sección titulada «Componentes»| Pieza | Función |
|---|---|
Plugin Moodle (local_ssea) | Captura eventos, sincroniza dimensiones, emite JWT con rol y course_ids |
Ingest (ssea-ingest) | Acepta eventos en el edge, normaliza, valida, encola |
Cola de eventos + Processor (ssea-processor) | Deduplicación, upsert en OLTP |
| OLTP | Eventos, dimensiones (users, courses, enrollments), agregados |
Report worker (ssea-report-worker) | Cron horario: agrega y emite artifacts JSON+CSV + snapshots |
| Almacenamiento de objetos | Artifacts pre-computados de informes, exportes, archivos diarios |
| Snapshots key-value | Resumen del último artifact por tenant + tipo (TTL 35 min) |
API (ssea-api) | REST autenticado; superficie de política que gobierna cada lectura |
Dashboard (ssea-dashboard) | SPA React; consume sólo lecturas pre-calculadas |
Router de dominio (ssea-router) | Resuelve dominios custom (analytics.cliente.edu) al tenant correcto |
Alerts (ssea-alerts) | Cron horario; calcula scores de riesgo y dispara webhooks |
Export (ssea-export) | Streams a CSV/JSON role-gated; archivado diario JSONL |
Superficie de política
Sección titulada «Superficie de política»Entre la autenticación y el acceso a datos vive una única superficie de política. Cada endpoint declara la capability que requiere; un módulo central decide allow/deny y construye el filtro de scope que se concatena al SQL.
Request ↓authenticate() ← extrae rol, tenant_id, course_ids ↓policy.decide({ ← un solo punto de decisión ctx, capability, resource: {tenant_id, id}}) ↓ allow deny ├─→ scope.clause aplicado al SQL → datos ↓ └─→ audit row vía waitUntil envelope uniforme + audit row- 28 capabilities registradas en producción (matriz completa).
- Decisiones se asientan en una tabla de auditoría unificada (
policy_audit_log) con discriminadoresactor_roleyacting_as_tenant. - Una prueba de contrato build-time (
capabilities.contract.test.ts) bloquea CI si un endpoint nuevo intenta llegar a producción sin capability registrada o sin tests cross-tenant + cross-role. - La superficie es extensible a roles futuros (e.g., reseller) sin tocar handlers existentes — sólo se registran nuevas reglas.
Aislamiento multi-tenant
Sección titulada «Aislamiento multi-tenant»Garantía no-negociable (constitución, principio I): ningún rol distinto de superadmin puede observar datos de otro tenant.
- Toda consulta al OLTP incluye
WHERE tenant_id = ?antes de cualquier otro filtro. - Toda clave de objeto en almacenamiento se prefija con
{tenant_id}/. - Los JWT firmados embeben
tenant_idcomo claim obligatorio; el middleware rechaza tokens sin él. - Los super-admins que entran en otro tenant lo hacen explícitamente vía
tenant.switch; cada lectura se registra conacting_as_tenantpuesto. - Tests cross-tenant son obligatorios para cada endpoint nuevo, validados por la prueba de contrato build-time.
Almacenamiento
Sección titulada «Almacenamiento»| Capa | Naturaleza | Retención |
|---|---|---|
| OLTP | Eventos crudos + dimensiones + agregados horarios/diarios | Variable por tenant + plan (retención de datos) |
Almacenamiento de objetos — reports/ | Artifacts JSON+CSV de informes (último por tipo + histórico) | 90 días por defecto |
Almacenamiento de objetos — archive/ | JSONL diario de eventos | 365 días |
| Snapshots key-value | Último artifact por (tenant, tipo) | TTL 35 minutos |
Audit log (policy_audit_log) | Una fila por decisión de la política | 90 días general; 365 días para operadores |
Branding y dominios custom
Sección titulada «Branding y dominios custom»Cada tenant puede operar bajo un dominio propio (analytics.cliente.edu). El router resuelve el host al tenant, aplica branding (logo, colores, nombre) y expone el dashboard SPA sin filtrar referencias a infraestructura. La marca SoftSys aparece sólo bajo el dominio principal; resellers y clientes finales obtienen un producto enteramente blanqueado.
Presupuestos de rendimiento
Sección titulada «Presupuestos de rendimiento»| Componente | Presupuesto |
|---|---|
| Ingest de eventos (P95) | < 100 ms en el edge |
Decisión de política (policy.decide, parte síncrona, P95) | < 1 ms in-process (verificado en CI por tests/policy/perf.test.ts) |
| Audit log list endpoint (P95, 100 k filas) | < 500 ms |
| Dashboard inicial (cold cache) | < 2 s |
| Generación de informe horario | < 30 s para tenant típico |
El presupuesto de la decisión de política se enforce en CI: una regresión que cruce el doble del nominal (2 ms en runners CI) falla npm test. Esto previene que cambios en el módulo de política impacten silenciosamente la latencia de cada request autenticada.
Cadencia operativa
Sección titulada «Cadencia operativa»| Tarea | Trigger | Frecuencia |
|---|---|---|
| Ingest de eventos | Push del plugin | Continuo (500+ req/min) |
| Sync incremental Moodle ↔ OLTP | Plugin scheduled task | 5 min |
| Sync completo Moodle ↔ OLTP | Plugin scheduled task | Diario, madrugada |
| Generación de informes | Cron en report worker | Cada hora |
| Cálculo de risk scores | Cron en alerts | Cada hora |
| Archivado diario JSONL | Cron en export | Diario |
| Limpieza de audit log | Cron en api (futuro) | Diario |
Para profundizar
Sección titulada «Para profundizar»- Flujo de datos — el ciclo de vida de un request, paso a paso.
- Roles y permisos — matriz completa, envelope de denegación, auditoría.
- Tipos de informe — catálogo y capabilities por reporte.
- Seguridad — modelo de amenazas, GDPR, criptografía.