Files
IPSExtractor/docs/MIGRACION_MICROSERVICIOS.md
T

17 KiB
Raw Blame History

Migración a microservicios y arquitectura IPSExtractor

Documento de referencia para la rama migration-services respecto a main. Describe qué hay de nuevo, cómo funciona la infraestructura, los patrones de diseño, cómo escalar el sistema y cómo incorporar servicios futuros.

Nota: el repositorio no tiene rama master; la línea base de comparación es main.


1. Resumen ejecutivo

IPSExtractor evolucionó de un monolito Python (interfaz gráfica de escritorio + API embebida) a una arquitectura de microservicios orquestada con Docker Compose, con una aplicación de escritorio Electron + React como cliente principal.

Aspecto Estado actual
Despliegue docker compose up — stack completo en contenedores
Puerta de entrada API Gateway en 0.0.0.0:8189 (único puerto expuesto al host)
Persistencia PostgreSQL 16 + migraciones Alembic
Cola async Redis 7 + workers ARQ para extracción LLM
Conteo count-api (Go, Tesseract + ONNX), stateless
Cliente IPSExtractor Desktop v1.0.0 (2026-07-06), instalador NSIS Windows

Alcance de la migración (rama migration-services): ~170 archivos modificados, +27 000 / −8 500 líneas, 5 commits sobre main.


2. Qué cambió respecto a main

Antes y después

Antes (main) Ahora (migration-services)
gui/ + gui_main.py — UI desktop en Python services/desktop_app/ — Electron + React + TypeScript
server/app/ — API monolítica api_gateway + extraction + cleaning + count-api
application/ — lógica de dominio acoplada al monolito Dominio repartido bajo services/<nombre>/
installer.iss / build_installer.bat — Inno Setup electron-builder → IPSExtractor-Desktop-1.0.0-Setup.exe
Sin orquestación unificada docker-compose.yml — stack completo
Sin modelo de trabajos en BD PostgreSQL: jobs, tasks, conteo, extracción, limpieza
Procesamiento síncrono en el mismo proceso Redis + workers ARQ (WORKER_REPLICAS)

Eliminado

  • gui/ — pestañas de extracción, limpieza, métricas, preclasificación, etc.
  • application/ en la raíz del repo
  • server/ — API FastAPI monolítica
  • gui_main.py, requirements.txt raíz, installer.iss
  • Tests legacy en tests/ (sustituidos por tests por servicio / desktop)

Añadido (highlights)

Ruta Rol
services/api_gateway/ Proxy HTTP único hacia el resto de servicios
services/extraction/ Jobs, tasks, conteo en BD, extracción LLM, API REST
services/extraction/worker/ Workers ARQ para procesamiento asíncrono
services/cleaning/ Limpieza y transformación de datos CSV
services/services-count-facturas-medicas-serving/ count-api — OCR + clasificación ONNX (Go)
services/db/ Migraciones Alembic (PostgreSQL)
services/desktop_app/ App Electron + React
docs/database.dbml Esquema completo de la base de datos
.env.example Variables de entorno de referencia

Commits en migration-services (sobre main)

  1. Eliminación del monolito legacy + introducción de Docker Compose microservicios
  2. App desktop Electron + wizards de extracción
  3. Redis, .env.example, indexación de carpetas
  4. Servicio db-migrations, jobs y conteo en PostgreSQL
  5. Healthchecks, puerto 8189, OCR tuning, instalador desktop v1.0.0

3. Infraestructura nueva

3.1 Despliegue Docker

Solo el API Gateway publica un puerto al host. El resto de servicios se comunican por la red interna de Docker.

