codigo0/docs/ARQUITECTURA_CONTENT_ADAPTER.md

319 lines
7.5 KiB
Markdown

# 🏗️ 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:**
```typescript
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:**
```javascript
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
```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
```typescript
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
```typescript
import { useContentAdapter } from '@/services/content-adapter';
function MyComponent() {
const { getProtocol, getAllProtocols } = useContentAdapter();
const protocol = getProtocol('rcp-adulto-svb');
// ...
}
```
### Opción 3: Forzar refresh
```typescript
import { refreshContentPack } from '@/services/content-adapter';
// Forzar recarga del pack
await refreshContentPack();
```
---
## 🔧 CONFIGURACIÓN
### Backend
**Variables de entorno:**
```env
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
- [x] ContentAdapter interface
- [x] LocalContentAdapter (fallback)
- [x] ExternalContentAdapter (pack)
- [x] ContentAdapterFactory
- [x] Generador de Content Pack
- [x] API endpoints para pack
- [x] Cache en localStorage
- [x] 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
```typescript
// 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
```bash
# 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**