Architectures Back-End
Monolithes, microservices, événements, DDD, hexagonal, messaging & queues : comment structurer un systÚme back-end robuste, évolutif et maintenable.
Monolithes, microservices, SOA, serverless DDD, bounded contexts, hexagonal / clean architecture Events, queues, sagas, patterns de résilience
1) Panorama des architectures back-end
| Style | Description | Points forts | Limites / risques |
|---|---|---|---|
| Monolithe | Une seule application déployée en bloc. | Simple à déployer, debug plus facile, transactions locales. | Couplage fort, déploiements risqués, scalabilité limitée. |
| Monolithe modulaire | Monolithe, mais découpé en modules / contextes internes. | Transition douce vers microservices, meilleure isolation logicienne. | Discipline de modularisation nécessaire. |
| Microservices | Services indépendants, communiquant via réseau. | Scalabilité fine, indépendance de déploiement, stacks variées. | Complexité réseau, observabilité, cohérence distribuée. |
| Serverless | Fonctions Ă©phĂ©mĂšres dĂ©clenchĂ©es par Ă©vĂ©nements. | Facturation Ă lâusage, scaling automatique fort. | Cold start, limites runtime, vendor lock-in. |
Questions Ă se poser
- Volume de trafic ? Nombre de développeurs ? Réglementations ?
- FrĂ©quence de dĂ©ploiement souhaitĂ©e ? CapacitĂ© dâobservabilitĂ© ?
- Ătat du SI existant : historique monolithique, ESB, APIs ?
Schéma ultra simplifié (ASCII)
Monolithe
[ Web/API + Business + DB access ] ---> [ DB ]
Microservices
[ Service A ]-->[ DB A ]
[ Service B ]-->[ DB B ]
| ^
v |
[ Messaging / API ]2) Couches & séparation des responsabilités
Architecture en couches classique
[ Controller / API ]
|
[ Service / Domaine ]
|
[ Repository / ORM ]
|
[ DB / Cache ]- Chaque couche a une responsabilité claire.
- Le domaine ne dépend pas des détails (framework, DB, HTTP).
Bonnes pratiques
- Pas dâaccĂšs direct Ă la DB depuis les contrĂŽleurs.
- Regrouper les rÚgles métier dans des services/domain.
- Interfaces pour les repos â facile Ă mocker en tests.
Anti-patterns
- âFat controllerâ de 400 lignes.
- RequĂȘtes SQL dispersĂ©es partout dans le code.
- Couche âserviceâ qui ne fait que dĂ©lĂ©guer sans logique mĂ©tier.
Exemple simplifié (Python)
# controller
@app.post("/orders")
def create_order(payload: OrderIn):
return service.create_order(payload)
# service
class OrderService:
def __init__(self, repo):
self.repo = repo
def create_order(self, data):
order = Order(**data.dict())
order.validate_business_rules()
return self.repo.save(order)3) Domain-Driven Design & bounded contexts
Concepts clés
- Ubiquitous language : vocabulaire partagé dev + métier.
- Bounded context : pĂ©rimĂštre oĂč ce langage est cohĂ©rent.
- AgrĂ©gat : ensemble cohĂ©rent avec un ârootâ qui protĂšge les invariants.
Exemple de contextes
[ Billing ] [ Orders ] [ Catalog ]
| | |
factures commandes produits/prix- Chaque contexte peut ĂȘtre un module ou un service.
- Les communications se font via événements ou APIs claires.
Exemple dâagrĂ©gat Order (pseudo-code)
class Order(AggregateRoot):
def add_item(self, product, qty):
# rÚgle métier : pas plus de 50 lignes
if len(self.lines) >= 50:
raise MaxLinesExceeded()
self.lines.append(Line(product, qty))4) Architecture hexagonale / ports & adapters
Principe
[ Port in ]
UI / API / CLI
|
[ Domaine ]
|
DB / MQ / HTTP
[ Ports out ]- Le domaine ne dépend pas des technologies externes.
- Les adapters (HTTP, DB, MQ, file) implémentent les ports.
Exemple port + adapter (pseudo Python)
class OrderRepositoryPort(Protocol):
def save(self, order: Order) -> None: ...
def find(self, id: str) -> Order | None: ...
class DjangoOrderRepository(OrderRepositoryPort):
def save(self, order):
OrderModel.from_domain(order).save()
# service reçoit OrderRepositoryPort, pas l'ORMObjectif : rendre le domaine testable sans framework, et pouvoir changer la technologie (DB, transport) avec un impact limité.
5) ĂvĂ©nements, messaging & queues
Cas dâusage
- Envoyer des emails / SMS aprĂšs une commande.
- Synchroniser des données vers un data warehouse.
- DĂ©coupler actions lentes (facturation, PDF, exportâŠ).
Bonnes pratiques
- Messages immuables, auto-descriptifs (id, type, timestamp).
- Consommateurs idempotents (replay possible).
- Outbox pattern pour garantir la publication avec la transaction.
Exemple dâevent (JSON)
{
"type": "order.created",
"id": "evt_123",
"occurred_at": "2025-12-01T10:00:00Z",
"data": {
"order_id": "ord_789",
"customer_id": "cus_456",
"total": 123.45
}
}6) Résilience & patterns de fiabilité
-sm btn-outline-light" onclick="window.print()"> Imprimer
Patterns clés
- Timeouts partout (DB, HTTP, MQ).
- Retries avec backoff + jitter.
- Circuit breaker pour éviter les avalanches.
- Bulkheads pour isoler les ressources critiques.
Transactions & sagas
- Impossible de faire un 2PC global partout â utiliser des sagas.
- Saga orchestrée (service central) ou chorégraphiée (events).
- Compensations pour annuler les effets dâune Ă©tape.
Exemple pseudo-code âretry + timeoutâ
for attempt in range(3):
try:
with timeout(2.0):
return call_remote_service()
except TimeoutError:
sleep(backoff(attempt))
raise ServiceUnavailable()7) Observabilité & frontiÚres de service
SLO par service
- Uptime, latence P95/P99, taux dâerreur.
- Quota / limite pour Ă©viter lâabus dâun consommateur.
- Dashboards par domaine (Orders, Billing, AuthâŠ).
Instrumentation
- Logs structurés (JSON) avec
trace_id&span_id. - Métriques Prometheus :
http_requests_total,db_latency_ms⊠- Traces distribuées (OpenTelemetry).
Exemple métrique
http_requests_total{
service="orders-api",
route="/orders/{id}",
method="GET",
status="200"
} 123458) StratĂ©gies dâĂ©volution & migration
Approche pragmatique
- Stabiliser dâabord le monolithe (tests, mĂ©triques, modules).
- Identifier les âhotspotsâ : domaines Ă fort dĂ©bit ou forte instabilitĂ©. odule, pas tout le SI en une fois.
Strangler Fig Pattern
[ Client ]
|
[ API Gateway ]
| \
[ Monolithe ] [ Nouveau service Bounded Context ]- On route progressivement certains endpoints vers les nouveaux services.
- Le monolithe se rĂ©trĂ©cit jusquâĂ pouvoir ĂȘtre retirĂ©.
Anti-patterns de migration
- âBig bang rewriteâ sans plan de rollback.
- Créer des microservices sans DDD ni bounded contexts.
- Multiplication des services âanĂ©miquesâ avec peu de logique mĂ©tier.
Monolithes, microservices, SOA, serverless â quand choisir quoi ?
| Style | Contexte idéal | Forces | Risques / limites |
|---|---|---|---|
| Monolithe simple | Produit en phase de découverte, équipe petite (< 6 devs). | Déploiement simple, debug facile, transactions locales. | Couplage fort, release monolithiques. |
| Monolithe modulaire | Produit qui grossit, plusieurs domaines fonctionnels. | Modules isolés, base pour extraire des services plus tard. | Demande de la discipline (frontiÚres de modules claires). |
| Microservices | Organisation multi-squads, besoins de scalabilité fine. | Déploiements indépendants, stacks hétérogÚnes possibles. | Coût en infra, observabilité, cohérence distribuée. |
| Serverless | Charges trĂšs variables, Ă©vĂ©nements nombreux, petits traitements. | Facturation Ă lâusage, scaling automatique extrĂȘme. | Cold starts, limites runtime, debugging plus difficile. |
| SOA / ESB | SystĂšmes historiques, intĂ©grations multiples. | Mutualisation de services transverses. | Peut devenir un âgros bus magiqueâ difficile Ă maintenir. |
RĂšgle pragmatique
- Commencer par un monolithe modulaire bien structuré.
- Passer aux microservices uniquement en cas de douleurs réelles (scalabilité, vitesse de livraison, boundaries métier claires).
- Utiliser le serverless pour des tùches unitaires, déclenchées par événements.
Schéma ultra simple
Monolithe modulaire
[ Web/API ]
|
[ Module Orders ]
[ Module Billing ]
[ Module Catalog ]
Microservices
[ orders-api ] [ billing-api ] [ catalog-api ]
\ | /
[ messaging / API gateway ]DDD, bounded contexts & architectures hexagonale / clean
DDD & bounded contexts
- Ubiquitous language : mĂȘme vocabulaire dev/mĂ©tier.
- Bounded context : zone oĂč ce langage reste cohĂ©rent.
- Chaque contexte = module fort ou microservice.
[ Orders ] [ Billing ] [ Catalog ]
commandes factures produitsHexagonale / Clean architecture
- Domaine au centre, indépendant des frameworks.
- Ports (interfaces) et Adapters (implémentations techniques).
- Facilite les tests unitaires du domaine.
UI / API --> [ Application ]
|
[ Domaine ]
|
DB / MQ / HTTP clientsExemple mix DDD + hexagonal (pseudo-code)
class OrderService:
def __init__(self, repo: OrderRepoPort, bus: EventBusPort):
self.repo = repo
self.bus = bus
def place_order(self, cmd: PlaceOrder):
order = Order.from_cmd(cmd)
order.validate_invariants()
self.repo.save(order)
self.bus.publish(OrderPlaced(order_id=order.id))Checklist âarchitecture orientĂ©e domaineâ
- Le domaine compile-t-il sans dépendre du framework web / ORM ?
- Les frontiĂšres entre contextes sont-elles explicites (modules, packages) ?
- Les adaptations techniques (DB, MQ, HTTP) sont-elles isolées dans des adapters ?
Events, queues, sagas & patterns de résilience
ĂvĂ©nements & queues
- Découplage fort : producer & consumer ne se connaissent pas.
- Exemples : Kafka, RabbitMQ, SQS, Pub/SubâŠ
- Outbox pattern pour publier avec la transaction locale.
{
"type": "user.registered",
"id": "evt_123",
"occurred_at": "2025-12-08T10:00:00Z",
"data": { "user_id": "u42", "email": "x@y.z" }
}Sagas & cohérence distribuée
- Saga orchestrée : un orchestrateur appelle les services un par un.
- Saga chorégraphiée : chaque service réagit à des events.
- Compensation : opĂ©rations âinverseâ en cas dâĂ©chec de lâĂ©tape n.
createOrder
âââ reserveStock
âââ authorizePayment
âââ confirmOrder
# si payment KO â releaseStock + cancelOrderPatterns de rĂ©silience
- Timeouts sur chaque appel externe (DB, HTTP, MQ).
- Retries avec backoff + jitter, seulement sur erreurs transitoires.
- Circuit breaker pour couper un service en panne.
for attempt in range(3):
try:
with timeout(2.0):
return call_remote()
except Timeout:
sleep(backoff(attempt))
raise ServiceUnavailable()Checklist âevent-driven & rĂ©silienceâ
- Les messages sont-ils immuables et versionnés ?
- Les consommateurs sont-ils idempotents (replay possible) ?
- Timeouts, retries et circuit breakers sont-ils configurés sur les calls critiques ?
