Aller au contenu

ADR-0002 — Comment le backend consomme actuariat_lib

Statut Accepté — 2026-06-09
Portée backend/ (FastAPI), packaging d'image Docker, déploiement Dokploy
Décision Installer la lib depuis les sources ; contexte de build Docker = racine du repo
Contexte méthode Temps 3 « construire » de la méthode à 4 temps — brancher le moteur en service

🎯 À retenir

Le backend installe actuariat_lib depuis les sources du monorepo, avec le contexte de build Docker placé à la racine du dépôt (context: ., dockerfile: backend/Dockerfile). Source unique, sync automatique lib ↔ backend, aucune infra de registre. Bascule vers un index privé seulement quand un 2ᵉ module consommera la lib.

Contexte

Le moteur DBO (dbo_population_puc) vit dans actuariat_lib (src/actuariat_lib/, couche pure, validée par les oracles K1/K1-bis). Le backend FastAPI (backend/app/) expose la route POST /api/dbo qui traduit un payload JSON en HypothesesActuarielles, charge la table réglementaire (ADR-0001) et appelle le moteur — il ne recalcule rien.

Pour que la route fonctionne, l'image Docker du backend doit contenir actuariat_lib. Or le service backend se construisait avec build: ./backend : le contexte Docker est ./backend, alors que la lib vit dans ./srchors contexte. Un simple import ne suffit pas ; il faut décider comment l'image récupère la lib.

Deux contraintes transverses pèsent :

  1. Monorepo — lib et backend vivent dans le même dépôt, versionnent, se testent et se déploient ensemble (Dokploy rebuild dev à chaque push).
  2. Pureté de la libactuariat_lib ne doit acquérir aucune dépendance vers le backend. Le branchement doit rester unidirectionnel (backend → lib).

Options considérées

  • A — Install depuis les sources : contexte de build = racine, le Dockerfile fait COPY pyproject.toml src puis pip install .. Une seule source de vérité : la source du repo.
  • B — Wheel vendoré : builder actuariat_lib-1.1.0-py3-none-any.whl, le déposer dans backend/vendor/, le pointer depuis requirements.txt. Contexte de build inchangé (./backend).
  • C — Index privé : publier la lib sur un index (PyPI privé / registre), l'épingler par version dans requirements.txt.

Comparatif pour / contre

Critère A — Install depuis source B — Wheel vendoré C — Index privé
Source unique de vérité ✅ la source du monorepo ❌ wheel = copie à régénérer ✅ une version publiée
Sync lib ↔ backend ✅ automatique manuelle (rebuild+revendor à chaque évol.) ⚠️ via bump de version
Artefact binaire en git ✅ aucun .whl committé ✅ aucun
Cohérence avec le repo ✅ le service docs est déjà en context: . ❌ romprait l'idiome ⚠️ neutre
Pureté de la lib (unidirectionnel) ✅ backend → lib ✅ backend → lib ✅ backend → lib
Couplage des cadences ❌ lib et backend bumpent ensemble ✅ découplés ✅ découplés
Infra nécessaire aujourd'hui ✅ nulle ✅ nulle ❌ registre + publication + auth
Réutilisation multi-modules ⚠️ couplée à ce repo ⚠️ chacun re-vendore ✅✅ conçue pour ça
Risque de dérive silencieuse ✅ nul (toujours la source) ❌ wheel périmé non détecté ✅ version explicite
Build offline / reproductible ✅ (sources locales) ✅ (wheel local) ❌ dépend du registre

Décision

Option A — installer la lib depuis les sources, en passant le contexte de build du service backend à la racine du dépôt (build: { context: ., dockerfile: backend/Dockerfile }).

Justification. C'est un monorepo : lib et backend versionnent, se déploient et évoluent ensemble. La source unique est le modèle honnête — pas de wheel à re-synchroniser à la main (B, source de dérive silencieuse), pas d'infra de registre à monter (C, cérémonie prématurée). A s'aligne en plus sur l'idiome déjà présent dans le repo : le service docs se construit lui aussi avec context: .. B et C sont les bonnes réponses le jour où la lib devient un artefact indépendant consommé par un autre module — c'est exactement le déclencheur A→C déjà pré-enregistré dans l'ADR-0001.

Conséquences

  • Dockerfile backend (backend/Dockerfile) : chemins relatifs à la racine désormais. COPY pyproject.toml src + pip install . (couche dédiée, mise en cache tant que la lib ne bouge pas ; embarque aussi les tables CIMA via le package-data d'ADR-0001), puis COPY backend/requirements.txt + pip install -r, puis COPY backend/app.
  • Compose : docker-compose.yml et docker-compose.dev.yml passent le service backend à build: { context: ., dockerfile: backend/Dockerfile }.
  • .dockerignore racine (nouveau, partagé par les builds backend et docs) : exclut .venv, caches, frontend/node_modules, frontend/.next, references/, data/, refs/, .git, *.db.

⚠️ Piège

Le .dockerignore racine est partagé par les builds backend et docs. Ne pas exclure docs/, mkdocs.yml, requirements-docs.txt (le build docs en a besoin) ni pyproject.toml / src/ / backend/ (le build backend en a besoin) : une exclusion de trop casse silencieusement l'un des deux images.

  • Dév local : la lib est installée éditable dans le venv du backend (uv pip install --python backend/.venv/bin/python -e .) → import actuariat_lib immédiat pour lancer/serveur et tests.
  • Validation : image construite en Docker réel ; à l'intérieur, actuariat_lib s'importe, la table CIMA charge, les 4 fichiers package-data sont présents, et la route reproduit l'oracle K1 = 2 751 205,018849 FCFA via HTTP (backend/tests/test_dbo.py).

Déclencheur de réexamen (pré-enregistré)

Dès qu'un 2ᵉ module Quantis consomme actuariat_lib, migrer A → C : publier la lib sur un index privé et l'épingler par version (le même déclencheur que l'ADR-0001 sur les données). Tant qu'actuariat_lib n'a qu'un consommateur (le backend ActuaryLab), A reste cohérent.