FastAPI Hexagonal Code source

FastAPI Hexagonal

Une référence exécutable, pas un schéma. Un dépôt à lire et à copier pour faire du FastAPI hexagonal comme en vrai, avec les filets de sécurité et le déploiement qui vont avec — pas un CRUD déguisé en slides. Et la même méthode s'installe dans ton assistant de code, Claude Code, Codex ou Cursor, pour l'appliquer sur tes propres projets.

Pourquoi ce dépôt existe#

Dans une application mal structurée, le code métier finit par se dissoudre dans les détails techniques. On ne s'en aperçoit pas en écrivant : on s'en aperçoit en essayant de tester une règle sans lever une base, en changeant d'outil, ou en cherchant six mois plus tard où vit une décision.

Le symptôme n'est pas esthétique. C'est qu'une règle comme « une tâche terminée ne peut pas être terminée deux fois » n'a pas de domicile : elle est réécrite dans un router, oubliée dans un worker, contournée par un script d'import. Ce dépôt donne une adresse à chaque type de décision, et vérifie mécaniquement qu'elles y restent.

La règle est vérifiée, pas affichée#

« Le domaine ne dépend d'aucun framework » est une phrase que tout le monde écrit dans son README. Une règle d'architecture qui n'est pas vérifiée automatiquement se dégrade en quelques mois — alors ici, elle casse le build.

On ajoute un import interdit dans une entité du domaine :

  from __future__ import annotations

  from datetime import UTC, datetime
+ from sqlalchemy import Column

Le build casse, avec make imports et les hooks installés :

$ make imports

Analyzed 130 files, 437 dependencies.

