Aller au contenu

Cas d'étude — SaaS e-commerce multi-tenant

Appliquer la validation et la gouvernance a un système réel — un SaaS e-commerce multi-tenant de bout en bout.


Contexte

Ce cas d'étude traverse l'ensemble du parcours d'architecture (chapitres 00 a 08) et illustre comment chaque concept s'applique a un système concret. On retrouve ici le même système que dans les chapitres précédents, avec un focus sur la validation et la gouvernance.

Système de e-commerce SaaS multi-tenant : 50 tenants, 10 000 commandes par jour, conformité RGPD. Les tenants partagent l'infrastructure mais leurs données sont strictement isolées. Le système doit rester opérationnel pendant les pics de trafic (promotions, soldes) et garantir la traçabilité des données personnelles.


Cadrage — exigences non-fonctionnelles

Le cadrage (chapitre 01) définit les attributs qualité qui pilotent toutes les décisions architecturales.

Attribut Cible Mesure
Latence p99 < 300 ms Prometheus histogram
Disponibilité 99.95% (~4h/an) Uptime check externe
RPO < 5 minutes Ecart entre dernier backup et incident
Throughput 50 commandes/seconde en pic k6 load test

Ces exigences deviennent les seuils des fitness functions en CI. Sans mesure, ce ne sont que des vœux.


ADR structurant

Le processus de décision (chapitre 03) s'applique des le premier choix structurant.

ADR-001 : Monolithe modulaire avec extraction progressive

Contexte : l'équipe est de taille moyenne (8 développeurs), le domaine est bien compris, le budget infra est contraint. Une architecture microservices complète introduirait une complexité opérationnelle disproportionnee.

Décision : monolithe modulaire avec des modules a frontieres strictes. Le module Payment sera extrait en premier en service indépendant (contrainte PCI-DSS). Les autres modules peuvent être extraits progressivement si le besoin s'en fait sentir.

Conséquences : simplicité opérationnelle initiale, risque de couplage involontaire a surveiller via fitness functions, chemin d'extraction documente.

Gouvernance : cet ADR a été valide par le comite d'architecture. Le critère de révision est défini : si le throughput dépassé 200 commandes/seconde ou si l'équipe dépassé 20 développeurs, on reevalue l'architecture.

ADR-002 : PostgreSQL RLS pour l'isolation multi-tenant

Contexte : l'isolation des données entre tenants est une exigence reglementaire (RGPD). Trois options ont été evaluees : schéma par tenant, database par tenant, row-level security.

Décision : row-level security avec partitioning par tenant_id. Le filtrage est automatique via des policies RLS, l'application positionne le tenant en début de session.

Conséquences : simplicité opérationnelle (un seul schéma a maintenir), performance grâce au partitioning, risque de fuite si la policy RLS est mal configurée — couvert par une fitness function dédiée.

Gouvernance : revue sécurité obligatoire pour toute modification des policies RLS. Fitness function en CI qui valide l'isolation.


Topologie — vue C4

La modélisation (chapitre 04) produit les diagrammes de référence qui vivent dans l'architecture repository.

C4Container
    title SaaS e-commerce multi-tenant — Container diagram

    Person(user, "Acheteur", "Navigue, commande, paye")
    Person(admin, "Administrateur tenant", "Gere le catalogue et les commandes")

    System_Boundary(platform, "Plateforme e-commerce") {
        Container(gateway, "API Gateway", "nginx / Kong", "Routage, auth JWT, rate limiting par tenant")
        Container(catalog, "Catalog Module", "Python/FastAPI", "Produits, categories, stock")
        Container(order, "Order Module", "Python/FastAPI", "Panier, commandes, workflow")
        Container(notification, "Notification Module", "Python/FastAPI", "Email, SMS, webhooks")
        ContainerDb(postgres, "PostgreSQL", "PostgreSQL 16", "Row-level security, partitioning par tenant_id")
        ContainerDb(redis, "Redis", "Redis 7", "Cache par tenant, sessions")
        Container(mq, "RabbitMQ", "RabbitMQ 3.13", "Events inter-modules")
    }

    System_Ext(payment, "Payment Provider", "Stripe / Adyen")
    System_Ext(smtp, "SMTP Provider", "Mailgun")

    Rel(user, gateway, "HTTPS")
    Rel(admin, gateway, "HTTPS")
    Rel(gateway, catalog, "REST")
    Rel(gateway, order, "REST")
    Rel(order, payment, "REST (mTLS)")
    Rel(order, mq, "Publish: order.placed")
    Rel(notification, mq, "Subscribe: order.placed")
    Rel(notification, smtp, "SMTP")
    Rel(catalog, postgres, "SQL")
    Rel(order, postgres, "SQL")
    Rel(catalog, redis, "Cache")
    Rel(order, redis, "Sessions")

