BACKEND-ARCH-001: Arquitectura de Servicios Backend
Version: 1.0.0
Fecha: 2025-12-09
Estado: APROBADO
Iteracion: DroneValidation-2 (ARCH-001)
1. Tabla de Contenidos
2. Resumen Ejecutivo
Este documento especifica la arquitectura del backend de MedTime, siguiendo el principio de Zero-Knowledge Server. El servidor:
- NO puede descifrar datos de salud (PHI)
- Solo almacena blobs cifrados opacos
- Provee sincronizacion, catalogo publico, y OCR
flowchart LR
subgraph Cliente["CLIENTE (95% de la logica)"]
C1[Datos en claro - PHI]
C2[Cifrado E2E]
C3[Logica de negocio]
C4[UI/UX completa]
end
subgraph Servidor["SERVIDOR (5% de la logica)"]
S1[Blobs opacos cifrados]
S2[Sin capacidad de leer]
S3[Solo relay y storage]
S4[Metadata NO-PHI]
end
Cliente -->|"Cifrado E2E"| Servidor
subgraph Restricciones["El servidor NUNCA tiene acceso a:"]
R1[Nombres de medicamentos]
R2[Horarios de tomas]
R3[Datos del paciente]
R4[Historial medico]
end
style Cliente fill:#e1f5fe,stroke:#01579b
style Servidor fill:#fff3e0,stroke:#e65100
style Restricciones fill:#ffebee,stroke:#c62828
3. Principios Arquitectonicos
3.1. Zero-Knowledge by Design
| Principio |
Implementacion |
| PHI nunca en servidor |
Cifrado E2E antes de enviar |
| Blobs opacos |
encrypted_blob: bytea sin schema |
| Metadata minima |
Solo user_id, entity_type, updated_at |
| Logs sanitizados |
Sin PHI en logs, solo IDs y status |
3.2. Arquitectura Minimalista
backend_scope:
in_scope:
- Almacenamiento de blobs cifrados
- Sincronizacion entre dispositivos
- Catalogo publico de medicamentos
- OCR de recetas (procesamiento efimero)
- Autenticacion y autorizacion
- Push notifications (metadata only)
out_of_scope:
- Logica de negocio de medicamentos
- Calculos de adherencia
- Generacion de reportes
- Cualquier procesamiento de PHI
4. Vista de Contenedores (C4 Nivel 2)
flowchart TB
subgraph Mobile["Mobile Apps"]
iOS[iOS App]
Android[Android App]
end
Mobile -->|"HTTPS/TLS 1.3"| Gateway
subgraph Gateway["API GATEWAY<br/>(Kong / AWS API GW)"]
GW[Rate Limiting + Routing]
end
Gateway --> AuthSvc
Gateway --> SyncSvc
Gateway --> CatalogSvc
Gateway --> OcrSvc
subgraph Services["Backend Services"]
AuthSvc[Auth Service]
SyncSvc[Sync Service]
CatalogSvc[Catalog Service]
OcrSvc[OCR Service]
end
AuthSvc --> SupaAuth[(Supabase Auth)]
SyncSvc --> SupaDB[(Supabase Database)]
CatalogSvc --> DrugDB[(External Drug DB)]
OcrSvc --> VisionAPI[Cloud Vision API]
style Mobile fill:#e3f2fd,stroke:#1565c0
style Gateway fill:#fff8e1,stroke:#f9a825
style Services fill:#f3e5f5,stroke:#7b1fa2
style SupaAuth fill:#e8f5e9,stroke:#2e7d32
style SupaDB fill:#e8f5e9,stroke:#2e7d32
style DrugDB fill:#fce4ec,stroke:#c2185b
style VisionAPI fill:#fce4ec,stroke:#c2185b
4.1. Descripcion de Contenedores
| Contenedor |
Tecnologia |
Responsabilidad |
| API Gateway |
Kong / AWS API GW |
Rate limiting, routing, TLS termination |
| Auth Service |
Supabase Auth |
JWT, OAuth, session management |
| Sync Service |
Edge Functions |
Blob storage, conflict detection, push/pull |
| Catalog Service |
Edge Functions |
Medicamentos publicos, busqueda |
| OCR Service |
Cloud Function |
Vision API, parsing efimero |
5. Vista de Componentes (C4 Nivel 3)
5.1. API Gateway
flowchart TB
Request([INCOMING REQUEST])
Request --> TLS
subgraph Pipeline["API Gateway Pipeline"]
TLS["TLS Termination<br/>(TLS 1.3 only)"]
Rate["Rate Limiter<br/>(per tier/endpoint)"]
JWT["JWT Validator<br/>(Supabase verify)"]
Router["Request Router"]
end
TLS --> Rate
Rate --> JWT
JWT --> Router
Router -->|"/auth/*"| AuthSvc[Auth Service]
Router -->|"/sync/*"| SyncSvc[Sync Service]
Router -->|"/catalog/*"| CatSvc[Catalog Service]
Router -->|"/ocr/*"| OcrSvc[OCR Service]
style Request fill:#bbdefb,stroke:#1976d2
style Pipeline fill:#f5f5f5,stroke:#616161
style TLS fill:#c8e6c9,stroke:#388e3c
style Rate fill:#fff9c4,stroke:#fbc02d
style JWT fill:#ffccbc,stroke:#e64a19
style Router fill:#d1c4e9,stroke:#512da8
5.1.1. Configuracion Rate Limiting
# Decision del Director #1 (DV2)
rate_limits:
tiers:
free:
sync_push: { requests: 10, window: 60s }
sync_pull: { requests: 30, window: 60s }
catalog: { requests: 60, window: 60s }
ocr: { requests: 5, window: 60s }
pro:
sync_push: { requests: 50, window: 60s }
sync_pull: { requests: 150, window: 60s }
catalog: { requests: 120, window: 60s }
ocr: { requests: 20, window: 60s }
perfect:
sync_push: { requests: 100, window: 60s }
sync_pull: { requests: 300, window: 60s }
catalog: { requests: 200, window: 60s }
ocr: { requests: 50, window: 60s }
exceeded_response:
status: 429
headers:
- Retry-After
- X-RateLimit-Remaining
- X-RateLimit-Limit
- X-RateLimit-Reset
5.2. Sync Service
flowchart TB
Request([SYNC REQUEST])
Request --> Auth["Auth Middleware<br/>(JWT + user_id)"]
Auth --> Split{Route}
Split -->|POST /push| Push
Split -->|GET /pull| Pull
subgraph PushFlow["PUSH Flow"]
Push["PUSH Handler"]
BlobVal["Blob Validator<br/>(size, format)"]
Conflict["Conflict Check<br/>(version check)"]
Push --> BlobVal --> Conflict
end
subgraph PullFlow["PULL Flow"]
Pull["PULL Handler"]
Change["Change Detector<br/>(vector clock)"]
Fetcher["Blob Fetcher<br/>(paginated)"]
Pull --> Change --> Fetcher
end
Conflict --> DB
Fetcher --> DB
DB[(Supabase Database<br/>srv_encrypted_*)]
style Request fill:#bbdefb,stroke:#1976d2
style Auth fill:#ffccbc,stroke:#e64a19
style PushFlow fill:#e8f5e9,stroke:#388e3c
style PullFlow fill:#e3f2fd,stroke:#1565c0
style DB fill:#fff3e0,stroke:#ef6c00
5.2.1. Componentes del Sync Service
| Componente |
Funcion |
| Auth Middleware |
Valida JWT, extrae user_id |
| PUSH Handler |
Recibe blobs cifrados, valida tamano |
| PULL Handler |
Retorna cambios desde last_sync |
| Blob Validator |
Verifica formato y tamano maximo (1MB) |
| Change Detector |
Compara vector clocks para delta sync |
| Conflict Check |
Detecta conflictos de version |
| Blob Fetcher |
Recupera blobs con paginacion |
5.2.2. Flujo de PUSH
sequenceDiagram
participant C as Cliente
participant S as Sync Service
participant D as Database
C->>S: POST /sync/push<br/>{entities: [blob1, blob2]}
Note over S: Validate JWT<br/>Check rate limit<br/>Validate blob sizes
S->>D: BEGIN TRANSACTION
S->>D: Check versions
D-->>S: Version info
alt No conflict
S->>D: UPSERT blobs
S->>D: COMMIT
S-->>C: 200 OK<br/>{synced: 2, conflicts: []}
else Conflict detected
S->>D: ROLLBACK
S-->>C: 409 Conflict<br/>{conflicts: [...]}
end
5.3. Catalog Service
flowchart TB
Request([CATALOG REQUEST])
Request --> Cache["Cache Layer<br/>(Redis / CDN)"]
Cache --> Split{Route}
Split -->|"GET /catalog/search?q=..."| Search
Split -->|"GET /catalog/:id"| Detail
subgraph SearchFlow["Search Flow"]
Search["Search Handler"]
FTS["FTS Engine<br/>(Postgres FTS)"]
Search --> FTS
end
subgraph DetailFlow["Detail Flow"]
Detail["Detail Handler"]
DrugInfo["Drug Details<br/>(from external)"]
Detail --> DrugInfo
end
FTS --> DrugDB
DrugInfo --> DrugDB
DrugDB[(External Drug DB<br/>RxNorm / COFEPRIS)]
style Request fill:#bbdefb,stroke:#1976d2
style Cache fill:#fff9c4,stroke:#fbc02d
style SearchFlow fill:#e8f5e9,stroke:#388e3c
style DetailFlow fill:#f3e5f5,stroke:#7b1fa2
style DrugDB fill:#fce4ec,stroke:#c2185b
5.3.1. Fuentes de Datos del Catalogo
| Fuente |
Datos |
Actualizacion |
| RxNorm (FDA) |
Medicamentos US |
Semanal |
| COFEPRIS |
Medicamentos MX |
Mensual |
| EMA |
Medicamentos EU |
Mensual |
| Cache Local |
Busquedas frecuentes |
24h TTL |
5.4. OCR Service
flowchart TB
Request(["OCR REQUEST<br/>(imagen cifrada)"])
Request --> Decrypt
subgraph Processing["Procesamiento Efimero (RAM only)"]
Decrypt["Image Decryptor<br/>(efimero, en RAM)"]
Vision["Cloud Vision API<br/>(Google/AWS)"]
Parser["Prescription Parser<br/>(regex + NLP)"]
Encrypt["Result Encryptor<br/>(con clave cliente)"]
Cleanup["Cleanup Handler<br/>(borrar imagen)"]
Decrypt --> Vision
Vision --> Parser
Parser --> Encrypt
Encrypt --> Cleanup
end
Cleanup --> Response([Encrypted Result])
Warning>"⚠️ IMPORTANTE:<br/>La imagen NUNCA se persiste en disco.<br/>Procesamiento 100% en memoria,<br/>eliminacion inmediata."]
style Request fill:#ffcdd2,stroke:#c62828
style Processing fill:#fff8e1,stroke:#f9a825
style Decrypt fill:#e1f5fe,stroke:#0288d1
style Vision fill:#e8f5e9,stroke:#388e3c
style Parser fill:#f3e5f5,stroke:#7b1fa2
style Encrypt fill:#e1f5fe,stroke:#0288d1
style Cleanup fill:#ffccbc,stroke:#e64a19
style Response fill:#c8e6c9,stroke:#388e3c
style Warning fill:#ffebee,stroke:#c62828
5.4.1. Flujo de Seguridad OCR
ocr_security:
image_handling:
storage: "RAM only, never disk"
max_size: 10MB
retention: 0 # Eliminacion inmediata
encryption: "Client-provided key for result"
processing:
timeout: 30s
isolation: "Container per request"
logging: "No image content, only status codes"
cleanup:
on_success: "Immediate memory wipe"
on_failure: "Immediate memory wipe"
verification: "Memory zeroed before release"
5.5. Auth Service
flowchart TB
Request([AUTH REQUEST])
Request --> SupaAuth
subgraph AuthProvider["Supabase Auth (managed service)"]
SupaAuth["Auth Router"]
SupaAuth --> Email["Email/Password"]
SupaAuth --> OAuth["OAuth Providers<br/>(Google, Apple)"]
end
Email --> JWT
OAuth --> JWT
subgraph TokenFlow["Token Generation"]
JWT["JWT Generator<br/>(RS256)"]
Session["Session Store<br/>(Redis)"]
JWT --> Session
end
Session --> Response([JWT Token])
style Request fill:#bbdefb,stroke:#1976d2
style AuthProvider fill:#e8f5e9,stroke:#388e3c
style Email fill:#fff9c4,stroke:#fbc02d
style OAuth fill:#f3e5f5,stroke:#7b1fa2
style TokenFlow fill:#fff3e0,stroke:#ef6c00
style JWT fill:#ffccbc,stroke:#e64a19
style Session fill:#e1f5fe,stroke:#0288d1
style Response fill:#c8e6c9,stroke:#388e3c
5.5.1. Configuracion JWT
jwt_config:
algorithm: RS256
issuer: "https://auth.medtime.app"
audience: "medtime-api"
expiration:
access_token: 1h
refresh_token: 7d
claims:
required:
- sub (user_id)
- email
- tier (free/pro/perfect)
optional:
- family_id
- role
6. Patrones de Comunicacion
6.1. Sync Protocol
sync_protocol:
format: JSON over HTTPS
compression: gzip (>1KB)
pagination:
default_limit: 50 # Decision del Director #4
max_limit: 100
delta_sync:
method: vector_clock
conflict_detection: version_mismatch
resolution: client_side # Decision del Director #3
retry_policy:
max_attempts: 3
backoff: exponential
base_delay_ms: 1000
6.2. Endpoints Principales
| Endpoint |
Metodo |
Descripcion |
/sync/push |
POST |
Enviar blobs cifrados |
/sync/pull |
GET |
Obtener cambios desde timestamp |
/sync/status |
GET |
Estado de sincronizacion |
/catalog/search |
GET |
Buscar medicamentos |
/catalog/{id} |
GET |
Detalle de medicamento |
/ocr/process |
POST |
Procesar imagen de receta |
/auth/token |
POST |
Obtener/refrescar JWT |
7. Modelo de Datos del Servidor
7.1. Tablas Zero-Knowledge
-- srv_encrypted_entities: Blobs opacos
CREATE TABLE srv_encrypted_entities (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id),
entity_type TEXT NOT NULL, -- 'medication', 'dose', etc.
encrypted_blob BYTEA NOT NULL, -- Datos cifrados (opaco)
version INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ, -- Soft delete
client_id TEXT NOT NULL, -- Para multi-device
-- Metadata NO-PHI
blob_size INTEGER NOT NULL,
checksum TEXT NOT NULL -- SHA-256 del blob
);
-- Indices para performance
CREATE INDEX idx_entities_user_type ON srv_encrypted_entities(user_id, entity_type);
CREATE INDEX idx_entities_updated ON srv_encrypted_entities(updated_at);
CREATE INDEX idx_entities_sync ON srv_encrypted_entities(user_id, updated_at)
WHERE deleted_at IS NULL;
7.2. Tablas de Soporte (NO-PHI)
-- srv_sync_status: Estado de sync por dispositivo
CREATE TABLE srv_sync_status (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
device_id TEXT NOT NULL,
last_sync_at TIMESTAMPTZ,
sync_version INTEGER DEFAULT 0,
UNIQUE(user_id, device_id)
);
-- srv_ocr_requests: Metadata de OCR (sin imagen)
CREATE TABLE srv_ocr_requests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
status TEXT NOT NULL, -- 'pending', 'processing', 'done', 'failed'
created_at TIMESTAMPTZ DEFAULT NOW(),
completed_at TIMESTAMPTZ,
error_code TEXT -- Solo codigo, sin detalles de imagen
);
-- Cleanup job: 30 dias (Decision del Director #7)
-- Ejecutar diariamente a las 03:00 UTC
8. Consideraciones de Seguridad
8.1. Zero-Knowledge Enforcement
zero_knowledge_rules:
database:
- "encrypted_blob es BYTEA opaco"
- "No hay columnas para datos en claro"
- "RLS por user_id estricto"
logs:
- "Sin PHI en logs de aplicacion"
- "Solo IDs, timestamps, status codes"
- "Rotacion de logs: 7 dias"
api:
- "No hay endpoints que expongan datos en claro"
- "OCR: imagen nunca en disco"
- "Rate limiting por tier"
monitoring:
- "Alertas solo por metricas, no contenido"
- "Dashboards sin datos de usuario"
8.2. RLS (Row Level Security)
-- Politica: usuarios solo ven sus propios datos
ALTER TABLE srv_encrypted_entities ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_own_data" ON srv_encrypted_entities
FOR ALL
USING (auth.uid() = user_id);
-- Politica para cuidadores (familia)
CREATE POLICY "family_access" ON srv_encrypted_entities
FOR SELECT
USING (
user_id IN (
SELECT supervised_user_id FROM srv_family_links
WHERE supervisor_id = auth.uid()
AND status = 'active'
)
);
9.1. Targets
| Metrica |
Target |
Notas |
| P99 Latency (sync) |
< 500ms |
50 entidades |
| P99 Latency (catalog) |
< 200ms |
Con cache |
| P99 Latency (OCR) |
< 5s |
Imagen completa |
| Throughput |
1000 req/s |
Por instancia |
| Availability |
99.9% |
SLA target |
9.2. Estrategia de Escalado
scaling:
api_gateway:
type: horizontal
min_instances: 2
max_instances: 10
metric: request_rate
sync_service:
type: horizontal
min_instances: 2
max_instances: 20
metric: queue_depth
database:
type: vertical + read_replicas
primary: db.m5.xlarge
replicas: 2
cache:
type: Redis Cluster
nodes: 3
memory: 8GB per node
10. Monitoreo y Observabilidad
10.1. Metricas Clave
metrics:
business:
- sync_operations_total
- sync_conflicts_total
- ocr_requests_total
- ocr_success_rate
technical:
- request_latency_p50/p95/p99
- error_rate_5xx
- database_connections
- cache_hit_rate
security:
- auth_failures_total
- rate_limit_exceeded_total
- invalid_jwt_total
10.2. Alertas
| Alerta |
Condicion |
Severidad |
| High Error Rate |
5xx > 1% |
Critical |
| Sync Queue Backlog |
depth > 1000 |
Warning |
| Database CPU |
> 80% |
Warning |
| Auth Failures Spike |
> 100/min |
Critical |
| Rate Limit Abuse |
single IP > 1000/min |
Warning |
11. Referencias
Documento generado por: ArchitectureDrone
Fecha: 2025-12-09
Revision: DV2-ARCH-001
Estado: APROBADO
"El servidor es un simple relay. La inteligencia esta en el cliente."