docling-studio/.claude/plan.md

6.4 KiB

Plan : Architecture Simplification — 2 services (Vue + FastAPI/SQLite)

Objectif

Supprimer le backend Spring Boot (passe-plat) et PostgreSQL. Le document-parser Python absorbe toute la logique backend. On passe de 4 services à 2 services : Vue frontend + FastAPI Python.

Architecture Python — Clean Architecture légère

document-parser/
├── main.py                     # FastAPI app, CORS, lifespan, routers mount
├── bbox.py                     # (existant) coord conversion
├── test_bbox.py                # (existant) bbox tests
├── requirements.txt            # + aiosqlite, aiofiles
├── Dockerfile                  # (adapté)
├── .dockerignore               # (existant)
│
├── domain/                     # 🧠 Modèles métier purs (pas de dépendance framework)
│   ├── __init__.py
│   ├── models.py               # Document, AnalysisJob, Status (dataclasses/Pydantic)
│   └── parsing.py              # Logique d'extraction Docling (déplacée depuis main.py)
│
├── api/                        # 🌐 Couche HTTP (FastAPI routers)
│   ├── __init__.py
│   ├── schemas.py              # Pydantic request/response schemas (DTOs)
│   ├── documents.py            # Router /api/documents (CRUD + upload + preview)
│   └── analyses.py             # Router /api/analyses (CRUD + async processing)
│
├── persistence/                # 💾 Couche données (SQLite via aiosqlite)
│   ├── __init__.py
│   ├── database.py             # SQLite connection, init schema, get_db()
│   ├── document_repo.py        # CRUD documents
│   └── analysis_repo.py        # CRUD analysis jobs
│
└── services/                   # ⚙️ Orchestration (use cases)
    ├── __init__.py
    ├── document_service.py     # Upload, delete, preview (file I/O + persistence)
    └── analysis_service.py     # Create job, background parse, update status

Pourquoi cette structure plutôt qu'hexagonale ?

  • domain/ : modèles purs, testables, zéro import framework → l'esprit de l'hexagonale
  • api/ : adaptateur HTTP (port entrant)
  • persistence/ : adaptateur stockage (port sortant)
  • services/ : orchestration des use cases
  • Pas de ports/adapters formels (interfaces abstraites) → overkill pour le scope
  • Un mec de Docling voit ça, il comprend en 5 secondes. Clean, pas over-engineered.

Endpoints conservés (contrat API identique)

Le frontend ne change quasiment pas — mêmes URLs, mêmes payloads :

Method Path Description
POST /api/documents/upload Upload PDF (multipart)
GET /api/documents List documents
GET /api/documents/{id} Get document
DELETE /api/documents/{id} Delete document + file
GET /api/documents/{id}/preview?page=&dpi= Page preview PNG
POST /api/analyses Create analysis (body: {documentId})
GET /api/analyses List analyses
GET /api/analyses/{id} Get analysis (polling)
DELETE /api/analyses/{id} Delete analysis
GET /health Health check

Détails d'implémentation

1. SQLite (persistence/database.py)

  • aiosqlite pour async natif avec FastAPI
  • Schema identique au Liquibase actuel (2 tables: documents, analysis_jobs)
  • DB file dans volume Docker : /app/data/docling_studio.db
  • Init schema au startup (CREATE TABLE IF NOT EXISTS)
  • Pas besoin d'Alembic pour un projet de cette taille

2. File storage

  • Même logique : ./uploads/{uuid}_{filename}
  • Volume Docker monté sur /app/uploads

3. Async analysis (services/analysis_service.py)

  • asyncio.create_task() pour le background processing (pas besoin de Celery)
  • Status polling identique : PENDING → RUNNING → COMPLETED | FAILED
  • Le parse Docling tourne dans un thread via asyncio.to_thread() (car bloquant + lock)

4. CORS

  • fastapi.middleware.cors.CORSMiddleware dans main.py
  • Origins configurables via env var

5. domain/parsing.py

  • Déplace depuis main.py : _build_converter(), _get_element_type(), extract_pages_detail(), _process_content_item()
  • Le converter et le lock restent globaux (singleton pattern)

Changements Frontend

Minimes — seulement la configuration :

  1. vite.config.js : proxy target change 80818000
  2. frontend/Dockerfile (nginx) : proxy_pass change backend:8081document-parser:8000
  3. api.js : AUCUN changement (mêmes paths /api/...)
  4. stores : AUCUN changement

docker-compose.yml simplifié

services:
  document-parser:
    build: ./document-parser
    ports:
      - "8000:8000"
    volumes:
      - uploads_data:/app/uploads
      - db_data:/app/data
    deploy:
      resources:
        limits:
          memory: 4g

  frontend:
    build: ./frontend
    ports:
      - "3000:80"
    depends_on:
      - document-parser

volumes:
  uploads_data:
  db_data:

Plus de postgres, plus de backend Java. 2 services, clean.

Étapes d'exécution

  1. Créer la structure Python (domain/, api/, persistence/, services/)
  2. persistence/database.py — SQLite async init + schema
  3. persistence/document_repo.py — CRUD documents
  4. persistence/analysis_repo.py — CRUD analyses
  5. domain/models.py — Dataclasses Document, AnalysisJob, Status
  6. domain/parsing.py — Extraire la logique Docling depuis main.py
  7. api/schemas.py — Pydantic DTOs (DocumentResponse, AnalysisResponse, etc.)
  8. services/document_service.py — Upload, delete, preview, list, get
  9. services/analysis_service.py — Create, run background, status tracking
  10. api/documents.py — Router documents (5 endpoints)
  11. api/analyses.py — Router analyses (4 endpoints)
  12. main.py — Réécrire : CORS, lifespan (DB init), mount routers
  13. requirements.txt — Ajouter aiosqlite, aiofiles
  14. docker-compose.yml — Simplifier (2 services)
  15. frontend/vite.config.js — Changer proxy target
  16. frontend/Dockerfile — Changer nginx proxy_pass
  17. Supprimer le dossier backend/ entier
  18. Mettre à jour README.md — Nouvelle architecture

Ce qu'on ne touche PAS

  • bbox.py, test_bbox.py (inchangés)
  • Toute la logique Vue (stores, components, pages)
  • Logique Docling (parsing, extraction) — juste déplacée
  • Dockerfile du parser (juste adapter le CMD si nécessaire)