Ce diagramme est versionne dans l'architecture repository (chapitre 04 de cette section). Toute modification de la topologie passe par une PR avec un ADR si le changement est structurant.


Communication inter-modules

Les patterns de communication (chapitre 06) sont documentes et gouvernes.

  • Synchrone (REST) : opérations utilisateur directes — consultation catalogue, création commande, interrogation statut. Justification : cohérence forte requise, UX immédiate.
  • Asynchrone (RabbitMQ) : notifications post-commande, mise à jour analytics, génération de factures. Justification : découplage temporel, résilience aux pics, pas de contrainte de cohérence forte.

Le choix REST vs messaging est documente dans ADR-003. Le critère de révision : si le volume d'events dépassé 10 000/seconde ou si le besoin de replay emerge, on reevalue le broker (Kafka en assessment sur le radar).

Guardrail : une fitness function vérifié qu'aucun appel synchrone n'est fait depuis un handler d'événement asynchrone — ce serait un anti-pattern qui reintroduirait du couplage temporel.


Données et isolation multi-tenant

La stratégie de données (chapitre 06) est un point critique pour la sécurité et la conformité.

PostgreSQL avec deux mécanismes d'isolation :

  • Row-level security : chaque requête filtre automatiquement par tenant_id via une policy RLS. L'application positionne SET app.current_tenant = :tenant_id en début de session.
  • Partitioning : les tables orders et order_items sont partitionnees par tenant_id pour les performances de requête et la facilite de purge RGPD (suppression de partition entière).

Cache Redis structure par tenant : clef tenant:{tenant_id}:catalog:{product_id} — evite les collisions et permet l'invalidation selective par tenant.

Fitness function : un test vérifié qu'aucune requête SQL dans le codebase n'accede aux tables partitionnees sans clause WHERE tenant_id = .... Les requêtes sans filtre tenant sont une fuite de données potentielle.


Résilience

Les patterns de résilience (chapitre 07) sont instrumentes et valides en continu.

  • Circuit breaker sur le payment provider : seuil de 50% d'erreurs sur 10 secondes ouvre le circuit. En état ouvert, les tentatives de paiement retournent une erreur explicite plutôt que de bloquer.
  • Retry avec backoff exponentiel sur les notifications : 3 tentatives, backoff 1s / 5s / 30s. Les échecs definitifs vont en dead letter queue pour analyse.
  • Health checks readiness : incluent la connectivité PostgreSQL et RabbitMQ — un pod sans accès DB ne reçoit pas de trafic.

Gouvernance : les seuils de circuit breaker et les politiques de retry sont documentes dans l'architecture repository. Toute modification passe par un ADR léger.


Sécurité

La stratégie de sécurité (chapitre 08) est validee et gouvernee en continu.

  • Zero trust inter-modules : chaque requête inter-module porte un JWT interne signe par le service appelant, vérifié par le service cible. Pas de confiance implicite sur le réseau interne.
  • TLS everywhere : TLS entre gateway et modules, mTLS entre le monolithe et le payment provider (exigence PCI-DSS).
  • STRIDE appliqué : modèle de menace formalise sur l'API gateway (spoofing de tenant_id, elevation de privilege) et sur le payment flow (tampering des montants, information disclosure des données carte).

Guardrails sécurité :

  • Scan CVE automatique en CI (Trivy) — zero vulnérabilité critique autorisee
  • Détection de secrets dans le code (gitleaks) — bloque le merge
  • Vérification des headers de sécurité HTTP (OWASP ZAP baseline) — alerte si manquant

Validation — SLO et fitness functions

