Scaffold cookiecutter¶
Le template mic-struct/template/ génère un service mic-struct complet,
déjà sur la structure canonique, qui
passe make gate à 100 % du premier coup.
Usage¶
pip install cookiecutter
cookiecutter https://gitlab.com/code-libs/python/mic/mic-struct.git --directory=template
Trois questions (les valeurs entre crochets sont les défauts) :
service_name [example-service]:
service_description [A mic-struct service — customize me]:
http_framework [fastapi]: # ou litestar
Le hook pre_gen valide vos réponses avant la génération :
service_namedoit être en kebab-case et en minuscules (ex.my-service).http_framework∈ {fastapi,litestar}.service_description: longueur max calculée dynamiquement (selon la longueur du nom) pour ne pas casserruff E501.
Échec → message clair, pas de scaffold inutilisable.
Ce qui est généré¶
example-service/
├── pyproject.toml # packages=["app"] ; mic-core[<framework>,auth,client,observability] + mic-datastore[postgres]
├── Makefile # gate / format / lint / typecheck / test-cov / run / migrate*
├── README.md
├── .gitignore
├── .python-version # 3.14
├── alembic.ini
├── alembic/ # env.py + script.py.mako + versions/
├── tools/bump_service.py
├── app/ # ← package (PAS src/<name>/)
│ ├── bootstrap/
│ │ ├── config/settings.py # pydantic-settings
│ │ ├── context/execution_context.py # conteneur DI (frozen dataclass)
│ │ ├── providers/{auth,database}.py # factories de dépendances
│ │ ├── startup/{http_routes,http_runtime}.py
│ │ └── wiring/{system,widgets}.py # 1 fichier de câblage par module
│ ├── entrypoints/
│ │ ├── http/main.py # entrypoint uvicorn
│ │ └── cli/
│ ├── frameworks/
│ │ ├── fastapi/middlewares/auth.py
│ │ ├── litestar/
│ │ └── cli/
│ ├── modules/
│ │ ├── system/ # health + ready (toujours présent)
│ │ │ ├── controller/{health,readiness}.py
│ │ │ └── interface/http/routes.py
│ │ └── widgets/ # module CRUD exemple — à remplacer, 5 couches
│ │ ├── controller/{create_widget,read_widget}.py # cas d'usage (classes)
│ │ ├── interface/http/v1/{create_widget,read_widget,routes}.py # /api/v1
│ │ └── model/
│ │ ├── business/{create_widget,read_widget}.py # résultats (StrictSchema)
│ │ ├── persistence/widget_record.py # WidgetRecord
│ │ └── repositories/widget_repository.py # WidgetRepository
│ ├── platform/services/
│ └── shared/
└── tests/ # miroir de app/
├── conftest.py # fixtures database + client (SQLite + StaticPool)
├── test_alembic.py # structure + exécutabilité + drift
├── test_app.py · test_health.py · test_init.py · test_routes.py · test_settings.py
└── modules/{system,widgets}/… # tests miroir 100 %
C'est exactement la structure canonique : package
app/, modules décomposés en 5 couchescontroller/ + interface/http/v1/ + model/{business,persistence,repositories}/, routes métier sous/api/v1. Pas desrc/<name>/, ni dedomain.py/routes.pyà plat.
Premier make gate¶
cd example-service
make bootstrap-dev # crée .venv + installe le service + deps de dev
make gate # format-check + lint + typecheck + test-cov
make gate enchaîne black --check, ruff, mypy --strict, puis
pytest --cov=app --cov-branch --cov-fail-under=100. Le service généré passe à
100 % lignes + branches du premier coup.
Adapter pour votre métier¶
- Renommez
app/modules/widgets/enapp/modules/<votre_domaine>/. - Adaptez le SQLModel dans
model/persistence/<entity>_record.pyet son accès dansmodel/repositories/<entity>_repository.py. - Écrivez vos cas d'usage dans
controller/<action>.py(1 classe par action) et leurs résultats dansmodel/business/<action>.py. - Adaptez payloads/handlers + routes dans
interface/http/v1/<action>.pyetinterface/http/v1/routes.py(sous/api/v1). - Mettez à jour les tests miroir dans
tests/modules/<votre_domaine>/. - Câblez le module dans
app/bootstrap/wiring/<votre_domaine>.pypuisstartup/http_routes.py.
Détail fichier par fichier : Anatomie d'un module. Exemples
réels suivant cette structure : rdlink-api.