Architecture hexagonale (dépendances vers l'intérieur) KEPT
Domaine sans framework BROKEN
…
task_manager.domain is not allowed to import sqlalchemy:

-   task_manager.domain.task.entities -> sqlalchemy (l.10)

import-linter suit le graphe d'imports réel, y compris transitif — là où un grep ne verrait rien. Deux contrats sont déclarés dans pyproject.toml : l'empilement des couches, et l'interdiction faite au domaine d'atteindre fastapi, sqlalchemy, pydantic ou starlette. Tout le reste de ce dépôt — ports, mappers, DTO, composition root — n'est que la machinerie qui rend cette règle tenable.

La forme que ça donne#

DRIVING · PILOTENT DRIVEN · PILOTÉS API HTTP CLI Worker Application Domaine PostgreSQL Valkey SMTP RabbitMQ FastAPI click consumer RabbitMQ use cases · DTO · ports techniques entités · value objects · ports métier exceptions métier SQLAlchemy cache aiosmtplib aio-pika sans framework · aucune I/O appellent les use cases implémentent les ports
Les flèches sont les import. À l'exécution, l'application appelle la base ; à la compilation, c'est la base qui dépend d'une interface définie plus au centre.

Ce qu'on y trouve#

Jusqu'au cluster#

La plupart des dépôts d'architecture s'arrêtent au docker compose up. Celui-ci va jusqu'à un cluster Kubernetes, parce que c'est là que les décisions prises dans le code se paient — ou se vérifient.

Le pooler fonctionne en mode transaction : la connexion PostgreSQL peut changer entre deux transactions. Les usages liés à une session demandent donc une attention particulière. Les réglages SQLAlchemy et le Job Alembic en connexion directe sont détaillés dans le chapitre sur les données. Ces contraintes existent déjà avec Compose ; Kubernetes y ajoute le placement et le basculement des instances.

BriqueCe qui tournePourquoi c'est là
ClusterKind, 1 control-plane + 3 workersReproduire l'anti-affinité et la perte d'un nœud sur un portable
PostgreSQLCloudNativePG, 3 instances + pooler ×2 en mode transactionBascule primaire gérée par l'opérateur, pas par un script
BrokerRabbitMQ, 3 nœuds via l'opérateur officielLe worker d'e-mails consomme une vraie file, avec DLQ
Montée en chargeHPA api 2→6, frontend 2→4 · PDB minAvailable: 1Limiter les évictions applicatives simultanées
DurcissementrunAsNonRoot, readOnlyRootFilesystem, capabilities drop ALL, seccompSur les pods qu'on maîtrise ; les images tierces restent en défaut, et c'est écrit
MigrationsJob Alembic contre PostgreSQL en direct, avant l'APIConnexion directe pour maîtriser la session

Le tout se monte par un script (./k8s/deploy.sh) et se démonte par un autre. La section infrastructure détaille chaque brique : les données, les workloads, la résilience, le réseau et l'exploitation.

Ce que ce n'est pas#

Ce que tu cherchesIciOù regarder sinon
Comprendre comment on isole un domaine, avec la preuveOui—
Un exemple complet de use case à la NetworkPolicyOui—
Un support de cours ou un kata d'équipeOui—
Une méthode que ton assistant de code applique avec toiOuiL'Agent Skill, dans Claude Code, Codex et Cursor — il transmet les décisions, il ne génère pas l'appli à ta place
Un starter « clone and ship to prod »Nonfull-stack-fastapi-template
Un framework, une bibliothèque, un paquet PyPINonLe code se lit et se copie ; seul l'Agent Skill s'installe
La bonne architecture, valable partoutNonQuand ne pas l'utiliser
Un domaine métier riche à étudierNonLe métier est un prétexte assumé — c'est discuté ici

Le métier — un gestionnaire de tâches — est volontairement simple. Il n'est pas le produit : le comment est le produit.

Cette liste de « non » est le point important. Un dépôt qui prétend tout couvrir n'apprend rien à personne, et le chapitre sur les cas où cette architecture est du sur-dimensionnement est écrit avant tous les autres.

Le cours#

Quinze chapitres progressifs, chacun ancré dans le code de ce dépôt. Il n'enseigne pas une arborescence de dossiers : il enseigne deux questions — à qui appartient cette décision ? et qu'est-ce qui doit être cohérent dans la même transaction ?

En complément, un carnet de terrain sur le DDD stratégique : la procédure d'atelier qu'un dépôt de code ne peut pas enseigner, et un diagnostic honnête des frontières de ce projet.

Le cours, utilisable par un agent#

La méthode de ce dépôt est aussi distribuée comme Agent Skill, installable dans Claude Code, OpenAI Codex et Cursor, et utilisable sur n'importe quel projet — pas seulement celui-ci. Il ne dit pas « recopie ce Task Manager » : il aide un agent à décider où placer une règle, où tracer une frontière d'agrégat, quel port créer, quand court-circuiter le domaine en lecture — et, tout aussi important, quand cette architecture est surdimensionnée.

# Claude Code
/plugin marketplace add EdwinAlkins/fastapi-hexagonal
/plugin install fastapi-hexagonal-ddd@fastapi-hexagonal
# OpenAI Codex
codex plugin marketplace add EdwinAlkins/fastapi-hexagonal
codex plugin add fastapi-hexagonal-ddd@fastapi-hexagonal
# Cursor
/add-plugin https://github.com/EdwinAlkins/fastapi-hexagonal

Le SKILL.md reste court : dix fiches de references/ — architecture, DDD stratégique, domaine, application, persistance, transactions et erreurs, lectures et écritures, événements, tests, structure de projet — ne sont chargées que lorsque la tâche l'exige. Le périmètre est volontairement restreint à l'architecture applicative : ni Kubernetes, ni observabilité, ni frontend, ni CI. Un skill qui essaie de tout faire se déclenche mal.

Le cours reste la source de vérité

Le skill est écrit en anglais — le déclenchement est plus fiable côté Claude, Codex et Cursor — alors que le cours reste en français. Il distingue systématiquement le principe, transférable, du choix de ce dépôt (UUIDv7, RabbitMQ, Valkey, PostgreSQL, import-linter, testcontainers) : ces derniers sont présentés comme des options défendables, jamais comme des lois. Toute correction de fond se fait d'abord dans le cours, puis se répercute dans le skill.

Contenu détaillé et développement : plugins/fastapi-hexagonal-ddd/.

Démarrage#

Python 3.14, uv et un démon Docker. La base et le cache tournent en conteneurs ; l'API tourne en local.

git clone https://github.com/EdwinAlkins/fastapi-hexagonal.git
cd fastapi-hexagonal
cp .env.template .env

docker compose up -d postgres pgbouncer valkey rabbitmq mailpit
docker compose run --rm migrate

cd backend
uv sync
make run                      # → http://localhost:8000/docs

Pour lancer la stack entière (API, worker, frontend, RabbitMQ, Mailpit, Prometheus) : docker compose up --build depuis la racine.

Et pour vérifier que tout tient — lint, contrats d'architecture, typage strict et les trois niveaux de tests :

# Depuis backend/
make check

Licence AGPL-3.0. Tout à lire, et une méthode à emporter.