SLO définis et instrumentes

  • p99 latence API < 300 ms — mesure via Prometheus histogram, alerte si dépassement sur 5 min
  • Error rate < 0.1% sur les endpoints commande — mesure via compteurs Prometheus
  • Disponibilité 99.95% — mesure via uptime check externe

Fitness functions CI

# Verifie que Order Module n'importe pas depuis Catalog Module
def test_order_does_not_import_catalog():
    ...

# Verifie qu'aucune image Docker ne depasse 500 MB
def test_docker_image_size():
    result = subprocess.run(
        ["docker", "image", "inspect", "ecommerce-api:latest",
         "--format", "{{.Size}}"],
        capture_output=True, text=True
    )
    size_bytes = int(result.stdout.strip())
    assert size_bytes < 500 * 1024 * 1024, (
        f"Image trop lourde: {size_bytes / 1e6:.0f} MB"
    )

# Verifie que toutes les requetes SQL filtrent par tenant_id
def test_all_queries_filter_by_tenant():
    ...

# Verifie que les circuit breakers sont configures
def test_circuit_breaker_config():
    ...

Gouvernance en action

Le comite d'architecture revoit trimestriellement :

  • Les métriques SLO : est-ce que les cibles sont atteintes ?
  • Les violations de fitness functions : combien, quelles tendances ?
  • Le backlog de dette technique : est-ce que le score total diminue ?
  • Le Technology Radar : est-ce que les choix technologiques restent pertinents ?

Les résultats sont documentes dans un compte-rendu accessible à toutes les équipes. Les actions sont trackees dans le backlog commun.


Dette technique identifiée

Le système e-commerce porte de la dette technique délibérée et prudente, documentee dans le backlog.

Item de dette Sévérité Effort Stratégie ADR
Cache Redis sans invalidation event-driven Moyenne Moyen Strangler fig vers pub/sub invalidation ADR-018
Monolithe sans feature flags Faible Faible Boy scout rule, ajouter progressivement ADR-022
Tests d'intégration lents (8 min) Moyenne Élevé Parallelisation CI + refactoring fixtures Planifie
Pas de distributed tracing Élevée Moyen Ajouter OpenTelemetry au prochain sprint ADR-025

Le score total de dette est suivi dans le tableau de bord et révisé à chaque rétrospective architecture.


Technology Radar du projet

Le comite maintient un radar spécifique au projet, aligne sur le radar de l'organisation.

Quadrant Technologie Anneau Justification
Backend FastAPI Adopt Standard confirme, performance et DX
Backend Celery Adopt Workers asynchrones, stable
Datastore PostgreSQL 16 Adopt RLS, partitioning, maturité
Datastore Redis 7 Adopt Cache et sessions, performant
Messaging RabbitMQ Adopt Simple, suffisant pour le volume actuel
Messaging Kafka Assess A évaluer si besoin de replay ou volume > 10k evt/s
Observabilité Prometheus Adopt Standard métriques
Observabilité OpenTelemetry Trial Distributed tracing, en cours d'évaluation

Leçons apprises

Après 18 mois d'exploitation, les enseignements clés :

  • Le RLS PostgreSQL fonctionne mais nécessité une vigilance constante. Chaque nouvelle requête doit être vérifiée par la fitness function tenant isolation. Deux incidents de fuite de données ont été evites grâce à ce guardrail.
  • Le monolithe modulaire a tenu pour 8 développeurs et 50 tenants. Le critère de révision (200 cmd/s ou 20 devs) n'a pas été atteint. La décision de ne pas partir en microservices etait la bonne pour ce contexte.
  • Les fitness functions sont le meilleur investissement du projet. Elles ont détecté 12 violations de couplage en 6 mois — toutes corrigees avant la production. Le coût de les écrire est negligeable compare au coût des bugs qu'elles ont evites.
  • La gouvernance légère suffit pour une équipe de cette taille. Un comite mensuel de 2 heures, des ADR en PR, et des fitness functions en CI couvrent 95% des besoins.

Tip

Un cas d'étude n'est pas un template a copier. C'est un exemple de raisonnement — chaque système a ses propres contraintes, ses propres trade-offs, et ses propres priorités. L'important est la démarche, pas la solution spécifique.

Chapitre suivant : Cas d'étude — Plateforme d'automatisation — validation et gouvernance appliquees a une plateforme d'automatisation infrastructure.