Servicio Puerto host Puerto interno Tecnología
api_gateway 8189 8000 Python / FastAPI
extraction — 8001 Python / FastAPI
cleaning — 8002 Python / FastAPI
count-api — 8080 Go
database — 5432 PostgreSQL 16
redis — 6379 Redis 7
worker — — Python ARQ (N réplicas)
db-migrations — — Alembic (one-shot)
flowchart TB
  subgraph host [Host]
    Desktop[DesktopApp Electron]
    Port8189["0.0.0.0:8189"]
  end
  subgraph docker [Red Docker interna]
    GW[api_gateway :8000]
    EXT[extraction :8001]
    CLN[cleaning :8002]
    CNT[count-api :8080]
    WRK[worker ARQ x N]
    PG[(PostgreSQL)]
    RD[(Redis)]
  end
  Desktop -->|"HTTPS prod o 127.0.0.1:8189 dev"| Port8189
  Port8189 --> GW
  GW --> EXT
  GW --> CLN
  GW --> CNT
  EXT --> PG
  EXT --> RD
  WRK --> PG
  WRK --> RD
  CLN --> PG

3.2 Arranque y dependencias

El orden de arranque está controlado por healthchecks y depends_on:

  1. database y redis deben estar healthy
  2. db-migrations ejecuta Alembic y termina con éxito
  3. extraction, cleaning, count-api y worker arrancan
  4. api_gateway espera a extraction healthy y count-api healthy
cp .env.example .env
docker compose up --build
curl http://127.0.0.1:8189/health/live

En Windows use 127.0.0.1 en lugar de localhost para evitar timeouts por resolución IPv6 (::1).

3.3 Flujo de negocio (centrado en jobs)

Un job agrupa el trabajo del usuario (carpeta de documentos). Dentro del job se ejecutan tasks por módulo (counting, extraction, cleaning).

sequenceDiagram
  participant App as DesktopApp
  participant GW as ApiGateway
  participant CNT as CountApi
  participant EXT as Extraction
  participant DB as PostgreSQL

  App->>GW: POST /count/v1/count
  GW->>CNT: proxy OCR+ONNX
  CNT-->>App: resultados por PDF
  App->>GW: POST /count-jobs
  GW->>EXT: persistir conteo
  EXT->>DB: count_job_files
  App->>GW: POST /tasks + ingest-many
  GW->>EXT: encolar extracción
  EXT->>DB: extraction_rows

3.4 Rutas del API Gateway

El gateway en services/api_gateway/api/main.py actúa como reverse proxy:

Prefijo público Servicio upstream
/jobs, /tasks, /count-jobs extraction:8001
/extraction/* extraction:8001
/cleaning/* cleaning:8002
/count/* count-api:8080
/health, /health/live, /config, /models Agregación / proxy

Timeouts diferenciados: peticiones JSON (~30 s) vs uploads OCR/conteo (hasta 600 s).


4. Patrones de diseño

Patrón Ubicación Propósito
API Gateway / Reverse Proxy services/api_gateway/api/main.py Un solo endpoint público; enrutamiento por prefijo
Microservicios sin paquetes compartidos services/* Cada servicio con Dockerfile, dominio y dependencias propias
Cola de trabajos async services/extraction/worker/, Redis Extracción LLM fuera del ciclo request/response HTTP
Registry / Plugin (desktop) services/desktop_app/src/renderer/modules/registry.ts Módulos counting / extraction se registran sin acoplar App.tsx
Job + Task (agregado) Tablas jobs → tasks en PostgreSQL Un trabajo del usuario agrupa módulos ejecutados
Healthchecks en cadena docker-compose.yml depends_on: service_healthy evita arranques prematuros
BFF con timeouts por ruta Gateway _is_upload_path() JSON ligero vs uploads pesados
Stateless vs stateful count-api (stateless) vs extraction (stateful + BD) Conteo horizontalmente escalable; extracción con persistencia

Registry de módulos (desktop)

Cada módulo implementa ModuleDescriptor y se auto-registra al importarse:

// services/desktop_app/src/renderer/modules/counting/CountingModule.ts
registerModule({
  type: "counting",
  label: "Conteo",
  Wizard: CountingWizardModule,
  Results: CountingResults,
  hasTask: (tasks) => tasks.some((t) => t.type === "counting"),
});

App.tsx solo importa los módulos; no necesita conocer su implementación interna.


5. Componentes principales

5.1 Backend

Servicio Responsabilidad
api_gateway Proxy HTTP, /health/live, CORS, logging, errores 503/504 hacia upstream caído
extraction CRUD de jobs/tasks, count-jobs, ingest de PDFs, estimación de costos, API LLM
worker Procesamiento ARQ en background; escala con WORKER_REPLICAS
count-api OCR (Tesseract) + inferencia ONNX por lote; sin estado ni BD
cleaning Reglas de limpieza CSV; expuesto vía /cleaning/*
db-migrations Alembic one-shot antes de que arranquen los servicios de aplicación

5.2 Base de datos (resumen)

jobs                   — contenedor de un trabajo del usuario
  tasks                — módulo ejecutado (counting / extraction / cleaning)
    count_job_files    — resultado de conteo por PDF
    extraction_batches — lote de extracción
      extraction_rows  — fila extraída por documento
    cleaning_jobs      — job de limpieza
      cleaned_rows     — fila transformada

Esquema completo: database.dbml

5.3 Desktop (services/desktop_app/)

Componente Función
App.tsx Navegación Home / Job / Settings; sidebar con submenú por job
HomeView.tsx Listado y creación de jobs
JobView.tsx Paneles de conteo y extracción (progreso en segundo plano)
JobResultsView.tsx Resultados unificados, export CSV, lanzar extracción desde válidos
CountingWizard.tsx Lotes paralelos hacia POST /count/v1/count
JobExtractionWizard.tsx Extracción con upload de PDFs desde el escritorio
apiClient.ts Cliente HTTP único hacia el gateway (timeouts, mensajes de error)
electron/folderIndex.cjs Indexación local: modo general o radicados
electron/preload.cjs Bridge seguro main ↔ renderer (contextBridge)

6. Cómo escalar para mayor velocidad

6.1 Conteo (count-api)

El conteo es el cuello de botella más frecuente en lotes grandes de PDFs.

Variable / ajuste Efecto Dónde
OCR_DPI=150 ~4× menos RAM que 300 DPI docker-compose.yml
OCR_POOL_SIZE Goroutines OCR por request docker-compose.yml
SYNC_PROCESS_CONCURRENCY Paralelismo interno por request docker-compose.yml
COUNT_API_MEMORY Límite RAM del contenedor (evitar OOM) docker-compose.yml
restart: unless-stopped Recuperación automática tras OOM docker-compose.yml
BATCH_SIZE=10 PDFs por petición HTTP CountingWizard.tsx
BATCH_CONCURRENCY=2 Lotes HTTP simultáneos desde desktop CountingWizard.tsx

Futuro: réplicas de count-api detrás del gateway con balanceo round-robin en el proxy.

6.2 Extracción LLM

Variable / comando Efecto
WORKER_REPLICAS=4 Más workers ARQ procesando en paralelo
docker compose up --scale worker=4 Escala horizontal de workers
WORKER_CONCURRENCY Tareas concurrentes por worker
CONCURRENCY_LIMIT Límite global en el servicio extraction
PDF_THREAD_COUNT Hilos para renderizado de PDF
PDF_DPI Resolución de páginas enviadas al LLM

6.3 Infraestructura y build

Problema Solución
BuildKit satura al construir count-api docker compose build count-api antes del stack completo
Build paralelo falla en Docker Desktop COMPOSE_BAKE=false COMPOSE_PARALLEL_LIMIT=2 (ver .env.example)
Conexiones BD colgadas Pool SQLAlchemy + connect_timeout en database.py
Gateway sin respuesta en Windows Usar 127.0.0.1:8189, no localhost

7. Cómo añadir un servicio futuro

Checklist para incorporar un nuevo microservicio sin romper la arquitectura:

Paso 1 — Crear el servicio

services/mi-servicio/
  Dockerfile
  api/main.py          # API propia (FastAPI, Go, etc.)
  domain/              # lógica de negocio
  requirements.txt
  README.md

Paso 2 — Registrar en Docker Compose

En docker-compose.yml:

  • Añadir el servicio sin ports al host (solo red interna)
  • Definir healthcheck si expone /health
  • Configurar depends_on según dependencias (BD, migraciones, etc.)

Paso 3 — Exponer vía API Gateway

En services/api_gateway/api/main.py:

MI_SERVICIO_URL = os.getenv("MI_SERVICIO_URL", "http://mi-servicio:8003").rstrip("/")

@app.api_route("/mi-servicio/{path:path}", methods=["GET", "POST", "PUT", "PATCH", "DELETE"])
async def mi_servicio_proxy(path: str, request: Request) -> Response:
    return await _proxy_request(request, f"{MI_SERVICIO_URL}/{path}")

Añadir MI_SERVICIO_URL en el bloque environment de api_gateway en compose.

Paso 4 — Base de datos (si aplica)

  1. Crear migración Alembic en services/db/alembic/versions/
  2. El servicio db-migrations la aplicará en el próximo docker compose up

Paso 5 — Módulo en la app desktop (si aplica)

  1. Crear services/desktop_app/src/renderer/modules/mi-modulo/
  2. Implementar ModuleDescriptor y llamar registerModule()
  3. Importar el módulo en App.tsx (una línea, igual que counting/extraction)
  4. Añadir entrada en el sidebar de JobView si requiere sección propia

Paso 6 — Documentación

  • README del servicio
  • Actualizar tabla de servicios en README.md
  • Actualizar este documento si el patrón de integración cambia
flowchart LR
  NewSvc[nuevo_servicio]
  Compose[docker-compose]
  GW[api_gateway nueva ruta]
  Reg[registerModule desktop]
  NewSvc --> Compose
  Compose --> GW
  GW --> Desktop
  Reg --> Desktop

8. App de escritorio — versión y funcionalidades

8.1 Versión e instalador

Campo Valor
Versión 1.0.0
Fecha de release 2026-07-06
Instalador services/desktop_app/release/IPSExtractor-Desktop-1.0.0-Setup.exe
Formato NSIS, Windows x64
Fuente de versión services/desktop_app/package.json + src/shared/version.json

Generar instalador:

cd services/desktop_app
npm install
npm run dist

8.2 URL del API Gateway

Entorno URL por defecto
Desarrollo (npm run dev) http://127.0.0.1:8189
Instalador (build producción) https://pys1.builder.xyz

La URL es configurable en Configuración de la app y se persiste en localStorage.

8.3 Funcionalidades incluidas

  • Gestión de trabajos (jobs): crear, abrir y organizar trabajos con carpeta local de documentos
  • Conteo: OCR + clasificación ML en lotes con barra de progreso en segundo plano
  • Validación humana: marcar archivos del conteo como válidos/inválidos
  • Extracción LLM: wizard con descubrimiento de campos, estimación de costo y ejecución
  • Extracción desde conteo: lanzar extracción solo sobre PDFs válidos del conteo
  • Resultados unificados: vista de conteo y extracción con exportación CSV
  • Configuración: URL del gateway, verificación de conexión, vista de config/modelos del backend
  • Versión visible: sidebar (menú) y esquina superior derecha (v1.0.0 · 6 jul 2026)

8.4 Arquitectura de la UI

Home (lista de jobs)
  └── Job activo
        ├── Conteo      → CountingWizardModule
        ├── Extracción  → JobExtractionWizard
        └── Resultados  → JobResultsView
Configuración (global)

La navegación usa sidebar lateral con submenú contextual por job (sin tabs en el área principal).


9. Enlaces relacionados

Documento Descripción
README.md Guía rápida del stack completo
services/api_gateway/README.md API Gateway
services/extraction/README.md Extracción LLM y jobs
services/cleaning/README.md Limpieza de datos
services/services-count-facturas-medicas-serving/README.md Count API (Go)
services/db/README.md Migraciones Alembic
services/desktop_app/README.md App Electron
database.dbml Esquema completo de PostgreSQL
.env.example Variables de entorno

Última actualización: 2026-07-06 — rama migration-services.