17 KiB
🅲 FASE C - MODELO DE DATOS CANÓNICO
Proyecto: Guía TES - Sistema de Gestión de Contenido
Fecha: 2025-01-06
Arquitecto: Sistema de Contenido
Estado: 📋 PLANIFICACIÓN
🎯 OBJETIVO
Definir el modelo de datos lógico (conceptual) que soporte:
- ✅ Base de datos en nuestro servidor (PostgreSQL/SQLite)
- ✅ Panel de administración completo
- ✅ Exportación a JSON (Content Pack)
- ✅ Consumo offline por la app PWA
- ✅ Versionado y reversibilidad
- ✅ Estados de validación (draft → review → approved → published)
⚠️ IMPORTANTE: Este es un modelo CONCEPTUAL, no tecnológico. Define QUÉ datos necesitamos, no CÓMO implementarlos.
📊 ENTIDADES PRINCIPALES
1. CONTENIDO (Content Items)
Representa cualquier pieza de contenido: protocolos, guías, manual, fármacos, checklists.
1.1 Identificación y Clasificación
ContentItem {
// IDENTIFICACIÓN ÚNICA
id: UUID // ID único e inmutable
slug: String // Identificador legible (ej: "control-hemorragias")
version: String // Versión semántica (ej: "1.2.3")
latest_version: Boolean // ¿Es la última versión?
// CLASIFICACIÓN
type: Enum // 'protocol' | 'guide' | 'manual' | 'drug' | 'checklist'
category: String // Categoría clínica (ej: "soporte_vital", "trauma")
subcategory: String? // Subcategoría opcional (ej: "hemorragia", "shock")
// CAPA FUNCIONAL (CRÍTICO: NO MEZCLAR)
functional_layer: Enum // 'operativa' | 'formativa' | 'referencia'
// METADATOS BÁSICOS
title: String // Título completo
short_title: String? // Título corto (para UI)
description: String // Descripción breve
keywords: String[] // Palabras clave para búsqueda
// CONTENIDO ESPECÍFICO
content: JSON // Estructura específica por tipo (ver abajo)
// PRIORIDAD Y CLASIFICACIÓN CLÍNICA
priority: Enum // 'critical' | 'high' | 'medium' | 'low'
clinical_priority: Enum? // Prioridad clínica específica (si aplica)
age_group: Enum? // 'adulto' | 'pediatrico' | 'neonatal' | 'todos'
// ESTADO Y VALIDACIÓN
status: Enum // 'draft' | 'in_review' | 'approved' | 'published' | 'archived'
validation_workflow: JSON? // Historial de validación (ver abajo)
// RELACIONES
related_content: UUID[] // IDs de contenido relacionado (bidireccional)
parent_content: UUID? // Si es versión de otro contenido
replaces_content: UUID? // Si reemplaza a otro contenido
// RECURSOS ASOCIADOS
media_resources: UUID[] // IDs de recursos multimedia asociados
// METADATOS DE GESTIÓN
created_by: UUID // Usuario que creó
created_at: Timestamp // Fecha de creación
updated_by: UUID // Usuario que actualizó
updated_at: Timestamp // Fecha de última actualización
published_at: Timestamp? // Fecha de publicación (si está published)
published_by: UUID? // Usuario que publicó
// FUENTE Y TRAZABILIDAD
source_guideline: String? // Fuente clínica (ej: "Manual TES Digital", "ERC 2021")
source_reference: String? // Referencia específica (ej: "BLOQUE_03_6_CONTROL_HEMORRAGIAS.md")
// METADATOS ADICIONALES
metadata: JSON // Metadatos flexibles (tiempo_lectura, dificultad, etc.)
}
1.2 Estructura de content por Tipo
PROTOCOLO (Operativa):
{
"steps": [
{
"order": 1,
"text": "Texto del paso",
"critical": false,
"verification": "¿Verificación opcional?",
"warnings": ["Advertencia 1", "Advertencia 2"]
}
],
"warnings": ["Advertencia global 1"],
"key_points": ["Punto clave 1"],
"equipment": ["Equipo 1", "Equipo 2"],
"drugs": ["Fármaco 1", "Fármaco 2"],
"checklist_mode": true,
"checklist_steps": ["Paso checklist 1"]
}
GUÍA (Formativa):
{
"secciones": [
{
"numero": 1,
"titulo": "Título de sección",
"archivo": "SECCION_01_NOMBRE.md",
"ruta": "/docs/consolidado/SECCION_01_NOMBRE.md",
"contenido_markdown": "...",
"tiempo_estimado": 5
}
],
"protocolo_operativo": {
"id": "uuid-protocolo",
"titulo": "Título del protocolo",
"ruta": "/ruta-protocolo"
},
"scorm_available": false,
"tiempo_total_estimado": 40
}
MANUAL (Referencia):
{
"parte": 1,
"parte_nombre": "Nombre de parte",
"bloque": 0,
"bloque_nombre": "Nombre de bloque",
"capitulo": "1.1.1",
"ruta_archivo": "/manual/BLOQUE_XX/ARCHIVO.md",
"ruta_url": "/manual/parte-i/bloque-0/1.1.1",
"contenido_markdown": "...",
"nivel_dificultad": "basico",
"importancia": "alta",
"tiempo_lectura": 15,
"navegacion": {
"anterior": "1.0.1",
"siguiente": "1.1.2",
"relacionados": ["1.1.2", "1.1.3"]
}
}
FÁRMACO (Referencia):
{
"generic_name": "Nombre genérico",
"trade_name": "Nombre comercial",
"category": "cardiovascular",
"presentation": "Presentación",
"adult_dose": "Dosis adulto",
"pediatric_dose": "Dosis pediátrica",
"routes": ["IV", "IM"],
"dilution": "Dilución",
"indications": ["Indicación 1"],
"contraindications": ["Contraindicación 1"],
"side_effects": ["Efecto adverso 1"],
"antidote": "Antídoto",
"notes": ["Nota 1"],
"critical_points": ["Punto crítico TES 1"],
"source": "Fuente"
}
CHECKLIST (Operativa):
{
"phase": "pre_escena",
"sections": [
{
"id": "seccion-1",
"title": "Título sección",
"category": "oxigeno",
"items": [
{
"id": "item-1",
"text": "Texto del item",
"critical": false
}
],
"notes": "Notas opcionales"
}
]
}
2. RECURSOS MULTIMEDIA (Media Resources)
Representa imágenes, vídeos y otros recursos visuales.
MediaResource {
// IDENTIFICACIÓN
id: UUID // ID único
filename: String // Nombre del archivo (ej: "hemorragia_presion_directa.jpg")
path: String // Ruta relativa (ej: "/assets/infografias/hemorragia/...")
url: String? // URL completa (si está en CDN)
// TIPO Y FORMATO
type: Enum // 'image' | 'video' | 'audio' | 'document'
format: String // 'jpg' | 'png' | 'svg' | 'mp4' | 'webm' | 'pdf'
mime_type: String // 'image/jpeg', 'video/mp4', etc.
// METADATOS VISUALES
alt: String // Texto alternativo (accesibilidad)
caption: String? // Caption opcional
title: String? // Título del recurso
// CLASIFICACIÓN
functional_layer: Enum // 'operativa' | 'formativa' | 'referencia'
category: String? // Categoría (ej: "hemorragia", "rcp")
tags: String[] // Tags para búsqueda
// DIMENSIONES Y TAMAÑO
width: Integer? // Ancho (para imágenes/vídeos)
height: Integer? // Alto (para imágenes/vídeos)
file_size: Integer // Tamaño en bytes
duration: Integer? // Duración en segundos (para vídeos/audio)
// PRIORIDAD Y USO
priority: Enum // 'critical' | 'high' | 'medium' | 'low'
usage_type: Enum[] // ['operativo', 'formativo'] - dónde se usa
// FUENTE Y ATRIBUCIÓN
source: String? // Fuente del recurso
attribution: String? // Atribución si es necesario
license: String? // Licencia (ej: "CC BY 4.0")
// ESTADO
status: Enum // 'draft' | 'approved' | 'published' | 'archived'
// METADATOS DE GESTIÓN
created_by: UUID
created_at: Timestamp
updated_by: UUID
updated_at: Timestamp
uploaded_at: Timestamp // Fecha de subida
// METADATOS ADICIONALES
metadata: JSON // Metadatos flexibles
}
3. RELACIONES CONTENIDO ⇄ RECURSOS
Tabla de relación muchos-a-muchos entre contenido y recursos multimedia.
ContentResourceRelation {
id: UUID
content_id: UUID // FK a ContentItem
resource_id: UUID // FK a MediaResource
// CONTEXTO DE LA RELACIÓN
context: String? // Dónde se usa (ej: "step-3", "seccion-2", "header")
order: Integer? // Orden de aparición
is_primary: Boolean // ¿Es recurso principal?
// METADATOS
created_at: Timestamp
created_by: UUID
}
4. RELACIONES CONTENIDO ⇄ CONTENIDO
Tabla de relación bidireccional entre contenidos (Protocolo ↔ Guía ↔ Manual).
ContentRelation {
id: UUID
source_content_id: UUID // FK a ContentItem (origen)
target_content_id: UUID // FK a ContentItem (destino)
// TIPO DE RELACIÓN
relation_type: Enum // 'protocol_to_guide' | 'guide_to_protocol' |
// 'protocol_to_manual' | 'guide_to_manual' |
// 'manual_to_protocol' | 'manual_to_guide'
// METADATOS
is_bidirectional: Boolean // ¿Es bidireccional?
order: Integer? // Orden de aparición
metadata: JSON? // Metadatos adicionales
created_at: Timestamp
created_by: UUID
}
5. VERSIONADO
Historial de versiones de contenido.
ContentVersion {
id: UUID
content_id: UUID // FK a ContentItem (versión actual)
version: String // Versión semántica (ej: "1.2.3")
previous_version_id: UUID? // FK a ContentVersion (versión anterior)
// CAMBIOS
change_summary: String // Resumen de cambios
change_details: JSON // Detalles de cambios (qué cambió)
change_type: Enum // 'major' | 'minor' | 'patch'
// METADATOS
created_at: Timestamp
created_by: UUID
published_at: Timestamp?
published_by: UUID?
}
6. VALIDACIÓN Y WORKFLOW
Historial de validación y estados.
ValidationWorkflow {
id: UUID
content_id: UUID // FK a ContentItem
// ESTADO ACTUAL
current_status: Enum // 'draft' | 'in_review' | 'approved' | 'rejected' | 'published'
// HISTORIAL
history: JSON[] // Array de eventos:
// [
// {
// "status": "draft",
// "timestamp": "...",
// "user_id": "...",
// "notes": "..."
// },
// {
// "status": "in_review",
// "timestamp": "...",
// "user_id": "...",
// "reviewer_id": "...",
// "notes": "..."
// }
// ]
// METADATOS
submitted_at: Timestamp?
submitted_by: UUID?
reviewed_at: Timestamp?
reviewed_by: UUID?
approved_at: Timestamp?
approved_by: UUID?
rejected_at: Timestamp?
rejected_by: UUID?
rejection_reason: String?
updated_at: Timestamp
}
7. USUARIOS Y ROLES
Gestión de usuarios del panel de administración.
User {
id: UUID
email: String // Email único
username: String // Nombre de usuario
password_hash: String // Hash de contraseña
role: Enum // 'super_admin' | 'admin' | 'editor_clinico' |
// 'editor_formativo' | 'revisor' | 'viewer' |
// 'tes_validador' | 'formador' | 'medico'
// PERMISOS GRANULARES
permissions: JSON // {
// "content:create": true,
// "content:edit": true,
// "content:delete": false,
// "content:publish": false,
// "content:validate": true,
// "media:upload": true,
// ...
// }
// ESTADO
is_active: Boolean
last_login: Timestamp?
// METADATOS
created_at: Timestamp
updated_at: Timestamp
}
8. AUDITORÍA
Registro de todas las acciones importantes.
AuditLog {
id: UUID
entity_type: String // 'content_item' | 'media_resource' | 'user' | ...
entity_id: UUID // ID de la entidad
// ACCIÓN
action: Enum // 'create' | 'update' | 'delete' | 'publish' |
// 'approve' | 'reject' | 'submit' | ...
action_details: JSON? // Detalles de la acción
// USUARIO Y CONTEXTO
user_id: UUID // FK a User
user_role: String // Rol del usuario (snapshot)
ip_address: String?
user_agent: String?
// METADATOS
timestamp: Timestamp
metadata: JSON? // Metadatos adicionales
}
🔗 RELACIONES ENTRE ENTIDADES
ContentItem (1) ──< (N) ContentResourceRelation (N) >── (1) MediaResource
ContentItem (1) ──< (N) ContentRelation (N) >── (1) ContentItem
ContentItem (1) ──< (N) ContentVersion
ContentItem (1) ──< (1) ValidationWorkflow
User (1) ──< (N) ContentItem (created_by, updated_by, published_by)
User (1) ──< (N) MediaResource (created_by, updated_by)
User (1) ──< (N) AuditLog
📦 EXPORTACIÓN A CONTENT PACK
El Content Pack JSON se genera desde estas entidades:
{
"version": "1.0.0",
"generatedAt": "2025-01-06T12:00:00Z",
"hash": "sha256:...",
"content": {
"protocols": [
// ContentItem donde type='protocol' AND status='published' AND functional_layer='operativa'
// Incluye: id, slug, title, short_title, description, content, priority, etc.
],
"guides": [
// ContentItem donde type='guide' AND status='published' AND functional_layer='formativa'
],
"drugs": [
// ContentItem donde type='drug' AND status='published' AND functional_layer='referencia'
],
"checklists": [
// ContentItem donde type='checklist' AND status='published' AND functional_layer='operativa'
],
"manuals": [
// ContentItem donde type='manual' AND status='published' AND functional_layer='referencia'
]
},
"media": {
"images": [
// MediaResource donde type='image' AND status='published'
// Incluye: id, filename, path, url, alt, caption, etc.
],
"videos": [
// MediaResource donde type='video' AND status='published'
]
},
"relations": [
// ContentRelation donde ambos contenidos están published
// Incluye: source_content_id, target_content_id, relation_type
],
"content_resource_relations": [
// ContentResourceRelation donde ambos están published
// Incluye: content_id, resource_id, context, order
]
}
🎯 PRINCIPIOS DE DISEÑO
1. Separación de Capas Funcionales
CRÍTICO: El campo functional_layer garantiza que:
- Contenido operativo NO se mezcle con formativo
- Contenido formativo NO se mezcle con referencia
- Cada capa tiene su propia estructura de
content
2. Versionado Semántico
- Major (X.0.0): Cambios incompatibles
- Minor (0.X.0): Nuevas funcionalidades compatibles
- Patch (0.0.X): Correcciones compatibles
3. Estados y Workflow
draft → in_review → approved → published
↓ ↓ ↓
rejected rejected archived
4. Trazabilidad Completa
- Todo cambio queda registrado en
AuditLog - Historial de versiones en
ContentVersion - Workflow de validación en
ValidationWorkflow
5. Relaciones Bidireccionales
ContentRelationpermite relaciones bidireccionales- Campo
is_bidirectionalindica si la relación es simétrica - El Content Pack incluye todas las relaciones
6. Offline-First
- El Content Pack es autocontenido (incluye todo lo necesario)
- Las relaciones están incluidas en el pack
- Los recursos multimedia tienen URLs relativas o absolutas
📝 NOTAS TÉCNICAS
Campos JSON Flexibles
Los campos content y metadata son JSON para permitir:
- Estructuras específicas por tipo de contenido
- Extensibilidad sin cambios de esquema
- Metadatos adicionales sin migraciones
UUIDs vs IDs Numéricos
- UUIDs para IDs principales (mejor para distribución, sin colisiones)
- Strings para slugs (legibles, SEO-friendly)
Timestamps
- Todos los timestamps en formato ISO 8601
- Timezone: UTC
- Incluir
created_atyupdated_aten todas las entidades principales
Soft Deletes
- No eliminar físicamente contenido publicado
- Usar campo
status='archived'para "eliminación" - Mantener historial completo
🚀 PRÓXIMOS PASOS
- Validar modelo con stakeholders clínicos
- Diseñar esquema de base de datos (PostgreSQL recomendado)
- Implementar migraciones de base de datos
- Crear API REST para panel de administración
- Implementar generación de Content Pack desde base de datos
- Crear panel de administración con CRUD completo
Última actualización: 2025-01-06
Estado: ✅ Modelo conceptual completo