App factory, lifespan et injection de dépendances
create_app() : l'assembleur
Le package api/ ne contient presque aucune logique : c'est l'assembleur. Sa fonction create_app() construit l'application en montant les routers de chaque module :
def create_app() -> FastAPI:
app = FastAPI(lifespan=lifespan)
app.add_middleware(CORSMiddleware, ...)
app.add_middleware(AuthMiddleware) # JWT â tenant_id (module 3)
app.include_router(copilot_router, prefix="/api/copilot")
app.include_router(catalog_router, prefix="/api/catalog")
app.include_router(engine_router) # /extract, /jobs/{id}
return app
Chaque module expose son router.py (la couche HTTP) qui dĂ©lĂšgue Ă ses services/ (le mĂ©tier). Trois couches, pas plus : router â service â shared.
lifespan : le cùblage au démarrage
Le lifespan (async context manager) remplace les vieux startup/shutdown events. C'est lĂ que tout se branche :
@asynccontextmanager
async def lifespan(app: FastAPI):
engine = create_async_engine(DATABASE_URL, pool_pre_ping=True)
checkpointer = await setup_postgres_checkpointer() # LangGraph, module 6
app.state.copilot_service = CopilotService(
catalog=LocalCatalogProvider(...), # les Protocols du module 0,
prices=LocalPriceProvider(...), # cùblés ICI et nulle part ailleurs
)
reconciler = asyncio.create_task(job_reconcile_loop()) # boucles de fond
yield # â l'app tourne
reconciler.cancel() # arrĂȘt propre
await engine.dispose()
L'injection du projet est volontairement simple : pas de conteneur DI magique â les dĂ©pendances vivent sur app.state, posĂ©es une fois au boot, lues par les handlers via request.app.state. Explicite, typĂ©, testable (en test on pose des fakes sur app.state).
Les middlewares : l'oignon
Chaque requĂȘte traverse, dans l'ordre : sĂ©curitĂ© (headers) â logs structurĂ©s (structlog, un JSON par requĂȘte) â mĂ©triques RED (rate/errors/duration, exposĂ©es pour Prometheus) â auth (valide le JWT, rĂ©sout le tenant_id, le pose sur request.state) â le router. Le tenant_id posĂ© ici est LA donnĂ©e que tout le reste consomme â module suivant.
OĂč les implĂ©mentations concrĂštes des Protocols sont-elles instanciĂ©es ?