Modelo de datos — diagrama ER completo¶
Esta página presenta el esquema relacional completo de Custodiam: las dieciocho tablas de la base de datos de negocio, agrupadas por módulo, más un mapa de las relaciones que cruzan los límites de un módulo a otro.
Es el complemento del Modelo de datos, que explica el patrón de diseño (catálogo + instancias + JSONB + enum discriminador, ADR-025); aquí el foco es el mapa entidad-relación de todo el esquema.
Alcance del diagrama
El esquema corresponde a la base de datos de negocio (custodiam), gestionada con SQLModel y migrada con Alembic. La base de datos de Keycloak (custodiam_kc, ~70 tablas internas del servidor de identidad) es independiente y queda fuera de este diagrama, coherente con la separación de ADR-009.
Convenciones del diagrama¶
- Clave primaria (
primary_key):idde tipouuid, generado en la capa de aplicación (no en la base de datos). - Clave foránea (
foreign_key): referencia a la clave primaria de otra tabla. unique: columna con restricción de unicidad.- Las relaciones se dibujan en notación crow's foot desde el lado "uno" (clave primaria) hacia el lado "muchos" (clave foránea).
- Por legibilidad, los índices compuestos, los valores por defecto y las cláusulas
ON DELETEno se representan en el diagrama; se describen en las notas de modelado.
Módulo Voluntarios¶
El núcleo del dominio de personas. La tabla voluntarios es la raíz; a su alrededor cuelgan los roles (con vigencia temporal), las disponibilidades por día, las acreditaciones y tallas (patrón catálogo + instancias de ADR-025), los contactos de emergencia y el historial de actividad.
voluntario_roles es una tabla intermedia con vigencia (fecha_desde/fecha_hasta): permite reasignar el mismo rol en periodos distintos. voluntario_eventos es el historial de actividad del voluntario (altas, bajas, cambios de rol, fichajes, asignaciones), con payload en JSONB para el contexto de cada evento.
Módulo Servicios y fichaje¶
Un servicio (preventivo, emergencia, formación u otro) recorre una máquina de estados borrador → publicado → activo → cerrado. Los voluntarios se asocian a un servicio mediante inscripciones_servicio (con un discriminador que distingue inscrito de convocado) y registran su presencia en fichajes.
Tanto inscripciones_servicio como fichajes referencian además al voluntario (voluntario_id); esas relaciones cruzan al módulo Voluntarios y se ven en el mapa global.
Módulo Inventario y ubicaciones¶
El inventario separa materiales y vehiculos en tablas distintas (campos divergentes y distinto corte de permisos), ambos ubicados opcionalmente en un catálogo común de ubicaciones con coordenadas. La asignación de material se modela con asignaciones_material, que apunta a exactamente uno de tres destinos (voluntario, servicio o vehículo); la asignación de vehículos a servicios va en asignaciones_vehiculo. La "devolución" es un borrado lógico: una asignación está activa mientras fecha_devolucion sea nula.
La restricción de "exactamente un destino" de asignaciones_material (un material se asigna a un voluntario o a un servicio o como dotación fija de un vehículo, nunca a más de uno) se garantiza con una restricción de tabla, detallada en ADR-031. Las columnas voluntario_id y servicio_id de las asignaciones cruzan a otros módulos: ver el mapa global.
Módulo Notificaciones¶
Cada voluntario registra sus tokens de envío (dispositivos, uno por plataforma) y cada emisión de aviso queda registrada en notificaciones con sus contadores de envío.
Ambas tablas se enlazan con otros módulos: dispositivos pertenece a un voluntario y notificaciones referencia opcionalmente al servicio que la motivó. Esas relaciones se ven en el mapa global.
Mapa global de relaciones¶
Las siete claves foráneas que cruzan los límites de un módulo son las que cosen el esquema. El módulo Voluntarios y el módulo Servicios actúan como destinos comunes; los módulos Inventario y Notificaciones apuntan hacia ellos.
Enumerados¶
El esquema usa trece tipos enumerados de PostgreSQL como discriminadores y máquinas de estado:
| Enumerado | Valores | Usado en |
|---|---|---|
estado_voluntario |
activo · baja · suspendido | voluntarios.estado |
categoria_acreditacion |
licencia_oficial · formacion_interna · otro | tipos_acreditacion.categoria, acreditaciones.categoria |
tipo_servicio |
preventivo · emergencia · formacion · otro | servicios.tipo |
estado_servicio |
borrador · publicado · activo · cerrado | servicios.estado |
tipo_inscripcion |
inscrito · convocado | inscripciones_servicio.tipo |
tipo_material |
personal · prestable · servicio | materiales.tipo |
estado_inventario |
operativo · averiado · perdido · en_uso | materiales.estado, vehiculos.estado |
tipo_vehiculo |
furgoneta · pick_up · ambulancia · remolque | vehiculos.tipo |
tipo_asignacion_material |
personal · prestamo · servicio · dotacion_vehiculo | asignaciones_material.tipo |
plataforma_dispositivo |
android · ios · web | dispositivos.plataforma |
tipo_notificacion |
emergencia · servicio · recordatorio · sistema | notificaciones.tipo |
prioridad_notificacion |
critica · alta · normal · baja | notificaciones.prioridad |
tipo_evento_voluntario |
alta · baja · anonimizacion · cambio de rol · fichajes · inscripciones · asignaciones de material | voluntario_eventos.tipo_evento |
Notas de modelado¶
- Claves primarias
uuid: todas las tablas usanidde tipouuidgenerado en la capa de aplicación, no por la base de datos. No se usan secuencias ni enteros autoincrementales. - Marcas de tiempo: las columnas de auditoría (
created_at,updated_at,enviada_at,ultima_actualizacion) sontimestamptz(con zona horaria); las marcas de dominio (fecha_inicio,hora_entrada,fecha_asignacion, etc.) sontimestampsin zona. - Borrado lógico, nunca físico: las bajas y devoluciones no eliminan filas. Un voluntario se da de baja con
estado = baja(o se anonimiza); una asignación se devuelve poniendofecha_devolucion. No hay ninguna cláusulaON DELETE CASCADEen el esquema: las únicas claves foráneas con borrado restringido explícito son las demateriales/vehiculos/asignaciones_materialhacia las tablas que referencian. - Columnas
JSONB:roles.permisos,tipos_acreditacion.campos_schema,acreditaciones.datos_especificosyvoluntario_eventos.payload. La matriz real de permisos por rol no vive enroles.permisos, sino espejada en código backend y cliente (ADR-013). - Catálogos pre-poblados:
roles,tipos_acreditacionytipos_equipamientose cargan con datos canónicos mediante data migrations de Alembic, versionados en Git como parte del esquema. - Valores derivados no persistidos: algunos atributos que la API expone (como el recuento de inscritos de un servicio o la duración de un fichaje) se calculan en consulta y no son columnas físicas.
Referencias¶
- Modelo de datos — el patrón de diseño catálogo + instancias + JSONB.
- ADR-002 SQLModel — ORM unificado.
- ADR-003 Alembic — migraciones de esquema.
- ADR-009 Dos bases de datos separadas — por qué la base de negocio y la de Keycloak van aparte.
- ADR-025 Modelo extensible — el patrón formal completo.
- ADR-031 Modelo material↔vehículo — la asignación con destino único.