Architecture par échelle¶
La structure MIC (Model / Interface / Controller, framework-agnostique, DI explicite) reste identique à toutes les échelles. Ce qui change, ce n'est pas le code métier — ce sont le déploiement et les capacités activées. On ne réécrit jamais un controller pour monter en charge : on ajoute des réplicas et on branche une brique.
< 1 000 utilisateurs — monoprocessus¶
Le palier « démarrage ». L'objectif est la simplicité, pas la résilience.
- 1 réplica ASGI (uvicorn) + Postgres. Redis optionnel ; SQLite suffit en dev/tests.
- Pas de cache distribué, pas d'outbox : un seul service, pas de propagation.
/health+/ready+/metricsdès le départ — c'est gratuit et ça évite d'ajouter l'observabilité après coup.- Repère : quelques dizaines de RPS, p95 < 200 ms sans effort.
< 1 million — réplicas horizontaux + cache + read-models¶
Le palier « produit en croissance ». On met horizontalement à l'échelle des réplicas stateless.
- N réplicas stateless derrière un répartiteur de charge. L'état vit dans Postgres / Redis, jamais dans le processus → l'auth est stateless (JWT audience-bound).
- Redis cache (cache-aside) pour les lectures
chaudes ; HTTP response cache pour les
GET. - Inter-service en push signé, enrobé d'un circuit breaker et rendu sûr par l'idempotence (livraison at-least-once).
- Outbox transactionnel + EventBus pour la cohérence entre services ; rate limiting par identité en bordure.
- Repère : centaines à quelques milliers de RPS, p95 < 200 ms / p99 < 500 ms.
> 1 million — sharding, CQRS, fan-out¶
Le palier « grande échelle ». Les jointures et les chemins synchrones deviennent le goulot ; on pré-calcule et on partitionne.
- Sharding applicatif Postgres (par tenant / clé de hash) + réplicas lecture.
- CQRS généralisé : write normalisé (source de vérité) + read-models Redis (Sorted Set / INCR / HLL) alimentés en asynchrone par l'EventBus. La lecture chaude = 1 appel Redis, pas un join.
- Verrouillage distribué pour les singletons (dispatcher outbox) et workers de drain dédiés.
- Observabilité : tracing distribué + métriques à cardinalité bornée
(
status_class), indispensable pour diagnostiquer à ce volume. - Repère : 10k+ RPS. La capacité se dimensionne avec marge — pas d'autoscaling magique, la cohérence se gagne par design (idempotence, outbox).
Repères par palier¶
| Palier | Réplicas | Données | Capacités clés | RPS indicatif |
|---|---|---|---|---|
| < 1K | 1 | Postgres (ou SQLite) | health/ready, metrics | ~10-50 |
| < 1M | N stateless | Postgres + Redis | cache, response-cache, outbox, eventbus, circuit breaker, idempotence | ~100-2 000 |
| > 1M | N + workers dédiés | Postgres shardé + replicas + Redis | read-models/CQRS, sharding, locking, tracing | 10 000+ |
Ce qui ne change jamais¶
À tous les paliers, la couche M/I/C est la même : controllers purs (fonctions
testables sans framework), interfaces fines (HttpRouteSpec), modèles SQLModel
sans logique. Un service qui passe de 1 à 22 modules, et de 1K à 1M+ utilisateurs,
garde la même structure — voir Structure canonique.
C'est la promesse MIC : faire évoluer le déploiement, jamais réécrire le métier.