Architecture
1. Vue système
Section intitulée « 1. Vue système »🔵 Tout tourne en conteneurs Docker, orchestrés par docker-compose.yml à la racine du repo.
Les artefacts licenciés (index SNOMED, cartes UMLS) ne sont pas dans les images : ils
sont montés en volumes RO depuis l’host (BYO) → Modèle de licence (open-core).
flowchart TB
subgraph host["Host (Ubuntu, RTX 4090)"]
subgraph dc["docker compose"]
web["web<br/>Next.js standalone :3000"]
api["api<br/>FastAPI + worker in-process :8000"]
db[("db<br/>Postgres :5432")]
sl["frontend (Streamlit legacy :8501, standby)"]
end
vols["Volumes RO (BYO) :<br/>xmen cache · checkpoints ·<br/>data/mondo · data/index · data/staging"]
runner["Runner host (systemd timer)<br/>builds d'index"]
end
web --> api
api --> db
api -. lit .-> vols
runner -. écrit .-> vols
runner --> db
classDef m fill:#e7f5ec,stroke:#2E7D5B
class web,api m
Le worker (pré-calcul des candidats, traitement imports/exports) tourne in-process
dans le conteneur api. Le runner host est hors conteneur (l’API monte l’index en RO et
n’embarque pas les builders) → Opérations & infra.
2. Les trois dépôts
Section intitulée « 2. Les trois dépôts »🔵 La frontière entre dépôts encode la frontière de licence et l’open-core.
| Dépôt | Rôle | Branche |
|---|---|---|
MB klouche/messier | API + linking + worker | messier-api-v1 |
Frontend klouche/messier-frontend | Next.js (UI curation/dashboard) | main |
MC messier-compositional | lib open-core : moteur compositionnel + builders | main |
MC contient les recettes de build (kb_builders) — jamais le contenu produit. MB consomme les index montés ; le frontend consomme l’API via le contrat.
3. Modèle de données (Postgres)
Section intitulée « 3. Modèle de données (Postgres) »🔵 Schéma greenfield (db/init_v1.sql). validations est append-only (correction =
nouvelle ligne supersedes) ; mentions.status est dénormalisé (source de vérité =
dernière validation, via trigger) pour le hot-path de la file.
erDiagram
users ||--o{ projects : owns
projects ||--o{ imports : has
imports ||--o{ import_rows : has
projects ||--o{ mentions : has
mentions ||--o{ validations : "append-only"
import_rows }o--|| mentions : "→"
projects ||--o{ exports : has
projects ||--o{ audit_log : traces
ontology_builds }o--|| users : requested_by
mentions {
uuid id
text raw_text
enum status
bool oncology
jsonb composite
uuid latest_validation_id
}
validations {
uuid id
enum kind
text mondo_id
jsonb snomed_emission
jsonb predicted_top1
}
exports {
enum format
enum scope
enum kind
jsonb filters
}
ontology_builds {
text ontology
enum status
jsonb sources
text result_version
}
Enums clés : mention_status (pending · pending_semantic · in_review · validated · rejected ·
pending_arbitration · arbitrated · pending_oncology), validation_kind (accept · reject ·
request_arbitration · arbitrate_accept · arbitrate_reject · undo), export_{format,scope,kind},
build_status.
4. Le contrat (OpenAPI)
Section intitulée « 4. Le contrat (OpenAPI) »🔵 contracts/openapi.yaml = source de vérité unique. On régénère :
features/api/types.ts(openapi-typescript v7) côté frontend ;- les schémas Pydantic côté API restent alignés.
Tout drift de contrat casse au compile-time TS — d’où la discipline « éditer le contrat d’abord ». ⚠️ openapi-typescript v7 traite les champs à valeur par défaut comme requis.
5. Flux de bout en bout
Section intitulée « 5. Flux de bout en bout »flowchart LR A["CSV/XLSX"] --> B["Import + mapping colonnes"] B --> C["import_rows"] C --> D["Worker : pré-calcul candidats<br/>(+ pré-routage code, cache)"] D --> E["mentions (file de curation)"] E --> F["Curation / arbitrage"] F --> G["Validation (+ émission SNOMED)"] G --> H["Export (biobanque / SPHN)"] classDef m fill:#e7f5ec,stroke:#2E7D5B class D,G m
Détail des étapes : Imports & exports (interop) · Moteur de linking (cœur ML) · Produit de curation (UX) · Pont terminologique & émission (Mondo→SNOMED).