codigo0/docs/ARQUITECTURA_CONTENT_ADAPTER.md

7.5 KiB

🏗️ ARQUITECTURA CONTENT ADAPTER - Sistema de Contenido Externo

Fecha: 2025-01-06
Estado: Implementación Inicial
Principio: ADITIVO - NO modifica código existente


🎯 OBJETIVO

Crear una capa de contenido externo que permita:

  • Gestionar contenido sin tocar código
  • Usar Content Pack JSON desde servidor
  • Fallback total a datos locales
  • La app funciona igual si el sistema falla

🧱 ARQUITECTURA

┌─────────────────────────────────────────┐
│         APP PWA (Frontend)               │
├─────────────────────────────────────────┤
│  ContentAdapterFactory                   │
│  ├── ExternalContentAdapter (NUEVO)      │  ← Content Pack JSON
│  └── LocalContentAdapter (EXISTENTE)    │  ← procedures.ts, drugs.ts
└─────────────────────────────────────────┘
           │                    │
           │                    │
           ▼                    ▼
┌──────────────────┐  ┌──────────────────┐
│ Content Pack JSON │  │ Datos Locales    │
│ (Servidor)       │  │ (Código actual)  │
└──────────────────┘  └──────────────────┘

📦 COMPONENTES CREADOS

1. ContentAdapter (src/services/content-adapter.ts)

Interfaz:

interface ContentAdapter {
  getProtocol(id: string): Procedure | null;
  getDrug(id: string): Drug | null;
  getAllProtocols(): Procedure[];
  getAllDrugs(): Drug[];
  isAvailable(): boolean;
}

Implementaciones:

  • LocalContentAdapter - Usa procedures.ts y drugs.ts (fallback)
  • ExternalContentAdapter - Usa Content Pack JSON (si disponible)

Factory:

  • ContentAdapterFactory.getAdapter() - Decide automáticamente
  • Prioridad: External → Local

2. Generador Content Pack (backend/src/services/pack-generator.js)

Funcionalidad:

  • Lee contenido publicado desde PostgreSQL
  • Incluye recursos multimedia asociados
  • Calcula hash SHA-256
  • Genera JSON optimizado

Método principal:

await packGenerator.generatePack(version, options)

3. API Content Pack (backend/src/routes/content-pack.js)

Endpoints:

  • GET /api/content-pack/latest.json - Pack más reciente
  • GET /api/content-pack/:version.json - Pack específico

Características:

  • Sin autenticación (público)
  • Cache headers (ETag, Cache-Control)
  • Generación on-the-fly si no existe archivo
  • 304 Not Modified si hash coincide

🔄 FLUJO DE FUNCIONAMIENTO

1. App Inicia

App → ContentAdapterFactory.getAdapter()
  → ExternalContentAdapter (intenta cargar pack)
    → Si existe: usa pack
    → Si no existe: fallback a LocalContentAdapter

2. Carga del Pack

ExternalContentAdapter
  → Intenta cache (localStorage)
  → Si no hay cache: descarga /api/content-pack/latest.json
  → Guarda en cache (24h)
  → Convierte ContentItem → Procedure/Drug

3. Fallback Automático

Si ExternalContentAdapter no está disponible:
  → LocalContentAdapter (siempre disponible)
  → Usa procedures.ts y drugs.ts
  → App funciona exactamente igual

📋 FORMATO CONTENT PACK JSON

{
  "metadata": {
    "version": "1.0.0",
    "generated_at": "2025-01-06T12:00:00Z",
    "hash": "sha256:abc123...",
    "total_items": 24,
    "total_resources": 0
  },
  "content": {
    "protocols": [
      {
        "id": "...",
        "type": "protocol",
        "slug": "rcp-adulto-svb",
        "title": "RCP Adulto - Soporte Vital Básico",
        "content": { ... },
        "priority": "critical",
        "status": "published",
        "version": "1.0.0"
      }
    ],
    "guides": [...],
    "drugs": [...],
    "checklists": [...]
  },
  "media": {
    "resources": [...],
    "associations": [...]
  }
}

GARANTÍAS DE SEGURIDAD

1. Fallback Total

  • Si el pack no existe → usa datos locales
  • Si el pack falla al cargar → usa datos locales
  • Si el pack está corrupto → usa datos locales
  • La app NUNCA crashea por falta de pack

2. No Modifica Código Existente

  • procedures.ts - NO modificado
  • drugs.ts - NO modificado
  • Componentes existentes - NO modificados
  • Service Worker - NO modificado
  • Rutas - NO modificadas

3. Compatibilidad Offline

  • Pack se cachea en localStorage
  • Funciona offline después de primera carga
  • Cache válido por 24 horas
  • Refresh manual disponible

🚀 USO EN LA APP

Opción 1: Usar directamente

import { getProtocol, getAllProtocols } from '@/services/content-adapter';

// Obtener protocolo (automáticamente usa pack o local)
const protocol = getProtocol('rcp-adulto-svb');

// Obtener todos (automáticamente usa pack o local)
const protocols = getAllProtocols();

Opción 2: Usar hook React

import { useContentAdapter } from '@/services/content-adapter';

function MyComponent() {
  const { getProtocol, getAllProtocols } = useContentAdapter();
  
  const protocol = getProtocol('rcp-adulto-svb');
  // ...
}

Opción 3: Forzar refresh

import { refreshContentPack } from '@/services/content-adapter';

// Forzar recarga del pack
await refreshContentPack();

🔧 CONFIGURACIÓN

Backend

Variables de entorno:

PACKS_DIR=/ruta/a/storage/packs  # Opcional

Estructura de directorios:

backend/
├── storage/
│   └── packs/
│       ├── pack-v1.0.0.json
│       └── pack-latest.json (symlink)

Frontend

No requiere configuración - Funciona automáticamente

Cache:

  • LocalStorage: content_pack y content_pack_time
  • TTL: 24 horas
  • Refresh manual disponible

📊 ESTADO ACTUAL

Implementado

  • ContentAdapter interface
  • LocalContentAdapter (fallback)
  • ExternalContentAdapter (pack)
  • ContentAdapterFactory
  • Generador de Content Pack
  • API endpoints para pack
  • Cache en localStorage
  • Conversión ContentItem → Procedure/Drug

Pendiente

  • Integrar en componentes existentes (opcional)
  • Service Worker para cache avanzado (opcional)
  • IndexedDB para packs grandes (opcional)
  • UI para forzar refresh (opcional)

🧪 TESTING

Verificar que funciona

// 1. Verificar adapter disponible
import { contentAdapter } from '@/services/content-adapter';
console.log('Adapter disponible:', contentAdapter.isAvailable());

// 2. Obtener protocolo
const protocol = contentAdapter.getProtocol('rcp-adulto-svb');
console.log('Protocolo:', protocol);

// 3. Verificar fallback
// Desconectar servidor → debería usar local

Verificar Content Pack

# Obtener pack
curl http://localhost:3000/api/content-pack/latest.json

# Verificar hash
# El hash debe coincidir con ETag header

⚠️ RESTRICCIONES CUMPLIDAS

  • NO modifica src/data/procedures.ts
  • NO modifica src/data/drugs.ts
  • NO modifica Service Worker
  • NO modifica rutas existentes
  • NO modifica componentes actuales
  • TODO es aditivo y desacoplado
  • Fallback total garantizado
  • App funciona igual si falla

Fin del Documento