Outbox e integraciones
Los efectos hacia el mundo exterior — avisar a una familia, crear un curso en Moodle, generar boletas — no pueden perderse si el servidor se cae, ni ejecutarse si la operación que los originó falló. El sistema lo garantiza con el patrón outbox sobre PostgreSQL (sin Redis ni brokers).
Cómo funciona
- El evento se inserta en la tabla
Outboxdentro de la misma transacción del cambio de dominio (la falta y su alerta se confirman o revierten juntas). - Un dispatcher reclama lotes pendientes de forma atómica (
FOR UPDATE SKIP LOCKED— varios workers sin duplicar trabajo). - Cada handler procesa con reintentos con backoff (5 intentos); si agota,
queda
FALLIDOy visible para administración.
Evidencia real de la simulación
Estado de la tabla tras el recorrido completo de este manual:
topic | status | count
----------------------+------------+-------
email.send | COMPLETADO | 1 ← comunicado de reunión de padres
moodle.provision | COMPLETADO | 5 ← matrículas y cursos aprovisionados
push.send | COMPLETADO | 3 ← falta de Ana, pago, comunicado
report-card.generate | COMPLETADO | 1 ← boletas del parcial cerrado
Cero pendientes, cero fallidos: todo lo que el dominio prometió, se despachó.
El panel para administración
Configuración → Sistema muestra los contadores por estado, el último error de cada evento fallido, y permite reintentar individualmente o todos los fallidos con un clic (por ejemplo, tras una caída del Moodle del colegio):

Los adapters
| Destino | Activación | Sin configurar |
|---|---|---|
| Email (SMTP) | SMTP_URL + SMTP_FROM |
Modo stub: registra en el log, el pipeline es idéntico |
| Push (FCM) | FCM_CREDENTIALS_PATH (service account de Firebase) |
Stub; además limpia tokens inválidos en cada envío real |
| Moodle | URL + token en Configuración → Integraciones (en BD, administrable; el token nunca se re-expone) | Stub; con conexión: aprovisionamiento idempotente vía mapeo uuid ↔ moodle_id y lectura de gradebook |
| Boletas PDF | Siempre activo | Se generan en cola (pg-boss) para no degradar el sistema en picos de cierre |
Qué NO va por outbox
El event emitter en memoria se usa solo para efectos internos no críticos, siempre después del commit. La regla del proyecto: si perder el efecto duele, va por outbox; si no, ni siquiera amerita un evento.