Saltar a contenido

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
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. Escalabilidad y Performance

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 Proposito
02-arquitectura-cliente-servidor.md Arquitectura dual
05-seguridad-servidor.md Seguridad server-side
API-SYNC-001.yaml Contrato API Sync
API-CATALOG-001.yaml Contrato API Catalog
API-OCR-001.yaml Contrato API OCR
DV2-Remediation-Plan.md Decisiones del Director

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."