Files
Johan LEROY 4c72fbbb69 docs: fonde les vues d'architecture du monorepo
Cinq vues Mermaid dans docs/architecture (vue d'ensemble, infra, backend,
frontend, donnees), plus leur index, les conventions de statut et la regle
de maintenance en PR.

Reprend les jalons J1-J4, disparus de dev lors de la reecriture du README
(2670483) et restes seulement sur main : plus rien sur la branche de travail
ne disait ce que le projet doit prouver.

Fige les decisions du module Terraform k3s, qui ne vivaient jusqu'ici que
dans des commentaires de code et des description de variables : version
epinglee obligatoire, Traefik desactive, kubeconfig en 600/root, state local.

Corrige trois affirmations devenues fausses : le frontend classe
"a initialiser" alors que le squelette existe depuis 49f4697, le port 4200
dit attendu par docker-compose.yml qui n'a aucun service frontend, et
l'arborescence core/ prescrite par TESTING.md sans exister.
2026-09-15 11:56:18 +02:00

6.4 KiB

Backend

API FastAPI, Python 3.14, SQLAlchemy asynchrone sur asyncpg. Source dans apps/backend.

Couches

La doctrine est posée dans apps/backend/README.md et TESTING.md : endpoints appelle services, qui appelle repositories, qui seuls touchent les models. Le sens de dépendance ne s'inverse jamais.

Dans les faits, trois de ces couches sont des dossiers vides.

flowchart TB
  ep["endpoints<br/>2 routes"]
  sc["schemas<br/>2 modèles Pydantic"]
  sv["services<br/>vide"]
  rp["repositories<br/>vide"]
  md["models<br/>vide"]
  db[("PostgreSQL")]

  ep --> sc
  ep -.-> sv
  sv -.-> rp
  rp -.-> md
  ep -->|"SQL brut, état actuel"| db
  rp -.-> db

Le trait plein de endpoints vers la base n'est pas une erreur de dessin : /health/ready exécute aujourd'hui son SELECT directement, sans repository. C'est acceptable pour une sonde d'infrastructure, qui vérifie la base elle-même et non une donnée métier. Ce raccourci ne doit pas servir de modèle au premier endpoint métier.

app/models/__init__.py ne contient qu'un avertissement, qui mérite d'être connu avant la première migration : tout modèle absent de ce module reste invisible d'un alembic revision --autogenerate, qui produirait alors un drop de sa table.

Démarrage

Point d'entrée : une factory, uvicorn app.main:create_app --factory. Aucune configuration n'est lue à l'import du module, ce qui rend l'application testable et les migrations indépendantes de l'environnement d'exécution.

sequenceDiagram
  participant U as uvicorn --factory
  participant F as create_app
  participant S as get_settings
  participant A as FastAPI

  U->>F: create_app()
  F->>S: Settings depuis .env et variables APP_*
  S-->>F: resolved
  F->>F: configure_logging(resolved)
  F->>A: FastAPI, docs fermés si prod
  F->>A: CORSMiddleware, seulement si allowed_origins
  F->>A: Instrumentator, expose /metrics
  F->>A: include_router, préfixe /api/v1
  A-->>U: application

Le lifespan n'ouvre aucune connexion. Au démarrage il journalise le nom, la version et l'environnement ; à l'arrêt il libère l'engine. L'engine lui-même est construit paresseusement au premier appel de get_engine(), mis en cache par lru_cache. Conséquence directe : une API qui démarre ne prouve rien sur la base, la première connexion réelle a lieu au premier GET /api/v1/health/ready. C'est ce qui rend cette sonde indispensable.

Configuration

Settings est un BaseSettings Pydantic, lu depuis .env avec le préfixe APP_.

Variable Défaut Rôle
APP_SECRET_KEY aucun Secret applicatif, SecretStr
DATABASE_URL aucun Chaîne de connexion, postgresql+asyncpg://...
APP_ENV local local, dev, staging ou prod
APP_DEBUG false Active aussi l'écho SQL de l'engine
APP_LOG_LEVEL INFO
APP_CORS_ORIGINS "" Liste séparée par des virgules. Vide, aucun middleware CORS n'est posé
APP_API_PREFIX /api/v1
APP_DATABASE_POOL_SIZE 5
APP_DATABASE_MAX_OVERFLOW 10

Deux pièges :

  • DATABASE_URL ne prend pas le préfixe APP_. C'est le seul réglage dans ce cas, par validation_alias, pour rester compatible avec la convention d'Alembic et des hébergeurs.
  • APP_SECRET_KEY et DATABASE_URL n'ont pas de valeur par défaut. L'application refuse de démarrer si l'un manque. C'est délibéré : mieux vaut un échec au démarrage qu'un service qui tourne avec un secret de démonstration.

Deux fichiers d'environnement, deux usages : .env à la racine alimente docker-compose.yml, apps/backend/.env alimente l'API lancée sur le poste.

Routes exposées

Méthode Chemin Dans l'OpenAPI Rôle
GET /api/v1/health/live oui Le processus répond. Ne touche pas la base
GET /api/v1/health/ready oui La base répond et l'extension TimescaleDB est chargée
GET /metrics non Format Prometheus, exposé par l'instrumentator
GET /docs, /redoc, /openapi.json non Désactivés quand APP_ENV=prod

Aucune route métier n'existe à ce jour.

/health/ready

Cette sonde porte une garde décrite dans l'ADR 0001 : un bootstrap de base sauté ne se voit pas au démarrage de l'API, elle le rend visible.

sequenceDiagram
  participant C as Client
  participant R as readiness
  participant E as get_engine
  participant D as PostgreSQL

  C->>R: GET /api/v1/health/ready
  R->>E: session, engine créé au premier appel
  R->>D: SELECT extversion FROM pg_extension WHERE extname = 'timescaledb'
  alt base injoignable
    D--xR: SQLAlchemyError ou OSError
    R-->>C: 503 Base de donnees injoignable
  else extension absente
    D-->>R: NULL
    R-->>C: 503 Extension TimescaleDB absente
  else
    D-->>R: version de l'extension
    R-->>C: 200 status ready
  end

Sécurité

Voir la vue consolidée dans 00-vue-ensemble.md. Côté backend :

  • Aucune authentification, aucune autorisation. Les deux routes sont publiques. Le premier endpoint métier imposera de trancher ce point.
  • Le CORS n'autorise que les origines listées, et n'existe pas si la liste est vide.
  • /docs, /redoc et /openapi.json disparaissent en production.
  • Le conteneur tourne en utilisateur non-root, avec un HEALTHCHECK sur /api/v1/health/live.
  • Ni limitation de débit, ni journalisation des accès, ni en-têtes de sécurité.

Observabilité

  • Journalisation par dictConfig : format console en développement, JSON dès APP_ENV=prod. sqlalchemy.engine est forcé à WARNING pour ne pas noyer les journaux.
  • /metrics au format Prometheus. Aucun collecteur ne le lit : monitoring/ est vide.

Tests

Conventions, gabarits et arborescence : apps/backend/TESTING.md. Deux points structurants y sont fixés : les doubles passent par app.dependency_overrides et jamais par unittest.mock, et les tests qui touchent la vraie base portent le marqueur integration, exclu par défaut.

Questions ouvertes

  • Authentification et autorisation : quel mécanisme, quelle granularité.
  • Pagination et fenêtrage des lectures de séries temporelles, qui conditionnent la forme des endpoints métier.
  • Politique de versionnement de l'API au-delà du préfixe /api/v1.