Aller au contenu

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_name doit ê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 casser ruff 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 couches controller/ + interface/http/v1/ + model/{business,persistence,repositories}/, routes métier sous /api/v1. Pas de src/<name>/, ni de domain.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

  1. Renommez app/modules/widgets/ en app/modules/<votre_domaine>/.
  2. Adaptez le SQLModel dans model/persistence/<entity>_record.py et son accès dans model/repositories/<entity>_repository.py.
  3. Écrivez vos cas d'usage dans controller/<action>.py (1 classe par action) et leurs résultats dans model/business/<action>.py.
  4. Adaptez payloads/handlers + routes dans interface/http/v1/<action>.py et interface/http/v1/routes.py (sous /api/v1).
  5. Mettez à jour les tests miroir dans tests/modules/<votre_domaine>/.
  6. Câblez le module dans app/bootstrap/wiring/<votre_domaine>.py puis startup/http_routes.py.

Détail fichier par fichier : Anatomie d'un module. Exemples réels suivant cette structure : rdlink-api.