[{"data":1,"prerenderedAt":12388},["ShallowReactive",2],{"search-api":-1,"listing-cat-architecture-craft-page-1":3},[4,816,5655,7251,7943,9373,11607],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"id":11,"date":12,"listed":13,"nocomments":7,"hidden":7,"categories":14,"tags":15,"--cover":20,"readingTime":21,"body":26,"_type":810,"_id":811,"_source":812,"_file":813,"_stem":814,"_extension":815},"/fr/architecture-craft/database-per-service-microservices","architecture-craft",false,"","Database per Service : quand ça vaut vraiment la complexité","Le pattern Database per Service est présenté comme obligatoire en microservices. Il ne l'est pas. Le critère objectif pour décider — et les alternatives ignorées.",35,"2026-03-25",true,[6],[16,17,18,19],"Microservices","Database per Service","Architecture Distribuée","Base de Données","covers/articles/database-per-service-microservices.jpg",{"text":22,"minutes":23,"time":24,"words":25},"8 min read",7.61,456600,1522,{"type":27,"children":28,"toc":801},"root",[29,37,51,56,61,66,70,77,82,87,106,116,126,136,150,153,159,169,179,184,193,206,224,237,240,246,251,392,402,405,411,416,424,429,571,576,584,589,597,602,605,611,619,644,654,662,665,671,676,681,704,707,713,728,741,754,767,780,783,795],{"type":30,"tag":31,"props":32,"children":34},"element","h1",{"id":33},"database-per-service-quand-ça-vaut-vraiment-la-complexité",[35],{"type":36,"value":9},"text",{"type":30,"tag":38,"props":39,"children":40},"p",{},[41,43,49],{"type":36,"value":42},"J'ai accompagné un client dans le secteur du commerce en ligne qui avait adopté Database per Service dès le début, par principe, avant même d'avoir 5 développeurs. 18 mois plus tard, l'équipe de 6 développeurs backend passait ",{"type":30,"tag":44,"props":45,"children":46},"strong",{},[47],{"type":36,"value":48},"30% de son temps",{"type":36,"value":50}," à gérer l'infrastructure de 20 bases de données, les sagas qui échouaient partiellement, et les incohérences de données entre services.",{"type":30,"tag":38,"props":52,"children":53},{},[54],{"type":36,"value":55},"La décision que nous avons prise ensemble : migrer 15 des 20 services vers un schéma-per-service dans une base partagée. Seuls les 5 services critiques (ceux qui avaient vraiment des besoins différents de performance, d'isolation, ou de moteur) ont conservé leur base dédiée.",{"type":30,"tag":38,"props":57,"children":58},{},[59],{"type":36,"value":60},"Résultat : le temps passé à l'infrastructure est passé de 30% à 8%. L'équipe a pu réinvestir ces heures dans de la valeur produit.",{"type":30,"tag":38,"props":62,"children":63},{},[64],{"type":36,"value":65},"Database per Service n'est pas un impératif des microservices. C'est un trade-off. Et comme tout trade-off, il se décide selon le contexte, pas selon un dogme.",{"type":30,"tag":67,"props":68,"children":69},"hr",{},[],{"type":30,"tag":71,"props":72,"children":74},"h2",{"id":73},"le-problème-réel-que-database-per-service-résout",[75],{"type":36,"value":76},"Le problème réel que Database per Service résout",{"type":30,"tag":38,"props":78,"children":79},{},[80],{"type":36,"value":81},"Sam Newman dans \"Building Microservices\" (2014) a popularisé l'idée que chaque microservice devrait posséder ses données. L'intention était valide : éviter le couplage de base de données partagée qui crée des dépendances implicites entre services.",{"type":30,"tag":38,"props":83,"children":84},{},[85],{"type":36,"value":86},"Ces couplages sont réels :",{"type":30,"tag":38,"props":88,"children":89},{},[90,95,97,104],{"type":30,"tag":44,"props":91,"children":92},{},[93],{"type":36,"value":94},"Couplage de schéma :",{"type":36,"value":96}," si le service A modifie la table ",{"type":30,"tag":98,"props":99,"children":101},"code",{"className":100},[],[102],{"type":36,"value":103},"orders",{"type":36,"value":105},", le service B qui lit cette table peut casser sans que l'équipe A s'en rende compte.",{"type":30,"tag":38,"props":107,"children":108},{},[109,114],{"type":30,"tag":44,"props":110,"children":111},{},[112],{"type":36,"value":113},"Couplage de déploiement :",{"type":36,"value":115}," une migration de base de données doit être coordonnée avec tous les services qui lisent ou écrivent les tables concernées.",{"type":30,"tag":38,"props":117,"children":118},{},[119,124],{"type":30,"tag":44,"props":120,"children":121},{},[122],{"type":36,"value":123},"Couplage de performance :",{"type":36,"value":125}," une requête lente du service A consomme des ressources de connexion qui dégradent les performances du service B.",{"type":30,"tag":38,"props":127,"children":128},{},[129,134],{"type":30,"tag":44,"props":130,"children":131},{},[132],{"type":36,"value":133},"Couplage de technologie :",{"type":36,"value":135}," tous les services sont contraints d'utiliser le même moteur de base de données, même si certains auraient besoin d'un graph database et d'autres d'un document store.",{"type":30,"tag":38,"props":137,"children":138},{},[139,141,148],{"type":36,"value":140},"Ces couplages valent la peine d'être éliminés, mais la question est : à quel coût, et avec quelle alternative ? Et la décision de les éliminer (ou de les tolérer) mérite d'être consignée dans un ",{"type":30,"tag":142,"props":143,"children":145},"a",{"href":144},"/fr/architecture-craft/adr-architecture-decision-record",[146],{"type":36,"value":147},"Architecture Decision Record",{"type":36,"value":149}," pour que le contexte reste accessible à toute l'équipe.",{"type":30,"tag":67,"props":151,"children":152},{},[],{"type":30,"tag":71,"props":154,"children":156},{"id":155},"le-coût-réel-de-database-per-service",[157],{"type":36,"value":158},"Le coût réel de Database per Service",{"type":30,"tag":38,"props":160,"children":161},{},[162,167],{"type":30,"tag":44,"props":163,"children":164},{},[165],{"type":36,"value":166},"Complexité opérationnelle :",{"type":36,"value":168}," 10 services = 10 bases de données à provisionner, monitorer, sauvegarder, mettre à jour, et maintenir en haute disponibilité. Sur AWS, 10 instances RDS PostgreSQL coûtent entre 500 et 2 000€ par mois selon la taille, avant les coûts d'ingénierie pour les gérer.",{"type":30,"tag":38,"props":170,"children":171},{},[172,177],{"type":30,"tag":44,"props":173,"children":174},{},[175],{"type":36,"value":176},"Transactions distribuées :",{"type":36,"value":178}," sans base de données partagée, une opération qui modifie des données dans plusieurs services (créer une commande ET décrémenter le stock ET enregistrer le paiement) ne peut plus être traitée dans une transaction ACID.",{"type":30,"tag":38,"props":180,"children":181},{},[182],{"type":36,"value":183},"Il faut implémenter le pattern Saga, décrit en détail par Vernon Vaughn dans \"Implementing Domain-Driven Design\" :",{"type":30,"tag":185,"props":186,"children":188},"pre",{"code":187},"Saga chorégraphie pour la création d'une commande :\nOrderService.createOrder() → publie OrderCreated\n→ InventoryService réserve le stock → publie InventoryReserved\n→ PaymentService charge le paiement → publie PaymentProcessed\n→ OrderService confirme la commande\n\nEn cas d'échec du paiement :\n→ PaymentService publie PaymentFailed\n→ InventoryService libère la réservation\n→ OrderService annule la commande\n",[189],{"type":30,"tag":98,"props":190,"children":191},{"__ignoreMap":8},[192],{"type":36,"value":187},{"type":30,"tag":38,"props":194,"children":195},{},[196,198,204],{"type":36,"value":197},"Chaque étape peut échouer, les messages peuvent être perdus ou dupliqués, et les états intermédiaires sont difficiles à debugger. Pour renforcer la robustesse de ces flux, les ",{"type":30,"tag":142,"props":199,"children":201},{"href":200},"/fr/architecture-craft/patterns-resilience-circuit-breaker-retry",[202],{"type":36,"value":203},"patterns de résilience : circuit breaker, retry, timeout",{"type":36,"value":205}," sont indispensables dès lors que les services communiquent de manière asynchrone.",{"type":30,"tag":38,"props":207,"children":208},{},[209,214,216,222],{"type":30,"tag":44,"props":210,"children":211},{},[212],{"type":36,"value":213},"Requêtes cross-services :",{"type":36,"value":215}," une requête qui joint des données de plusieurs services (liste des commandes avec le nom du client et le stock disponible) devient une API composition : plusieurs appels HTTP en séquence ou en parallèle, avec de la logique d'agrégation en mémoire. Cette communication asynchrone entre services introduit un ",{"type":30,"tag":142,"props":217,"children":219},{"href":218},"/fr/architecture-craft/couplage-temporel-code-asynchrone",[220],{"type":36,"value":221},"couplage temporel",{"type":36,"value":223}," qu'il faut gérer explicitement, notamment avec des garanties d'idempotence et d'ordre sur les événements.",{"type":30,"tag":225,"props":226,"children":231},"cta",{"cta":227,"href":228,"title":229,"type":230},"Réserver mon diagnostic gratuit →","https://app.kamanga.fr/forms/discovery-call","Vous migrez vers les microservices et vous hésitez sur la stratégie de base de données ?","call",[232],{"type":30,"tag":38,"props":233,"children":234},{},[235],{"type":36,"value":236},"Choisir la bonne stratégie de base de données pour une architecture distribuée dépend de nombreux facteurs contextuels que j'ai appris à évaluer sur le terrain, dans la finance, les médias, la logistique. En 30 minutes, on peut évaluer les trade-offs et définir l'approche adaptée à votre situation réelle.",{"type":30,"tag":67,"props":238,"children":239},{},[],{"type":30,"tag":71,"props":241,"children":243},{"id":242},"le-cadre-de-décision-objectif",[244],{"type":36,"value":245},"Le cadre de décision objectif",{"type":30,"tag":38,"props":247,"children":248},{},[249],{"type":36,"value":250},"La question n'est pas \"devrions-nous faire Database per Service ?\", c'est \"quel niveau de couplage de base de données est acceptable dans notre contexte ?\"",{"type":30,"tag":252,"props":253,"children":254},"table",{},[255,279],{"type":30,"tag":256,"props":257,"children":258},"thead",{},[259],{"type":30,"tag":260,"props":261,"children":262},"tr",{},[263,269,274],{"type":30,"tag":264,"props":265,"children":266},"th",{},[267],{"type":36,"value":268},"Critère",{"type":30,"tag":264,"props":270,"children":271},{},[272],{"type":36,"value":273},"Favorise Database per Service",{"type":30,"tag":264,"props":275,"children":276},{},[277],{"type":36,"value":278},"Favorise Base partagée",{"type":30,"tag":280,"props":281,"children":282},"tbody",{},[283,302,320,338,356,374],{"type":30,"tag":260,"props":284,"children":285},{},[286,292,297],{"type":30,"tag":287,"props":288,"children":289},"td",{},[290],{"type":36,"value":291},"Taille de l'équipe",{"type":30,"tag":287,"props":293,"children":294},{},[295],{"type":36,"value":296},"> 15 développeurs, équipes indépendantes",{"type":30,"tag":287,"props":298,"children":299},{},[300],{"type":36,"value":301},"\u003C 15 développeurs, une seule équipe",{"type":30,"tag":260,"props":303,"children":304},{},[305,310,315],{"type":30,"tag":287,"props":306,"children":307},{},[308],{"type":36,"value":309},"Fréquence de changement de schéma",{"type":30,"tag":287,"props":311,"children":312},{},[313],{"type":36,"value":314},"Fréquente (plusieurs fois par semaine)",{"type":30,"tag":287,"props":316,"children":317},{},[318],{"type":36,"value":319},"Rare (quelques fois par mois)",{"type":30,"tag":260,"props":321,"children":322},{},[323,328,333],{"type":30,"tag":287,"props":324,"children":325},{},[326],{"type":36,"value":327},"Exigences de performance",{"type":30,"tag":287,"props":329,"children":330},{},[331],{"type":36,"value":332},"Services avec des profils d'usage très différents",{"type":30,"tag":287,"props":334,"children":335},{},[336],{"type":36,"value":337},"Profils d'usage similaires",{"type":30,"tag":260,"props":339,"children":340},{},[341,346,351],{"type":30,"tag":287,"props":342,"children":343},{},[344],{"type":36,"value":345},"Exigences de disponibilité",{"type":30,"tag":287,"props":347,"children":348},{},[349],{"type":36,"value":350},"SLAs différents par service",{"type":30,"tag":287,"props":352,"children":353},{},[354],{"type":36,"value":355},"SLA uniforme",{"type":30,"tag":260,"props":357,"children":358},{},[359,364,369],{"type":30,"tag":287,"props":360,"children":361},{},[362],{"type":36,"value":363},"Transactions cross-services",{"type":30,"tag":287,"props":365,"children":366},{},[367],{"type":36,"value":368},"Rares",{"type":30,"tag":287,"props":370,"children":371},{},[372],{"type":36,"value":373},"Fréquentes et critiques",{"type":30,"tag":260,"props":375,"children":376},{},[377,382,387],{"type":30,"tag":287,"props":378,"children":379},{},[380],{"type":36,"value":381},"Maturité opérationnelle",{"type":30,"tag":287,"props":383,"children":384},{},[385],{"type":36,"value":386},"SRE dédié, infrastructure mature",{"type":30,"tag":287,"props":388,"children":389},{},[390],{"type":36,"value":391},"Pas d'équipe infra dédiée",{"type":30,"tag":38,"props":393,"children":394},{},[395,400],{"type":30,"tag":44,"props":396,"children":397},{},[398],{"type":36,"value":399},"La règle empirique :",{"type":36,"value":401}," si les équipes qui développent les services peuvent déployer indépendamment et que les transactions cross-services sont rares, Database per Service a du sens. Sinon, le coût dépasse la valeur.",{"type":30,"tag":67,"props":403,"children":404},{},[],{"type":30,"tag":71,"props":406,"children":408},{"id":407},"les-alternatives-ignorées",[409],{"type":36,"value":410},"Les alternatives ignorées",{"type":30,"tag":38,"props":412,"children":413},{},[414],{"type":36,"value":415},"Avant de sauter à \"une base de données par service\", il existe des options intermédiaires qui réduisent le couplage sans la complexité complète.",{"type":30,"tag":38,"props":417,"children":418},{},[419],{"type":30,"tag":44,"props":420,"children":421},{},[422],{"type":36,"value":423},"Option 1 : Schema per Service (dans la même base de données)",{"type":30,"tag":38,"props":425,"children":426},{},[427],{"type":36,"value":428},"Chaque service possède son schéma (namespace) dans une base de données partagée. Le service A ne peut accéder qu'aux tables du schéma A.",{"type":30,"tag":185,"props":430,"children":434},{"code":431,"language":432,"meta":8,"className":433,"style":8},"-- Service A n'accède qu'à son schéma\nSET search_path TO ordering;\nSELECT * FROM orders;  -- → ordering.orders\n\n-- Service B n'accède qu'à son schéma\nSET search_path TO inventory;\nSELECT * FROM products;  -- → inventory.products\n","sql","language-sql shiki shiki-themes catppuccin-frappe github-dark",[435],{"type":30,"tag":98,"props":436,"children":437},{"__ignoreMap":8},[438,450,476,506,515,524,545],{"type":30,"tag":439,"props":440,"children":443},"span",{"class":441,"line":442},"line",1,[444],{"type":30,"tag":439,"props":445,"children":447},{"style":446},"--shiki-default:#737994;--shiki-default-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit",[448],{"type":36,"value":449},"-- Service A n'accède qu'à son schéma\n",{"type":30,"tag":439,"props":451,"children":453},{"class":441,"line":452},2,[454,460,466,471],{"type":30,"tag":439,"props":455,"children":457},{"style":456},"--shiki-default:#CA9EE6;--shiki-dark:#F97583",[458],{"type":36,"value":459},"SET",{"type":30,"tag":439,"props":461,"children":463},{"style":462},"--shiki-default:#C6D0F5;--shiki-dark:#E1E4E8",[464],{"type":36,"value":465}," search_path ",{"type":30,"tag":439,"props":467,"children":468},{"style":456},[469],{"type":36,"value":470},"TO",{"type":30,"tag":439,"props":472,"children":473},{"style":462},[474],{"type":36,"value":475}," ordering;\n",{"type":30,"tag":439,"props":477,"children":479},{"class":441,"line":478},3,[480,485,491,496,501],{"type":30,"tag":439,"props":481,"children":482},{"style":456},[483],{"type":36,"value":484},"SELECT",{"type":30,"tag":439,"props":486,"children":488},{"style":487},"--shiki-default:#81C8BE;--shiki-dark:#F97583",[489],{"type":36,"value":490}," *",{"type":30,"tag":439,"props":492,"children":493},{"style":456},[494],{"type":36,"value":495}," FROM",{"type":30,"tag":439,"props":497,"children":498},{"style":462},[499],{"type":36,"value":500}," orders;  ",{"type":30,"tag":439,"props":502,"children":503},{"style":446},[504],{"type":36,"value":505},"-- → ordering.orders\n",{"type":30,"tag":439,"props":507,"children":509},{"class":441,"line":508},4,[510],{"type":30,"tag":439,"props":511,"children":512},{"emptyLinePlaceholder":13},[513],{"type":36,"value":514},"\n",{"type":30,"tag":439,"props":516,"children":518},{"class":441,"line":517},5,[519],{"type":30,"tag":439,"props":520,"children":521},{"style":446},[522],{"type":36,"value":523},"-- Service B n'accède qu'à son schéma\n",{"type":30,"tag":439,"props":525,"children":527},{"class":441,"line":526},6,[528,532,536,540],{"type":30,"tag":439,"props":529,"children":530},{"style":456},[531],{"type":36,"value":459},{"type":30,"tag":439,"props":533,"children":534},{"style":462},[535],{"type":36,"value":465},{"type":30,"tag":439,"props":537,"children":538},{"style":456},[539],{"type":36,"value":470},{"type":30,"tag":439,"props":541,"children":542},{"style":462},[543],{"type":36,"value":544}," inventory;\n",{"type":30,"tag":439,"props":546,"children":548},{"class":441,"line":547},7,[549,553,557,561,566],{"type":30,"tag":439,"props":550,"children":551},{"style":456},[552],{"type":36,"value":484},{"type":30,"tag":439,"props":554,"children":555},{"style":487},[556],{"type":36,"value":490},{"type":30,"tag":439,"props":558,"children":559},{"style":456},[560],{"type":36,"value":495},{"type":30,"tag":439,"props":562,"children":563},{"style":462},[564],{"type":36,"value":565}," products;  ",{"type":30,"tag":439,"props":567,"children":568},{"style":446},[569],{"type":36,"value":570},"-- → inventory.products\n",{"type":30,"tag":38,"props":572,"children":573},{},[574],{"type":36,"value":575},"Avantages : les transactions ACID restent possibles si nécessaire. Complexité opérationnelle minimale (une seule base de données).",{"type":30,"tag":38,"props":577,"children":578},{},[579],{"type":30,"tag":44,"props":580,"children":581},{},[582],{"type":36,"value":583},"Option 2 : Read replicas dédiées par service",{"type":30,"tag":38,"props":585,"children":586},{},[587],{"type":36,"value":588},"La base de données principale est partagée pour les écritures, mais chaque service lit depuis sa propre replica read-only synchronisée. Utile quand le problème principal est la contention de lecture.",{"type":30,"tag":38,"props":590,"children":591},{},[592],{"type":30,"tag":44,"props":593,"children":594},{},[595],{"type":36,"value":596},"Option 3 : CQRS sans séparation de base",{"type":30,"tag":38,"props":598,"children":599},{},[600],{"type":36,"value":601},"Séparer les modèles de lecture et d'écriture sans nécessairement avoir des bases de données séparées. Les requêtes complexes lisent des vues matérialisées maintenues à jour par des événements.",{"type":30,"tag":67,"props":603,"children":604},{},[],{"type":30,"tag":71,"props":606,"children":608},{"id":607},"quand-migrer-vers-database-per-service",[609],{"type":36,"value":610},"Quand migrer vers Database per Service",{"type":30,"tag":38,"props":612,"children":613},{},[614],{"type":30,"tag":44,"props":615,"children":616},{},[617],{"type":36,"value":618},"Les signaux que la base partagée devient un problème :",{"type":30,"tag":620,"props":621,"children":622},"ul",{},[623,629,634,639],{"type":30,"tag":624,"props":625,"children":626},"li",{},[627],{"type":36,"value":628},"Les migrations de base de données nécessitent de coordonner 3 équipes ou plus",{"type":30,"tag":624,"props":630,"children":631},{},[632],{"type":36,"value":633},"Un service lent dégrade régulièrement les performances des autres",{"type":30,"tag":624,"props":635,"children":636},{},[637],{"type":36,"value":638},"Deux services ont des besoins contradictoires sur le schéma d'une table partagée",{"type":30,"tag":624,"props":640,"children":641},{},[642],{"type":36,"value":643},"L'équipe veut utiliser un moteur de base de données différent pour un service spécifique",{"type":30,"tag":38,"props":645,"children":646},{},[647,652],{"type":30,"tag":44,"props":648,"children":649},{},[650],{"type":36,"value":651},"La migration progressive :",{"type":36,"value":653}," commencer par les tables du service le plus indépendant. Migrer la table, mettre en place la synchronisation des données si nécessaire (CDC avec Debezium, événements de domaine), valider, puis passer à la table suivante.",{"type":30,"tag":185,"props":655,"children":657},{"code":656},"Plan de migration en 4 phases :\nPhase 1 : service de notifications (aucune dépendance cross-service)\nPhase 2 : service de recherche (lecture seule, pas d'écriture cross-service)\nPhase 3 : service de catalogue produit (écriture rarement liée aux autres)\nPhase 4 : service de commandes (transactions complexes — laisser pour dernier)\n",[658],{"type":30,"tag":98,"props":659,"children":660},{"__ignoreMap":8},[661],{"type":36,"value":656},{"type":30,"tag":67,"props":663,"children":664},{},[],{"type":30,"tag":71,"props":666,"children":668},{"id":667},"le-cas-particulier-du-polyglot-persistence",[669],{"type":36,"value":670},"Le cas particulier du polyglot persistence",{"type":30,"tag":38,"props":672,"children":673},{},[674],{"type":36,"value":675},"Database per Service devient moins coûteux quand les services utilisent des bases de données managées (DynamoDB, MongoDB Atlas, Redis Cloud) avec une facturation à l'usage. Le coût opérationnel est absorbé par le fournisseur.",{"type":30,"tag":38,"props":677,"children":678},{},[679],{"type":36,"value":680},"Le pattern polyglot persistence (chaque service utilise le moteur adapté à ses besoins) ne vaut sa complexité que si les besoins sont vraiment distincts et que l'équipe a la maturité opérationnelle pour gérer plusieurs moteurs.",{"type":30,"tag":620,"props":682,"children":683},{},[684,689,694,699],{"type":30,"tag":624,"props":685,"children":686},{},[687],{"type":36,"value":688},"Service de recherche → Elasticsearch",{"type":30,"tag":624,"props":690,"children":691},{},[692],{"type":36,"value":693},"Service de sessions → Redis",{"type":30,"tag":624,"props":695,"children":696},{},[697],{"type":36,"value":698},"Service de catalogue → MongoDB (documents flexibles)",{"type":30,"tag":624,"props":700,"children":701},{},[702],{"type":36,"value":703},"Service de commandes → PostgreSQL (transactions ACID)",{"type":30,"tag":67,"props":705,"children":706},{},[],{"type":30,"tag":71,"props":708,"children":710},{"id":709},"faq-sur-database-per-service",[711],{"type":36,"value":712},"FAQ sur Database per Service",{"type":30,"tag":714,"props":715,"children":716},"details",{},[717,723],{"type":30,"tag":718,"props":719,"children":720},"summary",{},[721],{"type":36,"value":722},"1. Comment gérer la cohérence des données entre services sans transactions distribuées ?",{"type":30,"tag":38,"props":724,"children":725},{},[726],{"type":36,"value":727},"Eventual consistency avec compensation. Accepter que les données ne soient pas instantanément cohérentes entre services, seulement éventuellement cohérentes. Pour les cas où une cohérence forte est nécessaire, utiliser le pattern Outbox : écrire l'événement dans la même transaction que la donnée, puis publier l'événement de façon asynchrone. Pour les cas d'échec, implémenter des compensating transactions explicites plutôt que des rollbacks automatiques.",{"type":30,"tag":714,"props":729,"children":730},{},[731,736],{"type":30,"tag":718,"props":732,"children":733},{},[734],{"type":36,"value":735},"2. Peut-on utiliser des transactions distribuées (2PC) à la place des Sagas ?",{"type":30,"tag":38,"props":737,"children":738},{},[739],{"type":36,"value":740},"Techniquement oui, mais déconseillé. Le Two-Phase Commit est lent (lock pendant la phase de préparation), fragile (coordinator failure = système bloqué), et complexe à implémenter correctement. Les Sagas sont plus complexes à concevoir mais plus robustes en production. La recommandation de Sam Newman et de l'industrie : éviter 2PC, préférer les Sagas ou revoir l'architecture pour réduire les transactions cross-services.",{"type":30,"tag":714,"props":742,"children":743},{},[744,749],{"type":30,"tag":718,"props":745,"children":746},{},[747],{"type":36,"value":748},"3. Comment gérer les reportings qui nécessitent des données de plusieurs services ?",{"type":30,"tag":38,"props":750,"children":751},{},[752],{"type":36,"value":753},"Data warehouse ou OLAP dédié. Chaque service publie ses événements vers un pipeline de données (Kafka → Spark/Flink → Data Warehouse). Les analyses et rapports lisent le data warehouse, pas les bases des services. C'est le principe CQRS à l'échelle de l'architecture : les services gèrent les écritures, le data warehouse gère les lectures analytiques complexes.",{"type":30,"tag":714,"props":755,"children":756},{},[757,762],{"type":30,"tag":718,"props":758,"children":759},{},[760],{"type":36,"value":761},"4. Quel outil utiliser pour la synchronisation de données entre services ?",{"type":30,"tag":38,"props":763,"children":764},{},[765],{"type":36,"value":766},"Change Data Capture (CDC) avec Debezium : il capture les changements PostgreSQL/MySQL en temps réel et les publie sur Kafka. Pour des cas plus simples : événements de domaine publiés sur une queue à chaque changement d'état. L'approche CDC est plus robuste (aucun changement applicatif requis) mais plus complexe à opérer. Les événements de domaine sont plus simples mais nécessitent une discipline applicative.",{"type":30,"tag":714,"props":768,"children":769},{},[770,775],{"type":30,"tag":718,"props":771,"children":772},{},[773],{"type":36,"value":774},"5. Database per Service est-il compatible avec les architectures serverless (Lambda, Cloud Functions) ?",{"type":30,"tag":38,"props":776,"children":777},{},[778],{"type":36,"value":779},"Oui, et c'est souvent plus simple. Les bases de données managées à l'usage (DynamoDB, Firestore, Aurora Serverless) s'adaptent naturellement au modèle serverless : pas de pool de connexions à gérer, facturation à la requête. Le principal défi est la gestion des connexions (Lambda peut créer des milliers de connexions simultanées), résolu par des proxy de connexions comme RDS Proxy pour PostgreSQL/MySQL.",{"type":30,"tag":67,"props":781,"children":782},{},[],{"type":30,"tag":225,"props":784,"children":789},{"cta":785,"href":786,"title":787,"type":788},"Faire mon auto-évaluation →","/ema","Ressource gratuite : Engineering Maturity Self-Assessment","resource",[790],{"type":30,"tag":38,"props":791,"children":792},{},[793],{"type":36,"value":794},"L'Engineering Maturity Self-Assessment couvre le domaine Architecture Distribuée : évaluez votre maturité sur le découpage des services, la gestion des données, et la résilience. Score et plan d'action en 10 minutes.",{"type":30,"tag":796,"props":797,"children":798},"style",{},[799],{"type":36,"value":800},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":452,"depth":452,"links":802},[803,804,805,806,807,808,809],{"id":73,"depth":452,"text":76},{"id":155,"depth":452,"text":158},{"id":242,"depth":452,"text":245},{"id":407,"depth":452,"text":410},{"id":607,"depth":452,"text":610},{"id":667,"depth":452,"text":670},{"id":709,"depth":452,"text":712},"markdown","content:fr:architecture-craft:database-per-service-microservices.md","content","fr/architecture-craft/database-per-service-microservices.md","fr/architecture-craft/database-per-service-microservices","md",{"_path":817,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":818,"description":819,"id":820,"date":821,"listed":13,"nocomments":7,"hidden":7,"categories":822,"tags":823,"--cover":828,"readingTime":829,"body":833,"_type":810,"_id":5652,"_source":812,"_file":5653,"_stem":5654,"_extension":815},"/fr/architecture-craft/dependency-inversion-pratique","Dependency Inversion Principle : 3 exemples concrets","Le DIP est le principe SOLID le plus mal compris. Pas un pattern de conception — une règle sur la direction des dépendances. Trois implémentations dans trois langages.",30,"2026-03-13",[6],[824,825,826,827],"SOLID","Dependency Inversion","Architecture","Clean Code","covers/articles/dependency-inversion-principe.jpg",{"text":22,"minutes":830,"time":831,"words":832},7.155,429300,1431,{"type":27,"children":834,"toc":5643},[835,840,845,850,855,858,864,877,882,1262,1270,1316,1319,1325,1369,1377,1382,1385,1391,2470,2478,2971,2980,2983,2989,4481,4491,4494,4500,5425,5443,5446,5452,5460,5478,5486,5504,5522,5525,5531,5544,5557,5570,5591,5628,5631,5639],{"type":30,"tag":31,"props":836,"children":838},{"id":837},"dependency-inversion-principle-3-exemples-concrets",[839],{"type":36,"value":818},{"type":30,"tag":38,"props":841,"children":842},{},[843],{"type":36,"value":844},"Chez un client dans le secteur du retail en ligne que j'accompagnais (20 développeurs, 8 services backend), la suite de tests prenait 18 minutes à s'exécuter. Pas parce que les tests étaient lents. Parce que chaque test unitaire démarrait une vraie base de données PostgreSQL, un vrai serveur Redis, et appelait le vrai Sendgrid.",{"type":30,"tag":38,"props":846,"children":847},{},[848],{"type":36,"value":849},"Ce n'était pas un problème de tests. C'était un problème d'architecture : les modules métier dépendaient directement des implémentations concrètes d'infrastructure.",{"type":30,"tag":38,"props":851,"children":852},{},[853],{"type":36,"value":854},"Après avoir introduit le Dependency Inversion Principle sur les 8 services les plus critiques, la suite de tests est passée à 3 minutes. La couverture de tests a augmenté de 35% à 72% en 3 mois. Pas parce que les développeurs avaient soudain envie d'écrire des tests, mais parce que les tests étaient devenus faciles à écrire.",{"type":30,"tag":67,"props":856,"children":857},{},[],{"type":30,"tag":71,"props":859,"children":861},{"id":860},"le-problème-la-dépendance-directe",[862],{"type":36,"value":863},"Le problème : la dépendance directe",{"type":30,"tag":38,"props":865,"children":866},{},[867,869,875],{"type":36,"value":868},"Robert C. Martin (Uncle Bob) a formulé le DIP en 1996 dans ses travaux sur les principes SOLID : \"Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux doivent dépendre d'abstractions.\" C'est la règle de dépendance au cœur de la ",{"type":30,"tag":142,"props":870,"children":872},{"href":871},"/fr/architecture-craft/clean-architecture-3-regles",[873],{"type":36,"value":874},"Clean Architecture",{"type":36,"value":876}," : les flèches de dépendance doivent toujours pointer vers l'intérieur, vers le domaine métier.",{"type":30,"tag":38,"props":878,"children":879},{},[880],{"type":36,"value":881},"En pratique, la quasi-totalité des codebases sans discipline architecturale viole ce principe :",{"type":30,"tag":185,"props":883,"children":887},{"code":884,"language":885,"meta":8,"className":886,"style":8},"# Violation du DIP — le module de haut niveau dépend du module de bas niveau\n\nclass OrderService:\n    def __init__(self):\n        self.db = PostgreSQLDatabase(host=\"localhost\", port=5432)  # dépendance directe\n        self.email = SendgridEmailClient(api_key=\"...\")              # dépendance directe\n\n    def create_order(self, order_data):\n        order_id = self.db.save(order_data)\n        self.email.send_confirmation(order_data[\"email\"], order_id)\n        return order_id\n","python","language-python shiki shiki-themes catppuccin-frappe github-dark",[888],{"type":30,"tag":98,"props":889,"children":890},{"__ignoreMap":8},[891,899,906,926,956,1036,1088,1095,1131,1181,1248],{"type":30,"tag":439,"props":892,"children":893},{"class":441,"line":442},[894],{"type":30,"tag":439,"props":895,"children":896},{"style":446},[897],{"type":36,"value":898},"# Violation du DIP — le module de haut niveau dépend du module de bas niveau\n",{"type":30,"tag":439,"props":900,"children":901},{"class":441,"line":452},[902],{"type":30,"tag":439,"props":903,"children":904},{"emptyLinePlaceholder":13},[905],{"type":36,"value":514},{"type":30,"tag":439,"props":907,"children":908},{"class":441,"line":478},[909,914,920],{"type":30,"tag":439,"props":910,"children":911},{"style":456},[912],{"type":36,"value":913},"class",{"type":30,"tag":439,"props":915,"children":917},{"style":916},"--shiki-default:#E5C890;--shiki-default-font-style:italic;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit",[918],{"type":36,"value":919}," OrderService",{"type":30,"tag":439,"props":921,"children":923},{"style":922},"--shiki-default:#949CBB;--shiki-dark:#E1E4E8",[924],{"type":36,"value":925},":\n",{"type":30,"tag":439,"props":927,"children":928},{"class":441,"line":508},[929,934,940,945,951],{"type":30,"tag":439,"props":930,"children":931},{"style":456},[932],{"type":36,"value":933},"    def",{"type":30,"tag":439,"props":935,"children":937},{"style":936},"--shiki-default:#99D1DB;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[938],{"type":36,"value":939}," __init__",{"type":30,"tag":439,"props":941,"children":942},{"style":922},[943],{"type":36,"value":944},"(",{"type":30,"tag":439,"props":946,"children":948},{"style":947},"--shiki-default:#E78284;--shiki-default-font-style:italic;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit",[949],{"type":36,"value":950},"self",{"type":30,"tag":439,"props":952,"children":953},{"style":922},[954],{"type":36,"value":955},"):\n",{"type":30,"tag":439,"props":957,"children":958},{"class":441,"line":517},[959,965,970,975,980,986,990,996,1000,1006,1011,1016,1020,1026,1031],{"type":30,"tag":439,"props":960,"children":962},{"style":961},"--shiki-default:#E78284;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[963],{"type":36,"value":964},"        self",{"type":30,"tag":439,"props":966,"children":967},{"style":922},[968],{"type":36,"value":969},".",{"type":30,"tag":439,"props":971,"children":972},{"style":462},[973],{"type":36,"value":974},"db ",{"type":30,"tag":439,"props":976,"children":977},{"style":487},[978],{"type":36,"value":979},"=",{"type":30,"tag":439,"props":981,"children":983},{"style":982},"--shiki-default:#8CAAEE;--shiki-dark:#E1E4E8",[984],{"type":36,"value":985}," PostgreSQLDatabase",{"type":30,"tag":439,"props":987,"children":988},{"style":922},[989],{"type":36,"value":944},{"type":30,"tag":439,"props":991,"children":993},{"style":992},"--shiki-default:#EA999C;--shiki-default-font-style:italic;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit",[994],{"type":36,"value":995},"host",{"type":30,"tag":439,"props":997,"children":998},{"style":487},[999],{"type":36,"value":979},{"type":30,"tag":439,"props":1001,"children":1003},{"style":1002},"--shiki-default:#A6D189;--shiki-dark:#9ECBFF",[1004],{"type":36,"value":1005},"\"localhost\"",{"type":30,"tag":439,"props":1007,"children":1008},{"style":922},[1009],{"type":36,"value":1010},",",{"type":30,"tag":439,"props":1012,"children":1013},{"style":992},[1014],{"type":36,"value":1015}," port",{"type":30,"tag":439,"props":1017,"children":1018},{"style":487},[1019],{"type":36,"value":979},{"type":30,"tag":439,"props":1021,"children":1023},{"style":1022},"--shiki-default:#EF9F76;--shiki-dark:#79B8FF",[1024],{"type":36,"value":1025},"5432",{"type":30,"tag":439,"props":1027,"children":1028},{"style":922},[1029],{"type":36,"value":1030},")",{"type":30,"tag":439,"props":1032,"children":1033},{"style":446},[1034],{"type":36,"value":1035},"  # dépendance directe\n",{"type":30,"tag":439,"props":1037,"children":1038},{"class":441,"line":526},[1039,1043,1047,1052,1056,1061,1065,1070,1074,1079,1083],{"type":30,"tag":439,"props":1040,"children":1041},{"style":961},[1042],{"type":36,"value":964},{"type":30,"tag":439,"props":1044,"children":1045},{"style":922},[1046],{"type":36,"value":969},{"type":30,"tag":439,"props":1048,"children":1049},{"style":462},[1050],{"type":36,"value":1051},"email ",{"type":30,"tag":439,"props":1053,"children":1054},{"style":487},[1055],{"type":36,"value":979},{"type":30,"tag":439,"props":1057,"children":1058},{"style":982},[1059],{"type":36,"value":1060}," SendgridEmailClient",{"type":30,"tag":439,"props":1062,"children":1063},{"style":922},[1064],{"type":36,"value":944},{"type":30,"tag":439,"props":1066,"children":1067},{"style":992},[1068],{"type":36,"value":1069},"api_key",{"type":30,"tag":439,"props":1071,"children":1072},{"style":487},[1073],{"type":36,"value":979},{"type":30,"tag":439,"props":1075,"children":1076},{"style":1002},[1077],{"type":36,"value":1078},"\"...\"",{"type":30,"tag":439,"props":1080,"children":1081},{"style":922},[1082],{"type":36,"value":1030},{"type":30,"tag":439,"props":1084,"children":1085},{"style":446},[1086],{"type":36,"value":1087},"              # dépendance directe\n",{"type":30,"tag":439,"props":1089,"children":1090},{"class":441,"line":547},[1091],{"type":30,"tag":439,"props":1092,"children":1093},{"emptyLinePlaceholder":13},[1094],{"type":36,"value":514},{"type":30,"tag":439,"props":1096,"children":1098},{"class":441,"line":1097},8,[1099,1103,1109,1113,1117,1121,1127],{"type":30,"tag":439,"props":1100,"children":1101},{"style":456},[1102],{"type":36,"value":933},{"type":30,"tag":439,"props":1104,"children":1106},{"style":1105},"--shiki-default:#8CAAEE;--shiki-default-font-style:italic;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit",[1107],{"type":36,"value":1108}," create_order",{"type":30,"tag":439,"props":1110,"children":1111},{"style":922},[1112],{"type":36,"value":944},{"type":30,"tag":439,"props":1114,"children":1115},{"style":947},[1116],{"type":36,"value":950},{"type":30,"tag":439,"props":1118,"children":1119},{"style":922},[1120],{"type":36,"value":1010},{"type":30,"tag":439,"props":1122,"children":1124},{"style":1123},"--shiki-default:#EA999C;--shiki-default-font-style:italic;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit",[1125],{"type":36,"value":1126}," order_data",{"type":30,"tag":439,"props":1128,"children":1129},{"style":922},[1130],{"type":36,"value":955},{"type":30,"tag":439,"props":1132,"children":1134},{"class":441,"line":1133},9,[1135,1140,1144,1149,1153,1158,1162,1167,1171,1176],{"type":30,"tag":439,"props":1136,"children":1137},{"style":462},[1138],{"type":36,"value":1139},"        order_id ",{"type":30,"tag":439,"props":1141,"children":1142},{"style":487},[1143],{"type":36,"value":979},{"type":30,"tag":439,"props":1145,"children":1146},{"style":961},[1147],{"type":36,"value":1148}," self",{"type":30,"tag":439,"props":1150,"children":1151},{"style":922},[1152],{"type":36,"value":969},{"type":30,"tag":439,"props":1154,"children":1155},{"style":462},[1156],{"type":36,"value":1157},"db",{"type":30,"tag":439,"props":1159,"children":1160},{"style":922},[1161],{"type":36,"value":969},{"type":30,"tag":439,"props":1163,"children":1164},{"style":982},[1165],{"type":36,"value":1166},"save",{"type":30,"tag":439,"props":1168,"children":1169},{"style":922},[1170],{"type":36,"value":944},{"type":30,"tag":439,"props":1172,"children":1173},{"style":462},[1174],{"type":36,"value":1175},"order_data",{"type":30,"tag":439,"props":1177,"children":1178},{"style":922},[1179],{"type":36,"value":1180},")\n",{"type":30,"tag":439,"props":1182,"children":1184},{"class":441,"line":1183},10,[1185,1189,1193,1198,1202,1207,1211,1215,1220,1225,1230,1234,1239,1244],{"type":30,"tag":439,"props":1186,"children":1187},{"style":961},[1188],{"type":36,"value":964},{"type":30,"tag":439,"props":1190,"children":1191},{"style":922},[1192],{"type":36,"value":969},{"type":30,"tag":439,"props":1194,"children":1195},{"style":462},[1196],{"type":36,"value":1197},"email",{"type":30,"tag":439,"props":1199,"children":1200},{"style":922},[1201],{"type":36,"value":969},{"type":30,"tag":439,"props":1203,"children":1204},{"style":982},[1205],{"type":36,"value":1206},"send_confirmation",{"type":30,"tag":439,"props":1208,"children":1209},{"style":922},[1210],{"type":36,"value":944},{"type":30,"tag":439,"props":1212,"children":1213},{"style":1123},[1214],{"type":36,"value":1175},{"type":30,"tag":439,"props":1216,"children":1217},{"style":922},[1218],{"type":36,"value":1219},"[",{"type":30,"tag":439,"props":1221,"children":1222},{"style":1002},[1223],{"type":36,"value":1224},"\"",{"type":30,"tag":439,"props":1226,"children":1228},{"style":1227},"--shiki-default:#A6D189;--shiki-default-font-style:italic;--shiki-dark:#9ECBFF;--shiki-dark-font-style:inherit",[1229],{"type":36,"value":1197},{"type":30,"tag":439,"props":1231,"children":1232},{"style":1002},[1233],{"type":36,"value":1224},{"type":30,"tag":439,"props":1235,"children":1236},{"style":922},[1237],{"type":36,"value":1238},"],",{"type":30,"tag":439,"props":1240,"children":1241},{"style":462},[1242],{"type":36,"value":1243}," order_id",{"type":30,"tag":439,"props":1245,"children":1246},{"style":922},[1247],{"type":36,"value":1180},{"type":30,"tag":439,"props":1249,"children":1251},{"class":441,"line":1250},11,[1252,1257],{"type":30,"tag":439,"props":1253,"children":1254},{"style":456},[1255],{"type":36,"value":1256},"        return",{"type":30,"tag":439,"props":1258,"children":1259},{"style":462},[1260],{"type":36,"value":1261}," order_id\n",{"type":30,"tag":38,"props":1263,"children":1264},{},[1265],{"type":30,"tag":44,"props":1266,"children":1267},{},[1268],{"type":36,"value":1269},"Ce que ça coûte concrètement :",{"type":30,"tag":620,"props":1271,"children":1272},{},[1273,1286,1296,1306],{"type":30,"tag":624,"props":1274,"children":1275},{},[1276,1278,1284],{"type":36,"value":1277},"Impossible de tester ",{"type":30,"tag":98,"props":1279,"children":1281},{"className":1280},[],[1282],{"type":36,"value":1283},"OrderService",{"type":36,"value":1285}," sans une vraie base de données PostgreSQL et un compte Sendgrid",{"type":30,"tag":624,"props":1287,"children":1288},{},[1289,1291],{"type":36,"value":1290},"Changer de base de données oblige à modifier ",{"type":30,"tag":98,"props":1292,"children":1294},{"className":1293},[],[1295],{"type":36,"value":1283},{"type":30,"tag":624,"props":1297,"children":1298},{},[1299,1301],{"type":36,"value":1300},"Changer d'email provider oblige à modifier ",{"type":30,"tag":98,"props":1302,"children":1304},{"className":1303},[],[1305],{"type":36,"value":1283},{"type":30,"tag":624,"props":1307,"children":1308},{},[1309,1314],{"type":30,"tag":98,"props":1310,"children":1312},{"className":1311},[],[1313],{"type":36,"value":1283},{"type":36,"value":1315}," connaît des détails d'implémentation (host, port, api_key) qui n'ont rien à voir avec la logique métier de commande",{"type":30,"tag":67,"props":1317,"children":1318},{},[],{"type":30,"tag":71,"props":1320,"children":1322},{"id":1321},"le-principe-inverser-la-direction-des-dépendances",[1323],{"type":36,"value":1324},"Le principe : inverser la direction des dépendances",{"type":30,"tag":38,"props":1326,"children":1327},{},[1328,1330,1335,1337,1343,1345,1351,1353,1359,1361,1367],{"type":36,"value":1329},"La solution DIP : ",{"type":30,"tag":98,"props":1331,"children":1333},{"className":1332},[],[1334],{"type":36,"value":1283},{"type":36,"value":1336}," ne dépend pas de ",{"type":30,"tag":98,"props":1338,"children":1340},{"className":1339},[],[1341],{"type":36,"value":1342},"PostgreSQLDatabase",{"type":36,"value":1344}," ni de ",{"type":30,"tag":98,"props":1346,"children":1348},{"className":1347},[],[1349],{"type":36,"value":1350},"SendgridEmailClient",{"type":36,"value":1352},". Il dépend d'abstractions (",{"type":30,"tag":98,"props":1354,"children":1356},{"className":1355},[],[1357],{"type":36,"value":1358},"OrderRepository",{"type":36,"value":1360},", ",{"type":30,"tag":98,"props":1362,"children":1364},{"className":1363},[],[1365],{"type":36,"value":1366},"EmailNotifier",{"type":36,"value":1368},") que les implémentations concrètes respectent.",{"type":30,"tag":185,"props":1370,"children":1372},{"code":1371},"Sans DIP :\nOrderService → PostgreSQLDatabase\nOrderService → SendgridEmailClient\n\nAvec DIP :\nOrderService → OrderRepository (abstraction) ← PostgreSQLOrderRepository (implémentation)\nOrderService → EmailNotifier (abstraction)   ← SendgridEmailNotifier (implémentation)\n",[1373],{"type":30,"tag":98,"props":1374,"children":1375},{"__ignoreMap":8},[1376],{"type":36,"value":1371},{"type":30,"tag":38,"props":1378,"children":1379},{},[1380],{"type":36,"value":1381},"La flèche s'inverse : l'implémentation concrète dépend de l'abstraction, pas l'inverse.",{"type":30,"tag":67,"props":1383,"children":1384},{},[],{"type":30,"tag":71,"props":1386,"children":1388},{"id":1387},"exemple-1-python-injection-par-constructeur",[1389],{"type":36,"value":1390},"Exemple 1 : Python, injection par constructeur",{"type":30,"tag":185,"props":1392,"children":1394},{"code":1393,"language":885,"meta":8,"className":886,"style":8},"from abc import ABC, abstractmethod\n\nclass OrderRepository(ABC):\n    @abstractmethod\n    def save(self, order_data: dict) -> str:\n        pass\n\nclass EmailNotifier(ABC):\n    @abstractmethod\n    def send_confirmation(self, email: str, order_id: str) -> None:\n        pass\n\n# Le module de haut niveau — dépend uniquement des abstractions\nclass OrderService:\n    def __init__(self, repository: OrderRepository, notifier: EmailNotifier):\n        self.repository = repository\n        self.notifier = notifier\n\n    def create_order(self, order_data: dict) -> str:\n        order_id = self.repository.save(order_data)\n        self.notifier.send_confirmation(order_data[\"email\"], order_id)\n        return order_id\n\n# Les implémentations concrètes\nclass PostgreSQLOrderRepository(OrderRepository):\n    def save(self, order_data: dict) -> str:\n        return self.db.execute(\"INSERT INTO orders ...\", order_data)\n\nclass SendgridEmailNotifier(EmailNotifier):\n    def send_confirmation(self, email: str, order_id: str) -> None:\n        self.client.send(to=email, template=\"order_confirmation\", data={\"order_id\": order_id})\n\n# Composition à la racine de l'application\ndef create_order_service():\n    repository = PostgreSQLOrderRepository(db)\n    notifier = SendgridEmailNotifier(api_key=os.getenv(\"SENDGRID_KEY\"))\n    return OrderService(repository, notifier)\n",[1395],{"type":30,"tag":98,"props":1396,"children":1397},{"__ignoreMap":8},[1398,1431,1438,1463,1472,1529,1537,1544,1568,1575,1646,1653,1661,1670,1686,1745,1771,1797,1805,1857,1902,1963,1975,1983,1992,2017,2069,2119,2127,2152,2219,2316,2324,2333,2352,2380,2437],{"type":30,"tag":439,"props":1399,"children":1400},{"class":441,"line":442},[1401,1406,1411,1416,1422,1426],{"type":30,"tag":439,"props":1402,"children":1403},{"style":456},[1404],{"type":36,"value":1405},"from",{"type":30,"tag":439,"props":1407,"children":1408},{"style":462},[1409],{"type":36,"value":1410}," abc ",{"type":30,"tag":439,"props":1412,"children":1413},{"style":456},[1414],{"type":36,"value":1415},"import",{"type":30,"tag":439,"props":1417,"children":1419},{"style":1418},"--shiki-default:#C6D0F5;--shiki-dark:#79B8FF",[1420],{"type":36,"value":1421}," ABC",{"type":30,"tag":439,"props":1423,"children":1424},{"style":922},[1425],{"type":36,"value":1010},{"type":30,"tag":439,"props":1427,"children":1428},{"style":462},[1429],{"type":36,"value":1430}," abstractmethod\n",{"type":30,"tag":439,"props":1432,"children":1433},{"class":441,"line":452},[1434],{"type":30,"tag":439,"props":1435,"children":1436},{"emptyLinePlaceholder":13},[1437],{"type":36,"value":514},{"type":30,"tag":439,"props":1439,"children":1440},{"class":441,"line":478},[1441,1445,1450,1454,1459],{"type":30,"tag":439,"props":1442,"children":1443},{"style":456},[1444],{"type":36,"value":913},{"type":30,"tag":439,"props":1446,"children":1447},{"style":916},[1448],{"type":36,"value":1449}," OrderRepository",{"type":30,"tag":439,"props":1451,"children":1452},{"style":922},[1453],{"type":36,"value":944},{"type":30,"tag":439,"props":1455,"children":1456},{"style":1418},[1457],{"type":36,"value":1458},"ABC",{"type":30,"tag":439,"props":1460,"children":1461},{"style":922},[1462],{"type":36,"value":955},{"type":30,"tag":439,"props":1464,"children":1465},{"class":441,"line":508},[1466],{"type":30,"tag":439,"props":1467,"children":1469},{"style":1468},"--shiki-default:#EF9F76;--shiki-default-font-style:italic;--shiki-dark:#B392F0;--shiki-dark-font-style:inherit",[1470],{"type":36,"value":1471},"    @abstractmethod\n",{"type":30,"tag":439,"props":1473,"children":1474},{"class":441,"line":517},[1475,1479,1484,1488,1492,1496,1500,1505,1511,1515,1520,1525],{"type":30,"tag":439,"props":1476,"children":1477},{"style":456},[1478],{"type":36,"value":933},{"type":30,"tag":439,"props":1480,"children":1481},{"style":1105},[1482],{"type":36,"value":1483}," save",{"type":30,"tag":439,"props":1485,"children":1486},{"style":922},[1487],{"type":36,"value":944},{"type":30,"tag":439,"props":1489,"children":1490},{"style":947},[1491],{"type":36,"value":950},{"type":30,"tag":439,"props":1493,"children":1494},{"style":922},[1495],{"type":36,"value":1010},{"type":30,"tag":439,"props":1497,"children":1498},{"style":1123},[1499],{"type":36,"value":1126},{"type":30,"tag":439,"props":1501,"children":1502},{"style":922},[1503],{"type":36,"value":1504},":",{"type":30,"tag":439,"props":1506,"children":1508},{"style":1507},"--shiki-default:#EF9F76;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[1509],{"type":36,"value":1510}," dict",{"type":30,"tag":439,"props":1512,"children":1513},{"style":922},[1514],{"type":36,"value":1030},{"type":30,"tag":439,"props":1516,"children":1517},{"style":922},[1518],{"type":36,"value":1519}," ->",{"type":30,"tag":439,"props":1521,"children":1522},{"style":1507},[1523],{"type":36,"value":1524}," str",{"type":30,"tag":439,"props":1526,"children":1527},{"style":922},[1528],{"type":36,"value":925},{"type":30,"tag":439,"props":1530,"children":1531},{"class":441,"line":526},[1532],{"type":30,"tag":439,"props":1533,"children":1534},{"style":456},[1535],{"type":36,"value":1536},"        pass\n",{"type":30,"tag":439,"props":1538,"children":1539},{"class":441,"line":547},[1540],{"type":30,"tag":439,"props":1541,"children":1542},{"emptyLinePlaceholder":13},[1543],{"type":36,"value":514},{"type":30,"tag":439,"props":1545,"children":1546},{"class":441,"line":1097},[1547,1551,1556,1560,1564],{"type":30,"tag":439,"props":1548,"children":1549},{"style":456},[1550],{"type":36,"value":913},{"type":30,"tag":439,"props":1552,"children":1553},{"style":916},[1554],{"type":36,"value":1555}," EmailNotifier",{"type":30,"tag":439,"props":1557,"children":1558},{"style":922},[1559],{"type":36,"value":944},{"type":30,"tag":439,"props":1561,"children":1562},{"style":1418},[1563],{"type":36,"value":1458},{"type":30,"tag":439,"props":1565,"children":1566},{"style":922},[1567],{"type":36,"value":955},{"type":30,"tag":439,"props":1569,"children":1570},{"class":441,"line":1133},[1571],{"type":30,"tag":439,"props":1572,"children":1573},{"style":1468},[1574],{"type":36,"value":1471},{"type":30,"tag":439,"props":1576,"children":1577},{"class":441,"line":1183},[1578,1582,1587,1591,1595,1599,1604,1608,1612,1616,1620,1624,1628,1632,1636,1642],{"type":30,"tag":439,"props":1579,"children":1580},{"style":456},[1581],{"type":36,"value":933},{"type":30,"tag":439,"props":1583,"children":1584},{"style":1105},[1585],{"type":36,"value":1586}," send_confirmation",{"type":30,"tag":439,"props":1588,"children":1589},{"style":922},[1590],{"type":36,"value":944},{"type":30,"tag":439,"props":1592,"children":1593},{"style":947},[1594],{"type":36,"value":950},{"type":30,"tag":439,"props":1596,"children":1597},{"style":922},[1598],{"type":36,"value":1010},{"type":30,"tag":439,"props":1600,"children":1601},{"style":1123},[1602],{"type":36,"value":1603}," email",{"type":30,"tag":439,"props":1605,"children":1606},{"style":922},[1607],{"type":36,"value":1504},{"type":30,"tag":439,"props":1609,"children":1610},{"style":1507},[1611],{"type":36,"value":1524},{"type":30,"tag":439,"props":1613,"children":1614},{"style":922},[1615],{"type":36,"value":1010},{"type":30,"tag":439,"props":1617,"children":1618},{"style":1123},[1619],{"type":36,"value":1243},{"type":30,"tag":439,"props":1621,"children":1622},{"style":922},[1623],{"type":36,"value":1504},{"type":30,"tag":439,"props":1625,"children":1626},{"style":1507},[1627],{"type":36,"value":1524},{"type":30,"tag":439,"props":1629,"children":1630},{"style":922},[1631],{"type":36,"value":1030},{"type":30,"tag":439,"props":1633,"children":1634},{"style":922},[1635],{"type":36,"value":1519},{"type":30,"tag":439,"props":1637,"children":1639},{"style":1638},"--shiki-default:#CA9EE6;--shiki-dark:#79B8FF",[1640],{"type":36,"value":1641}," None",{"type":30,"tag":439,"props":1643,"children":1644},{"style":922},[1645],{"type":36,"value":925},{"type":30,"tag":439,"props":1647,"children":1648},{"class":441,"line":1250},[1649],{"type":30,"tag":439,"props":1650,"children":1651},{"style":456},[1652],{"type":36,"value":1536},{"type":30,"tag":439,"props":1654,"children":1656},{"class":441,"line":1655},12,[1657],{"type":30,"tag":439,"props":1658,"children":1659},{"emptyLinePlaceholder":13},[1660],{"type":36,"value":514},{"type":30,"tag":439,"props":1662,"children":1664},{"class":441,"line":1663},13,[1665],{"type":30,"tag":439,"props":1666,"children":1667},{"style":446},[1668],{"type":36,"value":1669},"# Le module de haut niveau — dépend uniquement des abstractions\n",{"type":30,"tag":439,"props":1671,"children":1673},{"class":441,"line":1672},14,[1674,1678,1682],{"type":30,"tag":439,"props":1675,"children":1676},{"style":456},[1677],{"type":36,"value":913},{"type":30,"tag":439,"props":1679,"children":1680},{"style":916},[1681],{"type":36,"value":919},{"type":30,"tag":439,"props":1683,"children":1684},{"style":922},[1685],{"type":36,"value":925},{"type":30,"tag":439,"props":1687,"children":1689},{"class":441,"line":1688},15,[1690,1694,1698,1702,1706,1710,1715,1719,1724,1728,1733,1737,1741],{"type":30,"tag":439,"props":1691,"children":1692},{"style":456},[1693],{"type":36,"value":933},{"type":30,"tag":439,"props":1695,"children":1696},{"style":936},[1697],{"type":36,"value":939},{"type":30,"tag":439,"props":1699,"children":1700},{"style":922},[1701],{"type":36,"value":944},{"type":30,"tag":439,"props":1703,"children":1704},{"style":947},[1705],{"type":36,"value":950},{"type":30,"tag":439,"props":1707,"children":1708},{"style":922},[1709],{"type":36,"value":1010},{"type":30,"tag":439,"props":1711,"children":1712},{"style":1123},[1713],{"type":36,"value":1714}," repository",{"type":30,"tag":439,"props":1716,"children":1717},{"style":922},[1718],{"type":36,"value":1504},{"type":30,"tag":439,"props":1720,"children":1722},{"style":1721},"--shiki-default:#EA999C;--shiki-dark:#E1E4E8",[1723],{"type":36,"value":1449},{"type":30,"tag":439,"props":1725,"children":1726},{"style":922},[1727],{"type":36,"value":1010},{"type":30,"tag":439,"props":1729,"children":1730},{"style":1123},[1731],{"type":36,"value":1732}," notifier",{"type":30,"tag":439,"props":1734,"children":1735},{"style":922},[1736],{"type":36,"value":1504},{"type":30,"tag":439,"props":1738,"children":1739},{"style":1721},[1740],{"type":36,"value":1555},{"type":30,"tag":439,"props":1742,"children":1743},{"style":922},[1744],{"type":36,"value":955},{"type":30,"tag":439,"props":1746,"children":1748},{"class":441,"line":1747},16,[1749,1753,1757,1762,1766],{"type":30,"tag":439,"props":1750,"children":1751},{"style":961},[1752],{"type":36,"value":964},{"type":30,"tag":439,"props":1754,"children":1755},{"style":922},[1756],{"type":36,"value":969},{"type":30,"tag":439,"props":1758,"children":1759},{"style":462},[1760],{"type":36,"value":1761},"repository ",{"type":30,"tag":439,"props":1763,"children":1764},{"style":487},[1765],{"type":36,"value":979},{"type":30,"tag":439,"props":1767,"children":1768},{"style":462},[1769],{"type":36,"value":1770}," repository\n",{"type":30,"tag":439,"props":1772,"children":1774},{"class":441,"line":1773},17,[1775,1779,1783,1788,1792],{"type":30,"tag":439,"props":1776,"children":1777},{"style":961},[1778],{"type":36,"value":964},{"type":30,"tag":439,"props":1780,"children":1781},{"style":922},[1782],{"type":36,"value":969},{"type":30,"tag":439,"props":1784,"children":1785},{"style":462},[1786],{"type":36,"value":1787},"notifier ",{"type":30,"tag":439,"props":1789,"children":1790},{"style":487},[1791],{"type":36,"value":979},{"type":30,"tag":439,"props":1793,"children":1794},{"style":462},[1795],{"type":36,"value":1796}," notifier\n",{"type":30,"tag":439,"props":1798,"children":1800},{"class":441,"line":1799},18,[1801],{"type":30,"tag":439,"props":1802,"children":1803},{"emptyLinePlaceholder":13},[1804],{"type":36,"value":514},{"type":30,"tag":439,"props":1806,"children":1808},{"class":441,"line":1807},19,[1809,1813,1817,1821,1825,1829,1833,1837,1841,1845,1849,1853],{"type":30,"tag":439,"props":1810,"children":1811},{"style":456},[1812],{"type":36,"value":933},{"type":30,"tag":439,"props":1814,"children":1815},{"style":1105},[1816],{"type":36,"value":1108},{"type":30,"tag":439,"props":1818,"children":1819},{"style":922},[1820],{"type":36,"value":944},{"type":30,"tag":439,"props":1822,"children":1823},{"style":947},[1824],{"type":36,"value":950},{"type":30,"tag":439,"props":1826,"children":1827},{"style":922},[1828],{"type":36,"value":1010},{"type":30,"tag":439,"props":1830,"children":1831},{"style":1123},[1832],{"type":36,"value":1126},{"type":30,"tag":439,"props":1834,"children":1835},{"style":922},[1836],{"type":36,"value":1504},{"type":30,"tag":439,"props":1838,"children":1839},{"style":1507},[1840],{"type":36,"value":1510},{"type":30,"tag":439,"props":1842,"children":1843},{"style":922},[1844],{"type":36,"value":1030},{"type":30,"tag":439,"props":1846,"children":1847},{"style":922},[1848],{"type":36,"value":1519},{"type":30,"tag":439,"props":1850,"children":1851},{"style":1507},[1852],{"type":36,"value":1524},{"type":30,"tag":439,"props":1854,"children":1855},{"style":922},[1856],{"type":36,"value":925},{"type":30,"tag":439,"props":1858,"children":1860},{"class":441,"line":1859},20,[1861,1865,1869,1873,1877,1882,1886,1890,1894,1898],{"type":30,"tag":439,"props":1862,"children":1863},{"style":462},[1864],{"type":36,"value":1139},{"type":30,"tag":439,"props":1866,"children":1867},{"style":487},[1868],{"type":36,"value":979},{"type":30,"tag":439,"props":1870,"children":1871},{"style":961},[1872],{"type":36,"value":1148},{"type":30,"tag":439,"props":1874,"children":1875},{"style":922},[1876],{"type":36,"value":969},{"type":30,"tag":439,"props":1878,"children":1879},{"style":462},[1880],{"type":36,"value":1881},"repository",{"type":30,"tag":439,"props":1883,"children":1884},{"style":922},[1885],{"type":36,"value":969},{"type":30,"tag":439,"props":1887,"children":1888},{"style":982},[1889],{"type":36,"value":1166},{"type":30,"tag":439,"props":1891,"children":1892},{"style":922},[1893],{"type":36,"value":944},{"type":30,"tag":439,"props":1895,"children":1896},{"style":462},[1897],{"type":36,"value":1175},{"type":30,"tag":439,"props":1899,"children":1900},{"style":922},[1901],{"type":36,"value":1180},{"type":30,"tag":439,"props":1903,"children":1905},{"class":441,"line":1904},21,[1906,1910,1914,1919,1923,1927,1931,1935,1939,1943,1947,1951,1955,1959],{"type":30,"tag":439,"props":1907,"children":1908},{"style":961},[1909],{"type":36,"value":964},{"type":30,"tag":439,"props":1911,"children":1912},{"style":922},[1913],{"type":36,"value":969},{"type":30,"tag":439,"props":1915,"children":1916},{"style":462},[1917],{"type":36,"value":1918},"notifier",{"type":30,"tag":439,"props":1920,"children":1921},{"style":922},[1922],{"type":36,"value":969},{"type":30,"tag":439,"props":1924,"children":1925},{"style":982},[1926],{"type":36,"value":1206},{"type":30,"tag":439,"props":1928,"children":1929},{"style":922},[1930],{"type":36,"value":944},{"type":30,"tag":439,"props":1932,"children":1933},{"style":1123},[1934],{"type":36,"value":1175},{"type":30,"tag":439,"props":1936,"children":1937},{"style":922},[1938],{"type":36,"value":1219},{"type":30,"tag":439,"props":1940,"children":1941},{"style":1002},[1942],{"type":36,"value":1224},{"type":30,"tag":439,"props":1944,"children":1945},{"style":1227},[1946],{"type":36,"value":1197},{"type":30,"tag":439,"props":1948,"children":1949},{"style":1002},[1950],{"type":36,"value":1224},{"type":30,"tag":439,"props":1952,"children":1953},{"style":922},[1954],{"type":36,"value":1238},{"type":30,"tag":439,"props":1956,"children":1957},{"style":462},[1958],{"type":36,"value":1243},{"type":30,"tag":439,"props":1960,"children":1961},{"style":922},[1962],{"type":36,"value":1180},{"type":30,"tag":439,"props":1964,"children":1966},{"class":441,"line":1965},22,[1967,1971],{"type":30,"tag":439,"props":1968,"children":1969},{"style":456},[1970],{"type":36,"value":1256},{"type":30,"tag":439,"props":1972,"children":1973},{"style":462},[1974],{"type":36,"value":1261},{"type":30,"tag":439,"props":1976,"children":1978},{"class":441,"line":1977},23,[1979],{"type":30,"tag":439,"props":1980,"children":1981},{"emptyLinePlaceholder":13},[1982],{"type":36,"value":514},{"type":30,"tag":439,"props":1984,"children":1986},{"class":441,"line":1985},24,[1987],{"type":30,"tag":439,"props":1988,"children":1989},{"style":446},[1990],{"type":36,"value":1991},"# Les implémentations concrètes\n",{"type":30,"tag":439,"props":1993,"children":1995},{"class":441,"line":1994},25,[1996,2000,2005,2009,2013],{"type":30,"tag":439,"props":1997,"children":1998},{"style":456},[1999],{"type":36,"value":913},{"type":30,"tag":439,"props":2001,"children":2002},{"style":916},[2003],{"type":36,"value":2004}," PostgreSQLOrderRepository",{"type":30,"tag":439,"props":2006,"children":2007},{"style":922},[2008],{"type":36,"value":944},{"type":30,"tag":439,"props":2010,"children":2011},{"style":916},[2012],{"type":36,"value":1358},{"type":30,"tag":439,"props":2014,"children":2015},{"style":922},[2016],{"type":36,"value":955},{"type":30,"tag":439,"props":2018,"children":2020},{"class":441,"line":2019},26,[2021,2025,2029,2033,2037,2041,2045,2049,2053,2057,2061,2065],{"type":30,"tag":439,"props":2022,"children":2023},{"style":456},[2024],{"type":36,"value":933},{"type":30,"tag":439,"props":2026,"children":2027},{"style":1105},[2028],{"type":36,"value":1483},{"type":30,"tag":439,"props":2030,"children":2031},{"style":922},[2032],{"type":36,"value":944},{"type":30,"tag":439,"props":2034,"children":2035},{"style":947},[2036],{"type":36,"value":950},{"type":30,"tag":439,"props":2038,"children":2039},{"style":922},[2040],{"type":36,"value":1010},{"type":30,"tag":439,"props":2042,"children":2043},{"style":1123},[2044],{"type":36,"value":1126},{"type":30,"tag":439,"props":2046,"children":2047},{"style":922},[2048],{"type":36,"value":1504},{"type":30,"tag":439,"props":2050,"children":2051},{"style":1507},[2052],{"type":36,"value":1510},{"type":30,"tag":439,"props":2054,"children":2055},{"style":922},[2056],{"type":36,"value":1030},{"type":30,"tag":439,"props":2058,"children":2059},{"style":922},[2060],{"type":36,"value":1519},{"type":30,"tag":439,"props":2062,"children":2063},{"style":1507},[2064],{"type":36,"value":1524},{"type":30,"tag":439,"props":2066,"children":2067},{"style":922},[2068],{"type":36,"value":925},{"type":30,"tag":439,"props":2070,"children":2072},{"class":441,"line":2071},27,[2073,2077,2081,2085,2089,2093,2098,2102,2107,2111,2115],{"type":30,"tag":439,"props":2074,"children":2075},{"style":456},[2076],{"type":36,"value":1256},{"type":30,"tag":439,"props":2078,"children":2079},{"style":961},[2080],{"type":36,"value":1148},{"type":30,"tag":439,"props":2082,"children":2083},{"style":922},[2084],{"type":36,"value":969},{"type":30,"tag":439,"props":2086,"children":2087},{"style":462},[2088],{"type":36,"value":1157},{"type":30,"tag":439,"props":2090,"children":2091},{"style":922},[2092],{"type":36,"value":969},{"type":30,"tag":439,"props":2094,"children":2095},{"style":982},[2096],{"type":36,"value":2097},"execute",{"type":30,"tag":439,"props":2099,"children":2100},{"style":922},[2101],{"type":36,"value":944},{"type":30,"tag":439,"props":2103,"children":2104},{"style":1002},[2105],{"type":36,"value":2106},"\"INSERT INTO orders ...\"",{"type":30,"tag":439,"props":2108,"children":2109},{"style":922},[2110],{"type":36,"value":1010},{"type":30,"tag":439,"props":2112,"children":2113},{"style":462},[2114],{"type":36,"value":1126},{"type":30,"tag":439,"props":2116,"children":2117},{"style":922},[2118],{"type":36,"value":1180},{"type":30,"tag":439,"props":2120,"children":2122},{"class":441,"line":2121},28,[2123],{"type":30,"tag":439,"props":2124,"children":2125},{"emptyLinePlaceholder":13},[2126],{"type":36,"value":514},{"type":30,"tag":439,"props":2128,"children":2130},{"class":441,"line":2129},29,[2131,2135,2140,2144,2148],{"type":30,"tag":439,"props":2132,"children":2133},{"style":456},[2134],{"type":36,"value":913},{"type":30,"tag":439,"props":2136,"children":2137},{"style":916},[2138],{"type":36,"value":2139}," SendgridEmailNotifier",{"type":30,"tag":439,"props":2141,"children":2142},{"style":922},[2143],{"type":36,"value":944},{"type":30,"tag":439,"props":2145,"children":2146},{"style":916},[2147],{"type":36,"value":1366},{"type":30,"tag":439,"props":2149,"children":2150},{"style":922},[2151],{"type":36,"value":955},{"type":30,"tag":439,"props":2153,"children":2154},{"class":441,"line":820},[2155,2159,2163,2167,2171,2175,2179,2183,2187,2191,2195,2199,2203,2207,2211,2215],{"type":30,"tag":439,"props":2156,"children":2157},{"style":456},[2158],{"type":36,"value":933},{"type":30,"tag":439,"props":2160,"children":2161},{"style":1105},[2162],{"type":36,"value":1586},{"type":30,"tag":439,"props":2164,"children":2165},{"style":922},[2166],{"type":36,"value":944},{"type":30,"tag":439,"props":2168,"children":2169},{"style":947},[2170],{"type":36,"value":950},{"type":30,"tag":439,"props":2172,"children":2173},{"style":922},[2174],{"type":36,"value":1010},{"type":30,"tag":439,"props":2176,"children":2177},{"style":1123},[2178],{"type":36,"value":1603},{"type":30,"tag":439,"props":2180,"children":2181},{"style":922},[2182],{"type":36,"value":1504},{"type":30,"tag":439,"props":2184,"children":2185},{"style":1507},[2186],{"type":36,"value":1524},{"type":30,"tag":439,"props":2188,"children":2189},{"style":922},[2190],{"type":36,"value":1010},{"type":30,"tag":439,"props":2192,"children":2193},{"style":1123},[2194],{"type":36,"value":1243},{"type":30,"tag":439,"props":2196,"children":2197},{"style":922},[2198],{"type":36,"value":1504},{"type":30,"tag":439,"props":2200,"children":2201},{"style":1507},[2202],{"type":36,"value":1524},{"type":30,"tag":439,"props":2204,"children":2205},{"style":922},[2206],{"type":36,"value":1030},{"type":30,"tag":439,"props":2208,"children":2209},{"style":922},[2210],{"type":36,"value":1519},{"type":30,"tag":439,"props":2212,"children":2213},{"style":1638},[2214],{"type":36,"value":1641},{"type":30,"tag":439,"props":2216,"children":2217},{"style":922},[2218],{"type":36,"value":925},{"type":30,"tag":439,"props":2220,"children":2222},{"class":441,"line":2221},31,[2223,2227,2231,2236,2240,2245,2249,2254,2258,2262,2266,2271,2275,2280,2284,2289,2293,2298,2303,2307,2311],{"type":30,"tag":439,"props":2224,"children":2225},{"style":961},[2226],{"type":36,"value":964},{"type":30,"tag":439,"props":2228,"children":2229},{"style":922},[2230],{"type":36,"value":969},{"type":30,"tag":439,"props":2232,"children":2233},{"style":462},[2234],{"type":36,"value":2235},"client",{"type":30,"tag":439,"props":2237,"children":2238},{"style":922},[2239],{"type":36,"value":969},{"type":30,"tag":439,"props":2241,"children":2242},{"style":982},[2243],{"type":36,"value":2244},"send",{"type":30,"tag":439,"props":2246,"children":2247},{"style":922},[2248],{"type":36,"value":944},{"type":30,"tag":439,"props":2250,"children":2251},{"style":992},[2252],{"type":36,"value":2253},"to",{"type":30,"tag":439,"props":2255,"children":2256},{"style":487},[2257],{"type":36,"value":979},{"type":30,"tag":439,"props":2259,"children":2260},{"style":462},[2261],{"type":36,"value":1197},{"type":30,"tag":439,"props":2263,"children":2264},{"style":922},[2265],{"type":36,"value":1010},{"type":30,"tag":439,"props":2267,"children":2268},{"style":992},[2269],{"type":36,"value":2270}," template",{"type":30,"tag":439,"props":2272,"children":2273},{"style":487},[2274],{"type":36,"value":979},{"type":30,"tag":439,"props":2276,"children":2277},{"style":1002},[2278],{"type":36,"value":2279},"\"order_confirmation\"",{"type":30,"tag":439,"props":2281,"children":2282},{"style":922},[2283],{"type":36,"value":1010},{"type":30,"tag":439,"props":2285,"children":2286},{"style":992},[2287],{"type":36,"value":2288}," data",{"type":30,"tag":439,"props":2290,"children":2291},{"style":487},[2292],{"type":36,"value":979},{"type":30,"tag":439,"props":2294,"children":2295},{"style":922},[2296],{"type":36,"value":2297},"{",{"type":30,"tag":439,"props":2299,"children":2300},{"style":1002},[2301],{"type":36,"value":2302},"\"order_id\"",{"type":30,"tag":439,"props":2304,"children":2305},{"style":922},[2306],{"type":36,"value":1504},{"type":30,"tag":439,"props":2308,"children":2309},{"style":462},[2310],{"type":36,"value":1243},{"type":30,"tag":439,"props":2312,"children":2313},{"style":922},[2314],{"type":36,"value":2315},"})\n",{"type":30,"tag":439,"props":2317,"children":2319},{"class":441,"line":2318},32,[2320],{"type":30,"tag":439,"props":2321,"children":2322},{"emptyLinePlaceholder":13},[2323],{"type":36,"value":514},{"type":30,"tag":439,"props":2325,"children":2327},{"class":441,"line":2326},33,[2328],{"type":30,"tag":439,"props":2329,"children":2330},{"style":446},[2331],{"type":36,"value":2332},"# Composition à la racine de l'application\n",{"type":30,"tag":439,"props":2334,"children":2336},{"class":441,"line":2335},34,[2337,2342,2347],{"type":30,"tag":439,"props":2338,"children":2339},{"style":456},[2340],{"type":36,"value":2341},"def",{"type":30,"tag":439,"props":2343,"children":2344},{"style":1105},[2345],{"type":36,"value":2346}," create_order_service",{"type":30,"tag":439,"props":2348,"children":2349},{"style":922},[2350],{"type":36,"value":2351},"():\n",{"type":30,"tag":439,"props":2353,"children":2354},{"class":441,"line":11},[2355,2360,2364,2368,2372,2376],{"type":30,"tag":439,"props":2356,"children":2357},{"style":462},[2358],{"type":36,"value":2359},"    repository ",{"type":30,"tag":439,"props":2361,"children":2362},{"style":487},[2363],{"type":36,"value":979},{"type":30,"tag":439,"props":2365,"children":2366},{"style":982},[2367],{"type":36,"value":2004},{"type":30,"tag":439,"props":2369,"children":2370},{"style":922},[2371],{"type":36,"value":944},{"type":30,"tag":439,"props":2373,"children":2374},{"style":462},[2375],{"type":36,"value":1157},{"type":30,"tag":439,"props":2377,"children":2378},{"style":922},[2379],{"type":36,"value":1180},{"type":30,"tag":439,"props":2381,"children":2383},{"class":441,"line":2382},36,[2384,2389,2393,2397,2401,2405,2409,2414,2418,2423,2427,2432],{"type":30,"tag":439,"props":2385,"children":2386},{"style":462},[2387],{"type":36,"value":2388},"    notifier ",{"type":30,"tag":439,"props":2390,"children":2391},{"style":487},[2392],{"type":36,"value":979},{"type":30,"tag":439,"props":2394,"children":2395},{"style":982},[2396],{"type":36,"value":2139},{"type":30,"tag":439,"props":2398,"children":2399},{"style":922},[2400],{"type":36,"value":944},{"type":30,"tag":439,"props":2402,"children":2403},{"style":992},[2404],{"type":36,"value":1069},{"type":30,"tag":439,"props":2406,"children":2407},{"style":487},[2408],{"type":36,"value":979},{"type":30,"tag":439,"props":2410,"children":2411},{"style":462},[2412],{"type":36,"value":2413},"os",{"type":30,"tag":439,"props":2415,"children":2416},{"style":922},[2417],{"type":36,"value":969},{"type":30,"tag":439,"props":2419,"children":2420},{"style":982},[2421],{"type":36,"value":2422},"getenv",{"type":30,"tag":439,"props":2424,"children":2425},{"style":922},[2426],{"type":36,"value":944},{"type":30,"tag":439,"props":2428,"children":2429},{"style":1002},[2430],{"type":36,"value":2431},"\"SENDGRID_KEY\"",{"type":30,"tag":439,"props":2433,"children":2434},{"style":922},[2435],{"type":36,"value":2436},"))\n",{"type":30,"tag":439,"props":2438,"children":2440},{"class":441,"line":2439},37,[2441,2446,2450,2454,2458,2462,2466],{"type":30,"tag":439,"props":2442,"children":2443},{"style":456},[2444],{"type":36,"value":2445},"    return",{"type":30,"tag":439,"props":2447,"children":2448},{"style":982},[2449],{"type":36,"value":919},{"type":30,"tag":439,"props":2451,"children":2452},{"style":922},[2453],{"type":36,"value":944},{"type":30,"tag":439,"props":2455,"children":2456},{"style":462},[2457],{"type":36,"value":1881},{"type":30,"tag":439,"props":2459,"children":2460},{"style":922},[2461],{"type":36,"value":1010},{"type":30,"tag":439,"props":2463,"children":2464},{"style":462},[2465],{"type":36,"value":1732},{"type":30,"tag":439,"props":2467,"children":2468},{"style":922},[2469],{"type":36,"value":1180},{"type":30,"tag":38,"props":2471,"children":2472},{},[2473],{"type":30,"tag":44,"props":2474,"children":2475},{},[2476],{"type":36,"value":2477},"Le gain pour les tests :",{"type":30,"tag":185,"props":2479,"children":2481},{"code":2480,"language":885,"meta":8,"className":886,"style":8},"# Test sans base de données ni Sendgrid — s'exécute en millisecondes\nclass InMemoryOrderRepository(OrderRepository):\n    def __init__(self):\n        self.orders = {}\n\n    def save(self, order_data: dict) -> str:\n        order_id = str(uuid.uuid4())\n        self.orders[order_id] = order_data\n        return order_id\n\ndef test_create_order():\n    repository = InMemoryOrderRepository()\n    notifier = MockEmailNotifier()\n    service = OrderService(repository, notifier)\n\n    order_id = service.create_order({\"email\": \"user@example.com\", \"items\": [...]})\n\n    assert order_id in repository.orders\n    assert len(notifier.sent_confirmations) == 1\n",[2482],{"type":30,"tag":98,"props":2483,"children":2484},{"__ignoreMap":8},[2485,2493,2517,2540,2565,2572,2623,2661,2700,2711,2718,2734,2754,2774,2810,2817,2890,2897,2928],{"type":30,"tag":439,"props":2486,"children":2487},{"class":441,"line":442},[2488],{"type":30,"tag":439,"props":2489,"children":2490},{"style":446},[2491],{"type":36,"value":2492},"# Test sans base de données ni Sendgrid — s'exécute en millisecondes\n",{"type":30,"tag":439,"props":2494,"children":2495},{"class":441,"line":452},[2496,2500,2505,2509,2513],{"type":30,"tag":439,"props":2497,"children":2498},{"style":456},[2499],{"type":36,"value":913},{"type":30,"tag":439,"props":2501,"children":2502},{"style":916},[2503],{"type":36,"value":2504}," InMemoryOrderRepository",{"type":30,"tag":439,"props":2506,"children":2507},{"style":922},[2508],{"type":36,"value":944},{"type":30,"tag":439,"props":2510,"children":2511},{"style":916},[2512],{"type":36,"value":1358},{"type":30,"tag":439,"props":2514,"children":2515},{"style":922},[2516],{"type":36,"value":955},{"type":30,"tag":439,"props":2518,"children":2519},{"class":441,"line":478},[2520,2524,2528,2532,2536],{"type":30,"tag":439,"props":2521,"children":2522},{"style":456},[2523],{"type":36,"value":933},{"type":30,"tag":439,"props":2525,"children":2526},{"style":936},[2527],{"type":36,"value":939},{"type":30,"tag":439,"props":2529,"children":2530},{"style":922},[2531],{"type":36,"value":944},{"type":30,"tag":439,"props":2533,"children":2534},{"style":947},[2535],{"type":36,"value":950},{"type":30,"tag":439,"props":2537,"children":2538},{"style":922},[2539],{"type":36,"value":955},{"type":30,"tag":439,"props":2541,"children":2542},{"class":441,"line":508},[2543,2547,2551,2556,2560],{"type":30,"tag":439,"props":2544,"children":2545},{"style":961},[2546],{"type":36,"value":964},{"type":30,"tag":439,"props":2548,"children":2549},{"style":922},[2550],{"type":36,"value":969},{"type":30,"tag":439,"props":2552,"children":2553},{"style":462},[2554],{"type":36,"value":2555},"orders ",{"type":30,"tag":439,"props":2557,"children":2558},{"style":487},[2559],{"type":36,"value":979},{"type":30,"tag":439,"props":2561,"children":2562},{"style":922},[2563],{"type":36,"value":2564}," {}\n",{"type":30,"tag":439,"props":2566,"children":2567},{"class":441,"line":517},[2568],{"type":30,"tag":439,"props":2569,"children":2570},{"emptyLinePlaceholder":13},[2571],{"type":36,"value":514},{"type":30,"tag":439,"props":2573,"children":2574},{"class":441,"line":526},[2575,2579,2583,2587,2591,2595,2599,2603,2607,2611,2615,2619],{"type":30,"tag":439,"props":2576,"children":2577},{"style":456},[2578],{"type":36,"value":933},{"type":30,"tag":439,"props":2580,"children":2581},{"style":1105},[2582],{"type":36,"value":1483},{"type":30,"tag":439,"props":2584,"children":2585},{"style":922},[2586],{"type":36,"value":944},{"type":30,"tag":439,"props":2588,"children":2589},{"style":947},[2590],{"type":36,"value":950},{"type":30,"tag":439,"props":2592,"children":2593},{"style":922},[2594],{"type":36,"value":1010},{"type":30,"tag":439,"props":2596,"children":2597},{"style":1123},[2598],{"type":36,"value":1126},{"type":30,"tag":439,"props":2600,"children":2601},{"style":922},[2602],{"type":36,"value":1504},{"type":30,"tag":439,"props":2604,"children":2605},{"style":1507},[2606],{"type":36,"value":1510},{"type":30,"tag":439,"props":2608,"children":2609},{"style":922},[2610],{"type":36,"value":1030},{"type":30,"tag":439,"props":2612,"children":2613},{"style":922},[2614],{"type":36,"value":1519},{"type":30,"tag":439,"props":2616,"children":2617},{"style":1507},[2618],{"type":36,"value":1524},{"type":30,"tag":439,"props":2620,"children":2621},{"style":922},[2622],{"type":36,"value":925},{"type":30,"tag":439,"props":2624,"children":2625},{"class":441,"line":547},[2626,2630,2634,2638,2642,2647,2651,2656],{"type":30,"tag":439,"props":2627,"children":2628},{"style":462},[2629],{"type":36,"value":1139},{"type":30,"tag":439,"props":2631,"children":2632},{"style":487},[2633],{"type":36,"value":979},{"type":30,"tag":439,"props":2635,"children":2636},{"style":1507},[2637],{"type":36,"value":1524},{"type":30,"tag":439,"props":2639,"children":2640},{"style":922},[2641],{"type":36,"value":944},{"type":30,"tag":439,"props":2643,"children":2644},{"style":462},[2645],{"type":36,"value":2646},"uuid",{"type":30,"tag":439,"props":2648,"children":2649},{"style":922},[2650],{"type":36,"value":969},{"type":30,"tag":439,"props":2652,"children":2653},{"style":982},[2654],{"type":36,"value":2655},"uuid4",{"type":30,"tag":439,"props":2657,"children":2658},{"style":922},[2659],{"type":36,"value":2660},"())\n",{"type":30,"tag":439,"props":2662,"children":2663},{"class":441,"line":1097},[2664,2668,2672,2676,2680,2685,2690,2695],{"type":30,"tag":439,"props":2665,"children":2666},{"style":961},[2667],{"type":36,"value":964},{"type":30,"tag":439,"props":2669,"children":2670},{"style":922},[2671],{"type":36,"value":969},{"type":30,"tag":439,"props":2673,"children":2674},{"style":1123},[2675],{"type":36,"value":103},{"type":30,"tag":439,"props":2677,"children":2678},{"style":922},[2679],{"type":36,"value":1219},{"type":30,"tag":439,"props":2681,"children":2682},{"style":1123},[2683],{"type":36,"value":2684},"order_id",{"type":30,"tag":439,"props":2686,"children":2687},{"style":922},[2688],{"type":36,"value":2689},"]",{"type":30,"tag":439,"props":2691,"children":2692},{"style":487},[2693],{"type":36,"value":2694}," =",{"type":30,"tag":439,"props":2696,"children":2697},{"style":462},[2698],{"type":36,"value":2699}," order_data\n",{"type":30,"tag":439,"props":2701,"children":2702},{"class":441,"line":1133},[2703,2707],{"type":30,"tag":439,"props":2704,"children":2705},{"style":456},[2706],{"type":36,"value":1256},{"type":30,"tag":439,"props":2708,"children":2709},{"style":462},[2710],{"type":36,"value":1261},{"type":30,"tag":439,"props":2712,"children":2713},{"class":441,"line":1183},[2714],{"type":30,"tag":439,"props":2715,"children":2716},{"emptyLinePlaceholder":13},[2717],{"type":36,"value":514},{"type":30,"tag":439,"props":2719,"children":2720},{"class":441,"line":1250},[2721,2725,2730],{"type":30,"tag":439,"props":2722,"children":2723},{"style":456},[2724],{"type":36,"value":2341},{"type":30,"tag":439,"props":2726,"children":2727},{"style":1105},[2728],{"type":36,"value":2729}," test_create_order",{"type":30,"tag":439,"props":2731,"children":2732},{"style":922},[2733],{"type":36,"value":2351},{"type":30,"tag":439,"props":2735,"children":2736},{"class":441,"line":1655},[2737,2741,2745,2749],{"type":30,"tag":439,"props":2738,"children":2739},{"style":462},[2740],{"type":36,"value":2359},{"type":30,"tag":439,"props":2742,"children":2743},{"style":487},[2744],{"type":36,"value":979},{"type":30,"tag":439,"props":2746,"children":2747},{"style":982},[2748],{"type":36,"value":2504},{"type":30,"tag":439,"props":2750,"children":2751},{"style":922},[2752],{"type":36,"value":2753},"()\n",{"type":30,"tag":439,"props":2755,"children":2756},{"class":441,"line":1663},[2757,2761,2765,2770],{"type":30,"tag":439,"props":2758,"children":2759},{"style":462},[2760],{"type":36,"value":2388},{"type":30,"tag":439,"props":2762,"children":2763},{"style":487},[2764],{"type":36,"value":979},{"type":30,"tag":439,"props":2766,"children":2767},{"style":982},[2768],{"type":36,"value":2769}," MockEmailNotifier",{"type":30,"tag":439,"props":2771,"children":2772},{"style":922},[2773],{"type":36,"value":2753},{"type":30,"tag":439,"props":2775,"children":2776},{"class":441,"line":1672},[2777,2782,2786,2790,2794,2798,2802,2806],{"type":30,"tag":439,"props":2778,"children":2779},{"style":462},[2780],{"type":36,"value":2781},"    service ",{"type":30,"tag":439,"props":2783,"children":2784},{"style":487},[2785],{"type":36,"value":979},{"type":30,"tag":439,"props":2787,"children":2788},{"style":982},[2789],{"type":36,"value":919},{"type":30,"tag":439,"props":2791,"children":2792},{"style":922},[2793],{"type":36,"value":944},{"type":30,"tag":439,"props":2795,"children":2796},{"style":462},[2797],{"type":36,"value":1881},{"type":30,"tag":439,"props":2799,"children":2800},{"style":922},[2801],{"type":36,"value":1010},{"type":30,"tag":439,"props":2803,"children":2804},{"style":462},[2805],{"type":36,"value":1732},{"type":30,"tag":439,"props":2807,"children":2808},{"style":922},[2809],{"type":36,"value":1180},{"type":30,"tag":439,"props":2811,"children":2812},{"class":441,"line":1688},[2813],{"type":30,"tag":439,"props":2814,"children":2815},{"emptyLinePlaceholder":13},[2816],{"type":36,"value":514},{"type":30,"tag":439,"props":2818,"children":2819},{"class":441,"line":1747},[2820,2825,2829,2834,2838,2843,2848,2853,2857,2862,2866,2871,2875,2880,2885],{"type":30,"tag":439,"props":2821,"children":2822},{"style":462},[2823],{"type":36,"value":2824},"    order_id ",{"type":30,"tag":439,"props":2826,"children":2827},{"style":487},[2828],{"type":36,"value":979},{"type":30,"tag":439,"props":2830,"children":2831},{"style":462},[2832],{"type":36,"value":2833}," service",{"type":30,"tag":439,"props":2835,"children":2836},{"style":922},[2837],{"type":36,"value":969},{"type":30,"tag":439,"props":2839,"children":2840},{"style":982},[2841],{"type":36,"value":2842},"create_order",{"type":30,"tag":439,"props":2844,"children":2845},{"style":922},[2846],{"type":36,"value":2847},"({",{"type":30,"tag":439,"props":2849,"children":2850},{"style":1002},[2851],{"type":36,"value":2852},"\"email\"",{"type":30,"tag":439,"props":2854,"children":2855},{"style":922},[2856],{"type":36,"value":1504},{"type":30,"tag":439,"props":2858,"children":2859},{"style":1002},[2860],{"type":36,"value":2861}," \"user@example.com\"",{"type":30,"tag":439,"props":2863,"children":2864},{"style":922},[2865],{"type":36,"value":1010},{"type":30,"tag":439,"props":2867,"children":2868},{"style":1002},[2869],{"type":36,"value":2870}," \"items\"",{"type":30,"tag":439,"props":2872,"children":2873},{"style":922},[2874],{"type":36,"value":1504},{"type":30,"tag":439,"props":2876,"children":2877},{"style":922},[2878],{"type":36,"value":2879}," [",{"type":30,"tag":439,"props":2881,"children":2882},{"style":1418},[2883],{"type":36,"value":2884},"...",{"type":30,"tag":439,"props":2886,"children":2887},{"style":922},[2888],{"type":36,"value":2889},"]})\n",{"type":30,"tag":439,"props":2891,"children":2892},{"class":441,"line":1773},[2893],{"type":30,"tag":439,"props":2894,"children":2895},{"emptyLinePlaceholder":13},[2896],{"type":36,"value":514},{"type":30,"tag":439,"props":2898,"children":2899},{"class":441,"line":1799},[2900,2905,2910,2915,2919,2923],{"type":30,"tag":439,"props":2901,"children":2902},{"style":456},[2903],{"type":36,"value":2904},"    assert",{"type":30,"tag":439,"props":2906,"children":2907},{"style":462},[2908],{"type":36,"value":2909}," order_id ",{"type":30,"tag":439,"props":2911,"children":2912},{"style":456},[2913],{"type":36,"value":2914},"in",{"type":30,"tag":439,"props":2916,"children":2917},{"style":462},[2918],{"type":36,"value":1714},{"type":30,"tag":439,"props":2920,"children":2921},{"style":922},[2922],{"type":36,"value":969},{"type":30,"tag":439,"props":2924,"children":2925},{"style":462},[2926],{"type":36,"value":2927},"orders\n",{"type":30,"tag":439,"props":2929,"children":2930},{"class":441,"line":1807},[2931,2935,2940,2944,2948,2952,2957,2961,2966],{"type":30,"tag":439,"props":2932,"children":2933},{"style":456},[2934],{"type":36,"value":2904},{"type":30,"tag":439,"props":2936,"children":2937},{"style":1507},[2938],{"type":36,"value":2939}," len",{"type":30,"tag":439,"props":2941,"children":2942},{"style":922},[2943],{"type":36,"value":944},{"type":30,"tag":439,"props":2945,"children":2946},{"style":462},[2947],{"type":36,"value":1918},{"type":30,"tag":439,"props":2949,"children":2950},{"style":922},[2951],{"type":36,"value":969},{"type":30,"tag":439,"props":2953,"children":2954},{"style":462},[2955],{"type":36,"value":2956},"sent_confirmations",{"type":30,"tag":439,"props":2958,"children":2959},{"style":922},[2960],{"type":36,"value":1030},{"type":30,"tag":439,"props":2962,"children":2963},{"style":487},[2964],{"type":36,"value":2965}," ==",{"type":30,"tag":439,"props":2967,"children":2968},{"style":1022},[2969],{"type":36,"value":2970}," 1\n",{"type":30,"tag":225,"props":2972,"children":2974},{"cta":227,"href":228,"title":2973,"type":230},"Votre codebase est difficile à tester parce que les dépendances sont fortement couplées ?",[2975],{"type":30,"tag":38,"props":2976,"children":2977},{},[2978],{"type":36,"value":2979},"Introduire le DIP dans un codebase existant nécessite une stratégie progressive, pas un refactoring massif qui bloque toute l'équipe. En 30 minutes, on peut identifier les zones de couplage les plus problématiques et définir un plan de refactoring réaliste avec des gains mesurables.",{"type":30,"tag":67,"props":2981,"children":2982},{},[],{"type":30,"tag":71,"props":2984,"children":2986},{"id":2985},"exemple-2-typescript-interfaces-et-injection-par-framework",[2987],{"type":36,"value":2988},"Exemple 2 : TypeScript, interfaces et injection par framework",{"type":30,"tag":185,"props":2990,"children":2994},{"code":2991,"language":2992,"meta":8,"className":2993,"style":8},"// Les abstractions (interfaces TypeScript)\ninterface OrderRepository {\n    save(orderData: OrderData): Promise\u003Cstring>;\n    findById(orderId: string): Promise\u003COrder | null>;\n}\n\ninterface PaymentGateway {\n    charge(amount: number, currency: string, paymentMethod: string): Promise\u003CPaymentResult>;\n}\n\n// Le module de haut niveau\nclass OrderService {\n    constructor(\n        private readonly repository: OrderRepository,\n        private readonly payment: PaymentGateway\n    ) {}\n\n    async createPaidOrder(orderData: OrderData): Promise\u003COrder> {\n        const paymentResult = await this.payment.charge(\n            orderData.total,\n            orderData.currency,\n            orderData.paymentMethodId\n        );\n\n        if (!paymentResult.success) {\n            throw new PaymentFailedError(paymentResult.errorCode);\n        }\n\n        const orderId = await this.repository.save({\n            ...orderData,\n            paymentId: paymentResult.transactionId,\n            status: 'paid'\n        });\n\n        return this.repository.findById(orderId);\n    }\n}\n\n// Test — sans Stripe, sans base de données\ndescribe('OrderService', () => {\n    it('should create order with successful payment', async () => {\n        const mockRepository: OrderRepository = {\n            save: jest.fn().mockResolvedValue('order-123'),\n            findById: jest.fn().mockResolvedValue({ id: 'order-123', status: 'paid' })\n        };\n\n        const mockPayment: PaymentGateway = {\n            charge: jest.fn().mockResolvedValue({ success: true, transactionId: 'txn-456' })\n        };\n\n        const service = new OrderService(mockRepository, mockPayment);\n        const order = await service.createPaidOrder({ total: 1000, currency: 'EUR' });\n\n        expect(order.status).toBe('paid');\n    });\n});\n","typescript","language-typescript shiki shiki-themes catppuccin-frappe github-dark",[2995],{"type":30,"tag":98,"props":2996,"children":2997},{"__ignoreMap":8},[2998,3006,3023,3083,3149,3157,3164,3180,3269,3276,3283,3291,3306,3319,3349,3374,3386,3393,3450,3501,3522,3542,3558,3570,3577,3614,3651,3659,3666,3714,3730,3759,3776,3792,3799,3836,3844,3851,3859,3868,3904,3943,3972,4030,4116,4125,4133,4162,4247,4255,4263,4305,4390,4398,4447,4464],{"type":30,"tag":439,"props":2999,"children":3000},{"class":441,"line":442},[3001],{"type":30,"tag":439,"props":3002,"children":3003},{"style":446},[3004],{"type":36,"value":3005},"// Les abstractions (interfaces TypeScript)\n",{"type":30,"tag":439,"props":3007,"children":3008},{"class":441,"line":452},[3009,3014,3018],{"type":30,"tag":439,"props":3010,"children":3011},{"style":456},[3012],{"type":36,"value":3013},"interface",{"type":30,"tag":439,"props":3015,"children":3016},{"style":916},[3017],{"type":36,"value":1449},{"type":30,"tag":439,"props":3019,"children":3020},{"style":922},[3021],{"type":36,"value":3022}," {\n",{"type":30,"tag":439,"props":3024,"children":3025},{"class":441,"line":478},[3026,3031,3035,3040,3044,3049,3053,3057,3062,3068,3073,3078],{"type":30,"tag":439,"props":3027,"children":3028},{"style":1105},[3029],{"type":36,"value":3030},"    save",{"type":30,"tag":439,"props":3032,"children":3033},{"style":922},[3034],{"type":36,"value":944},{"type":30,"tag":439,"props":3036,"children":3037},{"style":992},[3038],{"type":36,"value":3039},"orderData",{"type":30,"tag":439,"props":3041,"children":3042},{"style":487},[3043],{"type":36,"value":1504},{"type":30,"tag":439,"props":3045,"children":3046},{"style":916},[3047],{"type":36,"value":3048}," OrderData",{"type":30,"tag":439,"props":3050,"children":3051},{"style":922},[3052],{"type":36,"value":1030},{"type":30,"tag":439,"props":3054,"children":3055},{"style":487},[3056],{"type":36,"value":1504},{"type":30,"tag":439,"props":3058,"children":3059},{"style":916},[3060],{"type":36,"value":3061}," Promise",{"type":30,"tag":439,"props":3063,"children":3065},{"style":3064},"--shiki-default:#99D1DB;--shiki-dark:#E1E4E8",[3066],{"type":36,"value":3067},"\u003C",{"type":30,"tag":439,"props":3069,"children":3070},{"style":1638},[3071],{"type":36,"value":3072},"string",{"type":30,"tag":439,"props":3074,"children":3075},{"style":3064},[3076],{"type":36,"value":3077},">",{"type":30,"tag":439,"props":3079,"children":3080},{"style":922},[3081],{"type":36,"value":3082},";\n",{"type":30,"tag":439,"props":3084,"children":3085},{"class":441,"line":508},[3086,3091,3095,3100,3104,3109,3113,3117,3121,3125,3130,3135,3141,3145],{"type":30,"tag":439,"props":3087,"children":3088},{"style":1105},[3089],{"type":36,"value":3090},"    findById",{"type":30,"tag":439,"props":3092,"children":3093},{"style":922},[3094],{"type":36,"value":944},{"type":30,"tag":439,"props":3096,"children":3097},{"style":992},[3098],{"type":36,"value":3099},"orderId",{"type":30,"tag":439,"props":3101,"children":3102},{"style":487},[3103],{"type":36,"value":1504},{"type":30,"tag":439,"props":3105,"children":3106},{"style":1638},[3107],{"type":36,"value":3108}," string",{"type":30,"tag":439,"props":3110,"children":3111},{"style":922},[3112],{"type":36,"value":1030},{"type":30,"tag":439,"props":3114,"children":3115},{"style":487},[3116],{"type":36,"value":1504},{"type":30,"tag":439,"props":3118,"children":3119},{"style":916},[3120],{"type":36,"value":3061},{"type":30,"tag":439,"props":3122,"children":3123},{"style":3064},[3124],{"type":36,"value":3067},{"type":30,"tag":439,"props":3126,"children":3127},{"style":916},[3128],{"type":36,"value":3129},"Order",{"type":30,"tag":439,"props":3131,"children":3132},{"style":487},[3133],{"type":36,"value":3134}," |",{"type":30,"tag":439,"props":3136,"children":3138},{"style":3137},"--shiki-default:#CA9EE6;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[3139],{"type":36,"value":3140}," null",{"type":30,"tag":439,"props":3142,"children":3143},{"style":3064},[3144],{"type":36,"value":3077},{"type":30,"tag":439,"props":3146,"children":3147},{"style":922},[3148],{"type":36,"value":3082},{"type":30,"tag":439,"props":3150,"children":3151},{"class":441,"line":517},[3152],{"type":30,"tag":439,"props":3153,"children":3154},{"style":922},[3155],{"type":36,"value":3156},"}\n",{"type":30,"tag":439,"props":3158,"children":3159},{"class":441,"line":526},[3160],{"type":30,"tag":439,"props":3161,"children":3162},{"emptyLinePlaceholder":13},[3163],{"type":36,"value":514},{"type":30,"tag":439,"props":3165,"children":3166},{"class":441,"line":547},[3167,3171,3176],{"type":30,"tag":439,"props":3168,"children":3169},{"style":456},[3170],{"type":36,"value":3013},{"type":30,"tag":439,"props":3172,"children":3173},{"style":916},[3174],{"type":36,"value":3175}," PaymentGateway",{"type":30,"tag":439,"props":3177,"children":3178},{"style":922},[3179],{"type":36,"value":3022},{"type":30,"tag":439,"props":3181,"children":3182},{"class":441,"line":1097},[3183,3188,3192,3197,3201,3206,3210,3215,3219,3223,3227,3232,3236,3240,3244,3248,3252,3256,3261,3265],{"type":30,"tag":439,"props":3184,"children":3185},{"style":1105},[3186],{"type":36,"value":3187},"    charge",{"type":30,"tag":439,"props":3189,"children":3190},{"style":922},[3191],{"type":36,"value":944},{"type":30,"tag":439,"props":3193,"children":3194},{"style":992},[3195],{"type":36,"value":3196},"amount",{"type":30,"tag":439,"props":3198,"children":3199},{"style":487},[3200],{"type":36,"value":1504},{"type":30,"tag":439,"props":3202,"children":3203},{"style":1638},[3204],{"type":36,"value":3205}," number",{"type":30,"tag":439,"props":3207,"children":3208},{"style":922},[3209],{"type":36,"value":1010},{"type":30,"tag":439,"props":3211,"children":3212},{"style":992},[3213],{"type":36,"value":3214}," currency",{"type":30,"tag":439,"props":3216,"children":3217},{"style":487},[3218],{"type":36,"value":1504},{"type":30,"tag":439,"props":3220,"children":3221},{"style":1638},[3222],{"type":36,"value":3108},{"type":30,"tag":439,"props":3224,"children":3225},{"style":922},[3226],{"type":36,"value":1010},{"type":30,"tag":439,"props":3228,"children":3229},{"style":992},[3230],{"type":36,"value":3231}," paymentMethod",{"type":30,"tag":439,"props":3233,"children":3234},{"style":487},[3235],{"type":36,"value":1504},{"type":30,"tag":439,"props":3237,"children":3238},{"style":1638},[3239],{"type":36,"value":3108},{"type":30,"tag":439,"props":3241,"children":3242},{"style":922},[3243],{"type":36,"value":1030},{"type":30,"tag":439,"props":3245,"children":3246},{"style":487},[3247],{"type":36,"value":1504},{"type":30,"tag":439,"props":3249,"children":3250},{"style":916},[3251],{"type":36,"value":3061},{"type":30,"tag":439,"props":3253,"children":3254},{"style":3064},[3255],{"type":36,"value":3067},{"type":30,"tag":439,"props":3257,"children":3258},{"style":916},[3259],{"type":36,"value":3260},"PaymentResult",{"type":30,"tag":439,"props":3262,"children":3263},{"style":3064},[3264],{"type":36,"value":3077},{"type":30,"tag":439,"props":3266,"children":3267},{"style":922},[3268],{"type":36,"value":3082},{"type":30,"tag":439,"props":3270,"children":3271},{"class":441,"line":1133},[3272],{"type":30,"tag":439,"props":3273,"children":3274},{"style":922},[3275],{"type":36,"value":3156},{"type":30,"tag":439,"props":3277,"children":3278},{"class":441,"line":1183},[3279],{"type":30,"tag":439,"props":3280,"children":3281},{"emptyLinePlaceholder":13},[3282],{"type":36,"value":514},{"type":30,"tag":439,"props":3284,"children":3285},{"class":441,"line":1250},[3286],{"type":30,"tag":439,"props":3287,"children":3288},{"style":446},[3289],{"type":36,"value":3290},"// Le module de haut niveau\n",{"type":30,"tag":439,"props":3292,"children":3293},{"class":441,"line":1655},[3294,3298,3302],{"type":30,"tag":439,"props":3295,"children":3296},{"style":456},[3297],{"type":36,"value":913},{"type":30,"tag":439,"props":3299,"children":3300},{"style":916},[3301],{"type":36,"value":919},{"type":30,"tag":439,"props":3303,"children":3304},{"style":922},[3305],{"type":36,"value":3022},{"type":30,"tag":439,"props":3307,"children":3308},{"class":441,"line":1663},[3309,3314],{"type":30,"tag":439,"props":3310,"children":3311},{"style":456},[3312],{"type":36,"value":3313},"    constructor",{"type":30,"tag":439,"props":3315,"children":3316},{"style":922},[3317],{"type":36,"value":3318},"(\n",{"type":30,"tag":439,"props":3320,"children":3321},{"class":441,"line":1672},[3322,3327,3332,3336,3340,3344],{"type":30,"tag":439,"props":3323,"children":3324},{"style":456},[3325],{"type":36,"value":3326},"        private",{"type":30,"tag":439,"props":3328,"children":3329},{"style":456},[3330],{"type":36,"value":3331}," readonly",{"type":30,"tag":439,"props":3333,"children":3334},{"style":992},[3335],{"type":36,"value":1714},{"type":30,"tag":439,"props":3337,"children":3338},{"style":487},[3339],{"type":36,"value":1504},{"type":30,"tag":439,"props":3341,"children":3342},{"style":916},[3343],{"type":36,"value":1449},{"type":30,"tag":439,"props":3345,"children":3346},{"style":922},[3347],{"type":36,"value":3348},",\n",{"type":30,"tag":439,"props":3350,"children":3351},{"class":441,"line":1688},[3352,3356,3360,3365,3369],{"type":30,"tag":439,"props":3353,"children":3354},{"style":456},[3355],{"type":36,"value":3326},{"type":30,"tag":439,"props":3357,"children":3358},{"style":456},[3359],{"type":36,"value":3331},{"type":30,"tag":439,"props":3361,"children":3362},{"style":992},[3363],{"type":36,"value":3364}," payment",{"type":30,"tag":439,"props":3366,"children":3367},{"style":487},[3368],{"type":36,"value":1504},{"type":30,"tag":439,"props":3370,"children":3371},{"style":916},[3372],{"type":36,"value":3373}," PaymentGateway\n",{"type":30,"tag":439,"props":3375,"children":3376},{"class":441,"line":1747},[3377,3382],{"type":30,"tag":439,"props":3378,"children":3379},{"style":922},[3380],{"type":36,"value":3381},"    )",{"type":30,"tag":439,"props":3383,"children":3384},{"style":922},[3385],{"type":36,"value":2564},{"type":30,"tag":439,"props":3387,"children":3388},{"class":441,"line":1773},[3389],{"type":30,"tag":439,"props":3390,"children":3391},{"emptyLinePlaceholder":13},[3392],{"type":36,"value":514},{"type":30,"tag":439,"props":3394,"children":3395},{"class":441,"line":1799},[3396,3401,3406,3410,3414,3418,3422,3426,3430,3434,3438,3442,3446],{"type":30,"tag":439,"props":3397,"children":3398},{"style":456},[3399],{"type":36,"value":3400},"    async",{"type":30,"tag":439,"props":3402,"children":3403},{"style":1105},[3404],{"type":36,"value":3405}," createPaidOrder",{"type":30,"tag":439,"props":3407,"children":3408},{"style":922},[3409],{"type":36,"value":944},{"type":30,"tag":439,"props":3411,"children":3412},{"style":992},[3413],{"type":36,"value":3039},{"type":30,"tag":439,"props":3415,"children":3416},{"style":487},[3417],{"type":36,"value":1504},{"type":30,"tag":439,"props":3419,"children":3420},{"style":916},[3421],{"type":36,"value":3048},{"type":30,"tag":439,"props":3423,"children":3424},{"style":922},[3425],{"type":36,"value":1030},{"type":30,"tag":439,"props":3427,"children":3428},{"style":487},[3429],{"type":36,"value":1504},{"type":30,"tag":439,"props":3431,"children":3432},{"style":916},[3433],{"type":36,"value":3061},{"type":30,"tag":439,"props":3435,"children":3436},{"style":3064},[3437],{"type":36,"value":3067},{"type":30,"tag":439,"props":3439,"children":3440},{"style":916},[3441],{"type":36,"value":3129},{"type":30,"tag":439,"props":3443,"children":3444},{"style":3064},[3445],{"type":36,"value":3077},{"type":30,"tag":439,"props":3447,"children":3448},{"style":922},[3449],{"type":36,"value":3022},{"type":30,"tag":439,"props":3451,"children":3452},{"class":441,"line":1807},[3453,3458,3463,3467,3472,3478,3483,3488,3492,3497],{"type":30,"tag":439,"props":3454,"children":3455},{"style":456},[3456],{"type":36,"value":3457},"        const",{"type":30,"tag":439,"props":3459,"children":3460},{"style":1418},[3461],{"type":36,"value":3462}," paymentResult",{"type":30,"tag":439,"props":3464,"children":3465},{"style":487},[3466],{"type":36,"value":2694},{"type":30,"tag":439,"props":3468,"children":3469},{"style":456},[3470],{"type":36,"value":3471}," await",{"type":30,"tag":439,"props":3473,"children":3475},{"style":3474},"--shiki-default:#E78284;--shiki-dark:#79B8FF",[3476],{"type":36,"value":3477}," this",{"type":30,"tag":439,"props":3479,"children":3481},{"style":3480},"--shiki-default:#81C8BE;--shiki-dark:#E1E4E8",[3482],{"type":36,"value":969},{"type":30,"tag":439,"props":3484,"children":3485},{"style":462},[3486],{"type":36,"value":3487},"payment",{"type":30,"tag":439,"props":3489,"children":3490},{"style":3480},[3491],{"type":36,"value":969},{"type":30,"tag":439,"props":3493,"children":3494},{"style":1105},[3495],{"type":36,"value":3496},"charge",{"type":30,"tag":439,"props":3498,"children":3499},{"style":462},[3500],{"type":36,"value":3318},{"type":30,"tag":439,"props":3502,"children":3503},{"class":441,"line":1859},[3504,3509,3513,3518],{"type":30,"tag":439,"props":3505,"children":3506},{"style":462},[3507],{"type":36,"value":3508},"            orderData",{"type":30,"tag":439,"props":3510,"children":3511},{"style":3480},[3512],{"type":36,"value":969},{"type":30,"tag":439,"props":3514,"children":3515},{"style":462},[3516],{"type":36,"value":3517},"total",{"type":30,"tag":439,"props":3519,"children":3520},{"style":922},[3521],{"type":36,"value":3348},{"type":30,"tag":439,"props":3523,"children":3524},{"class":441,"line":1904},[3525,3529,3533,3538],{"type":30,"tag":439,"props":3526,"children":3527},{"style":462},[3528],{"type":36,"value":3508},{"type":30,"tag":439,"props":3530,"children":3531},{"style":3480},[3532],{"type":36,"value":969},{"type":30,"tag":439,"props":3534,"children":3535},{"style":462},[3536],{"type":36,"value":3537},"currency",{"type":30,"tag":439,"props":3539,"children":3540},{"style":922},[3541],{"type":36,"value":3348},{"type":30,"tag":439,"props":3543,"children":3544},{"class":441,"line":1965},[3545,3549,3553],{"type":30,"tag":439,"props":3546,"children":3547},{"style":462},[3548],{"type":36,"value":3508},{"type":30,"tag":439,"props":3550,"children":3551},{"style":3480},[3552],{"type":36,"value":969},{"type":30,"tag":439,"props":3554,"children":3555},{"style":462},[3556],{"type":36,"value":3557},"paymentMethodId\n",{"type":30,"tag":439,"props":3559,"children":3560},{"class":441,"line":1977},[3561,3566],{"type":30,"tag":439,"props":3562,"children":3563},{"style":462},[3564],{"type":36,"value":3565},"        )",{"type":30,"tag":439,"props":3567,"children":3568},{"style":922},[3569],{"type":36,"value":3082},{"type":30,"tag":439,"props":3571,"children":3572},{"class":441,"line":1985},[3573],{"type":30,"tag":439,"props":3574,"children":3575},{"emptyLinePlaceholder":13},[3576],{"type":36,"value":514},{"type":30,"tag":439,"props":3578,"children":3579},{"class":441,"line":1994},[3580,3585,3590,3595,3600,3604,3609],{"type":30,"tag":439,"props":3581,"children":3582},{"style":456},[3583],{"type":36,"value":3584},"        if",{"type":30,"tag":439,"props":3586,"children":3587},{"style":462},[3588],{"type":36,"value":3589}," (",{"type":30,"tag":439,"props":3591,"children":3592},{"style":487},[3593],{"type":36,"value":3594},"!",{"type":30,"tag":439,"props":3596,"children":3597},{"style":462},[3598],{"type":36,"value":3599},"paymentResult",{"type":30,"tag":439,"props":3601,"children":3602},{"style":3480},[3603],{"type":36,"value":969},{"type":30,"tag":439,"props":3605,"children":3606},{"style":462},[3607],{"type":36,"value":3608},"success) ",{"type":30,"tag":439,"props":3610,"children":3611},{"style":922},[3612],{"type":36,"value":3613},"{\n",{"type":30,"tag":439,"props":3615,"children":3616},{"class":441,"line":2019},[3617,3622,3628,3633,3638,3642,3647],{"type":30,"tag":439,"props":3618,"children":3619},{"style":456},[3620],{"type":36,"value":3621},"            throw",{"type":30,"tag":439,"props":3623,"children":3625},{"style":3624},"--shiki-default:#CA9EE6;--shiki-default-font-weight:bold;--shiki-dark:#F97583;--shiki-dark-font-weight:inherit",[3626],{"type":36,"value":3627}," new",{"type":30,"tag":439,"props":3629,"children":3630},{"style":1105},[3631],{"type":36,"value":3632}," PaymentFailedError",{"type":30,"tag":439,"props":3634,"children":3635},{"style":462},[3636],{"type":36,"value":3637},"(paymentResult",{"type":30,"tag":439,"props":3639,"children":3640},{"style":3480},[3641],{"type":36,"value":969},{"type":30,"tag":439,"props":3643,"children":3644},{"style":462},[3645],{"type":36,"value":3646},"errorCode)",{"type":30,"tag":439,"props":3648,"children":3649},{"style":922},[3650],{"type":36,"value":3082},{"type":30,"tag":439,"props":3652,"children":3653},{"class":441,"line":2071},[3654],{"type":30,"tag":439,"props":3655,"children":3656},{"style":922},[3657],{"type":36,"value":3658},"        }\n",{"type":30,"tag":439,"props":3660,"children":3661},{"class":441,"line":2121},[3662],{"type":30,"tag":439,"props":3663,"children":3664},{"emptyLinePlaceholder":13},[3665],{"type":36,"value":514},{"type":30,"tag":439,"props":3667,"children":3668},{"class":441,"line":2129},[3669,3673,3678,3682,3686,3690,3694,3698,3702,3706,3710],{"type":30,"tag":439,"props":3670,"children":3671},{"style":456},[3672],{"type":36,"value":3457},{"type":30,"tag":439,"props":3674,"children":3675},{"style":1418},[3676],{"type":36,"value":3677}," orderId",{"type":30,"tag":439,"props":3679,"children":3680},{"style":487},[3681],{"type":36,"value":2694},{"type":30,"tag":439,"props":3683,"children":3684},{"style":456},[3685],{"type":36,"value":3471},{"type":30,"tag":439,"props":3687,"children":3688},{"style":3474},[3689],{"type":36,"value":3477},{"type":30,"tag":439,"props":3691,"children":3692},{"style":3480},[3693],{"type":36,"value":969},{"type":30,"tag":439,"props":3695,"children":3696},{"style":462},[3697],{"type":36,"value":1881},{"type":30,"tag":439,"props":3699,"children":3700},{"style":3480},[3701],{"type":36,"value":969},{"type":30,"tag":439,"props":3703,"children":3704},{"style":1105},[3705],{"type":36,"value":1166},{"type":30,"tag":439,"props":3707,"children":3708},{"style":462},[3709],{"type":36,"value":944},{"type":30,"tag":439,"props":3711,"children":3712},{"style":922},[3713],{"type":36,"value":3613},{"type":30,"tag":439,"props":3715,"children":3716},{"class":441,"line":820},[3717,3722,3726],{"type":30,"tag":439,"props":3718,"children":3719},{"style":487},[3720],{"type":36,"value":3721},"            ...",{"type":30,"tag":439,"props":3723,"children":3724},{"style":462},[3725],{"type":36,"value":3039},{"type":30,"tag":439,"props":3727,"children":3728},{"style":922},[3729],{"type":36,"value":3348},{"type":30,"tag":439,"props":3731,"children":3732},{"class":441,"line":2221},[3733,3738,3742,3746,3750,3755],{"type":30,"tag":439,"props":3734,"children":3735},{"style":462},[3736],{"type":36,"value":3737},"            paymentId",{"type":30,"tag":439,"props":3739,"children":3740},{"style":3480},[3741],{"type":36,"value":1504},{"type":30,"tag":439,"props":3743,"children":3744},{"style":462},[3745],{"type":36,"value":3462},{"type":30,"tag":439,"props":3747,"children":3748},{"style":3480},[3749],{"type":36,"value":969},{"type":30,"tag":439,"props":3751,"children":3752},{"style":462},[3753],{"type":36,"value":3754},"transactionId",{"type":30,"tag":439,"props":3756,"children":3757},{"style":922},[3758],{"type":36,"value":3348},{"type":30,"tag":439,"props":3760,"children":3761},{"class":441,"line":2318},[3762,3767,3771],{"type":30,"tag":439,"props":3763,"children":3764},{"style":462},[3765],{"type":36,"value":3766},"            status",{"type":30,"tag":439,"props":3768,"children":3769},{"style":3480},[3770],{"type":36,"value":1504},{"type":30,"tag":439,"props":3772,"children":3773},{"style":1002},[3774],{"type":36,"value":3775}," 'paid'\n",{"type":30,"tag":439,"props":3777,"children":3778},{"class":441,"line":2326},[3779,3784,3788],{"type":30,"tag":439,"props":3780,"children":3781},{"style":922},[3782],{"type":36,"value":3783},"        }",{"type":30,"tag":439,"props":3785,"children":3786},{"style":462},[3787],{"type":36,"value":1030},{"type":30,"tag":439,"props":3789,"children":3790},{"style":922},[3791],{"type":36,"value":3082},{"type":30,"tag":439,"props":3793,"children":3794},{"class":441,"line":2335},[3795],{"type":30,"tag":439,"props":3796,"children":3797},{"emptyLinePlaceholder":13},[3798],{"type":36,"value":514},{"type":30,"tag":439,"props":3800,"children":3801},{"class":441,"line":11},[3802,3806,3810,3814,3818,3822,3827,3832],{"type":30,"tag":439,"props":3803,"children":3804},{"style":456},[3805],{"type":36,"value":1256},{"type":30,"tag":439,"props":3807,"children":3808},{"style":3474},[3809],{"type":36,"value":3477},{"type":30,"tag":439,"props":3811,"children":3812},{"style":3480},[3813],{"type":36,"value":969},{"type":30,"tag":439,"props":3815,"children":3816},{"style":462},[3817],{"type":36,"value":1881},{"type":30,"tag":439,"props":3819,"children":3820},{"style":3480},[3821],{"type":36,"value":969},{"type":30,"tag":439,"props":3823,"children":3824},{"style":1105},[3825],{"type":36,"value":3826},"findById",{"type":30,"tag":439,"props":3828,"children":3829},{"style":462},[3830],{"type":36,"value":3831},"(orderId)",{"type":30,"tag":439,"props":3833,"children":3834},{"style":922},[3835],{"type":36,"value":3082},{"type":30,"tag":439,"props":3837,"children":3838},{"class":441,"line":2382},[3839],{"type":30,"tag":439,"props":3840,"children":3841},{"style":922},[3842],{"type":36,"value":3843},"    }\n",{"type":30,"tag":439,"props":3845,"children":3846},{"class":441,"line":2439},[3847],{"type":30,"tag":439,"props":3848,"children":3849},{"style":922},[3850],{"type":36,"value":3156},{"type":30,"tag":439,"props":3852,"children":3854},{"class":441,"line":3853},38,[3855],{"type":30,"tag":439,"props":3856,"children":3857},{"emptyLinePlaceholder":13},[3858],{"type":36,"value":514},{"type":30,"tag":439,"props":3860,"children":3862},{"class":441,"line":3861},39,[3863],{"type":30,"tag":439,"props":3864,"children":3865},{"style":446},[3866],{"type":36,"value":3867},"// Test — sans Stripe, sans base de données\n",{"type":30,"tag":439,"props":3869,"children":3871},{"class":441,"line":3870},40,[3872,3877,3881,3886,3890,3895,3900],{"type":30,"tag":439,"props":3873,"children":3874},{"style":1105},[3875],{"type":36,"value":3876},"describe",{"type":30,"tag":439,"props":3878,"children":3879},{"style":462},[3880],{"type":36,"value":944},{"type":30,"tag":439,"props":3882,"children":3883},{"style":1002},[3884],{"type":36,"value":3885},"'OrderService'",{"type":30,"tag":439,"props":3887,"children":3888},{"style":922},[3889],{"type":36,"value":1010},{"type":30,"tag":439,"props":3891,"children":3892},{"style":922},[3893],{"type":36,"value":3894}," ()",{"type":30,"tag":439,"props":3896,"children":3897},{"style":487},[3898],{"type":36,"value":3899}," =>",{"type":30,"tag":439,"props":3901,"children":3902},{"style":922},[3903],{"type":36,"value":3022},{"type":30,"tag":439,"props":3905,"children":3907},{"class":441,"line":3906},41,[3908,3913,3917,3922,3926,3931,3935,3939],{"type":30,"tag":439,"props":3909,"children":3910},{"style":1105},[3911],{"type":36,"value":3912},"    it",{"type":30,"tag":439,"props":3914,"children":3915},{"style":462},[3916],{"type":36,"value":944},{"type":30,"tag":439,"props":3918,"children":3919},{"style":1002},[3920],{"type":36,"value":3921},"'should create order with successful payment'",{"type":30,"tag":439,"props":3923,"children":3924},{"style":922},[3925],{"type":36,"value":1010},{"type":30,"tag":439,"props":3927,"children":3928},{"style":456},[3929],{"type":36,"value":3930}," async",{"type":30,"tag":439,"props":3932,"children":3933},{"style":922},[3934],{"type":36,"value":3894},{"type":30,"tag":439,"props":3936,"children":3937},{"style":487},[3938],{"type":36,"value":3899},{"type":30,"tag":439,"props":3940,"children":3941},{"style":922},[3942],{"type":36,"value":3022},{"type":30,"tag":439,"props":3944,"children":3946},{"class":441,"line":3945},42,[3947,3951,3956,3960,3964,3968],{"type":30,"tag":439,"props":3948,"children":3949},{"style":456},[3950],{"type":36,"value":3457},{"type":30,"tag":439,"props":3952,"children":3953},{"style":1418},[3954],{"type":36,"value":3955}," mockRepository",{"type":30,"tag":439,"props":3957,"children":3958},{"style":487},[3959],{"type":36,"value":1504},{"type":30,"tag":439,"props":3961,"children":3962},{"style":916},[3963],{"type":36,"value":1449},{"type":30,"tag":439,"props":3965,"children":3966},{"style":487},[3967],{"type":36,"value":2694},{"type":30,"tag":439,"props":3969,"children":3970},{"style":922},[3971],{"type":36,"value":3022},{"type":30,"tag":439,"props":3973,"children":3975},{"class":441,"line":3974},43,[3976,3981,3985,3990,3994,3999,4004,4008,4013,4017,4022,4026],{"type":30,"tag":439,"props":3977,"children":3978},{"style":462},[3979],{"type":36,"value":3980},"            save",{"type":30,"tag":439,"props":3982,"children":3983},{"style":3480},[3984],{"type":36,"value":1504},{"type":30,"tag":439,"props":3986,"children":3987},{"style":462},[3988],{"type":36,"value":3989}," jest",{"type":30,"tag":439,"props":3991,"children":3992},{"style":3480},[3993],{"type":36,"value":969},{"type":30,"tag":439,"props":3995,"children":3996},{"style":1105},[3997],{"type":36,"value":3998},"fn",{"type":30,"tag":439,"props":4000,"children":4001},{"style":462},[4002],{"type":36,"value":4003},"()",{"type":30,"tag":439,"props":4005,"children":4006},{"style":3480},[4007],{"type":36,"value":969},{"type":30,"tag":439,"props":4009,"children":4010},{"style":1105},[4011],{"type":36,"value":4012},"mockResolvedValue",{"type":30,"tag":439,"props":4014,"children":4015},{"style":462},[4016],{"type":36,"value":944},{"type":30,"tag":439,"props":4018,"children":4019},{"style":1002},[4020],{"type":36,"value":4021},"'order-123'",{"type":30,"tag":439,"props":4023,"children":4024},{"style":462},[4025],{"type":36,"value":1030},{"type":30,"tag":439,"props":4027,"children":4028},{"style":922},[4029],{"type":36,"value":3348},{"type":30,"tag":439,"props":4031,"children":4033},{"class":441,"line":4032},44,[4034,4039,4043,4047,4051,4055,4059,4063,4067,4071,4075,4080,4084,4089,4093,4098,4102,4107,4112],{"type":30,"tag":439,"props":4035,"children":4036},{"style":462},[4037],{"type":36,"value":4038},"            findById",{"type":30,"tag":439,"props":4040,"children":4041},{"style":3480},[4042],{"type":36,"value":1504},{"type":30,"tag":439,"props":4044,"children":4045},{"style":462},[4046],{"type":36,"value":3989},{"type":30,"tag":439,"props":4048,"children":4049},{"style":3480},[4050],{"type":36,"value":969},{"type":30,"tag":439,"props":4052,"children":4053},{"style":1105},[4054],{"type":36,"value":3998},{"type":30,"tag":439,"props":4056,"children":4057},{"style":462},[4058],{"type":36,"value":4003},{"type":30,"tag":439,"props":4060,"children":4061},{"style":3480},[4062],{"type":36,"value":969},{"type":30,"tag":439,"props":4064,"children":4065},{"style":1105},[4066],{"type":36,"value":4012},{"type":30,"tag":439,"props":4068,"children":4069},{"style":462},[4070],{"type":36,"value":944},{"type":30,"tag":439,"props":4072,"children":4073},{"style":922},[4074],{"type":36,"value":2297},{"type":30,"tag":439,"props":4076,"children":4077},{"style":462},[4078],{"type":36,"value":4079}," id",{"type":30,"tag":439,"props":4081,"children":4082},{"style":3480},[4083],{"type":36,"value":1504},{"type":30,"tag":439,"props":4085,"children":4086},{"style":1002},[4087],{"type":36,"value":4088}," 'order-123'",{"type":30,"tag":439,"props":4090,"children":4091},{"style":922},[4092],{"type":36,"value":1010},{"type":30,"tag":439,"props":4094,"children":4095},{"style":462},[4096],{"type":36,"value":4097}," status",{"type":30,"tag":439,"props":4099,"children":4100},{"style":3480},[4101],{"type":36,"value":1504},{"type":30,"tag":439,"props":4103,"children":4104},{"style":1002},[4105],{"type":36,"value":4106}," 'paid'",{"type":30,"tag":439,"props":4108,"children":4109},{"style":922},[4110],{"type":36,"value":4111}," }",{"type":30,"tag":439,"props":4113,"children":4114},{"style":462},[4115],{"type":36,"value":1180},{"type":30,"tag":439,"props":4117,"children":4119},{"class":441,"line":4118},45,[4120],{"type":30,"tag":439,"props":4121,"children":4122},{"style":922},[4123],{"type":36,"value":4124},"        };\n",{"type":30,"tag":439,"props":4126,"children":4128},{"class":441,"line":4127},46,[4129],{"type":30,"tag":439,"props":4130,"children":4131},{"emptyLinePlaceholder":13},[4132],{"type":36,"value":514},{"type":30,"tag":439,"props":4134,"children":4136},{"class":441,"line":4135},47,[4137,4141,4146,4150,4154,4158],{"type":30,"tag":439,"props":4138,"children":4139},{"style":456},[4140],{"type":36,"value":3457},{"type":30,"tag":439,"props":4142,"children":4143},{"style":1418},[4144],{"type":36,"value":4145}," mockPayment",{"type":30,"tag":439,"props":4147,"children":4148},{"style":487},[4149],{"type":36,"value":1504},{"type":30,"tag":439,"props":4151,"children":4152},{"style":916},[4153],{"type":36,"value":3175},{"type":30,"tag":439,"props":4155,"children":4156},{"style":487},[4157],{"type":36,"value":2694},{"type":30,"tag":439,"props":4159,"children":4160},{"style":922},[4161],{"type":36,"value":3022},{"type":30,"tag":439,"props":4163,"children":4165},{"class":441,"line":4164},48,[4166,4171,4175,4179,4183,4187,4191,4195,4199,4203,4207,4212,4216,4221,4225,4230,4234,4239,4243],{"type":30,"tag":439,"props":4167,"children":4168},{"style":462},[4169],{"type":36,"value":4170},"            charge",{"type":30,"tag":439,"props":4172,"children":4173},{"style":3480},[4174],{"type":36,"value":1504},{"type":30,"tag":439,"props":4176,"children":4177},{"style":462},[4178],{"type":36,"value":3989},{"type":30,"tag":439,"props":4180,"children":4181},{"style":3480},[4182],{"type":36,"value":969},{"type":30,"tag":439,"props":4184,"children":4185},{"style":1105},[4186],{"type":36,"value":3998},{"type":30,"tag":439,"props":4188,"children":4189},{"style":462},[4190],{"type":36,"value":4003},{"type":30,"tag":439,"props":4192,"children":4193},{"style":3480},[4194],{"type":36,"value":969},{"type":30,"tag":439,"props":4196,"children":4197},{"style":1105},[4198],{"type":36,"value":4012},{"type":30,"tag":439,"props":4200,"children":4201},{"style":462},[4202],{"type":36,"value":944},{"type":30,"tag":439,"props":4204,"children":4205},{"style":922},[4206],{"type":36,"value":2297},{"type":30,"tag":439,"props":4208,"children":4209},{"style":462},[4210],{"type":36,"value":4211}," success",{"type":30,"tag":439,"props":4213,"children":4214},{"style":3480},[4215],{"type":36,"value":1504},{"type":30,"tag":439,"props":4217,"children":4218},{"style":1022},[4219],{"type":36,"value":4220}," true",{"type":30,"tag":439,"props":4222,"children":4223},{"style":922},[4224],{"type":36,"value":1010},{"type":30,"tag":439,"props":4226,"children":4227},{"style":462},[4228],{"type":36,"value":4229}," transactionId",{"type":30,"tag":439,"props":4231,"children":4232},{"style":3480},[4233],{"type":36,"value":1504},{"type":30,"tag":439,"props":4235,"children":4236},{"style":1002},[4237],{"type":36,"value":4238}," 'txn-456'",{"type":30,"tag":439,"props":4240,"children":4241},{"style":922},[4242],{"type":36,"value":4111},{"type":30,"tag":439,"props":4244,"children":4245},{"style":462},[4246],{"type":36,"value":1180},{"type":30,"tag":439,"props":4248,"children":4250},{"class":441,"line":4249},49,[4251],{"type":30,"tag":439,"props":4252,"children":4253},{"style":922},[4254],{"type":36,"value":4124},{"type":30,"tag":439,"props":4256,"children":4258},{"class":441,"line":4257},50,[4259],{"type":30,"tag":439,"props":4260,"children":4261},{"emptyLinePlaceholder":13},[4262],{"type":36,"value":514},{"type":30,"tag":439,"props":4264,"children":4266},{"class":441,"line":4265},51,[4267,4271,4275,4279,4283,4287,4292,4296,4301],{"type":30,"tag":439,"props":4268,"children":4269},{"style":456},[4270],{"type":36,"value":3457},{"type":30,"tag":439,"props":4272,"children":4273},{"style":1418},[4274],{"type":36,"value":2833},{"type":30,"tag":439,"props":4276,"children":4277},{"style":487},[4278],{"type":36,"value":2694},{"type":30,"tag":439,"props":4280,"children":4281},{"style":3624},[4282],{"type":36,"value":3627},{"type":30,"tag":439,"props":4284,"children":4285},{"style":1105},[4286],{"type":36,"value":919},{"type":30,"tag":439,"props":4288,"children":4289},{"style":462},[4290],{"type":36,"value":4291},"(mockRepository",{"type":30,"tag":439,"props":4293,"children":4294},{"style":922},[4295],{"type":36,"value":1010},{"type":30,"tag":439,"props":4297,"children":4298},{"style":462},[4299],{"type":36,"value":4300}," mockPayment)",{"type":30,"tag":439,"props":4302,"children":4303},{"style":922},[4304],{"type":36,"value":3082},{"type":30,"tag":439,"props":4306,"children":4308},{"class":441,"line":4307},52,[4309,4313,4318,4322,4326,4330,4334,4339,4343,4347,4352,4356,4361,4365,4369,4373,4378,4382,4386],{"type":30,"tag":439,"props":4310,"children":4311},{"style":456},[4312],{"type":36,"value":3457},{"type":30,"tag":439,"props":4314,"children":4315},{"style":1418},[4316],{"type":36,"value":4317}," order",{"type":30,"tag":439,"props":4319,"children":4320},{"style":487},[4321],{"type":36,"value":2694},{"type":30,"tag":439,"props":4323,"children":4324},{"style":456},[4325],{"type":36,"value":3471},{"type":30,"tag":439,"props":4327,"children":4328},{"style":462},[4329],{"type":36,"value":2833},{"type":30,"tag":439,"props":4331,"children":4332},{"style":3480},[4333],{"type":36,"value":969},{"type":30,"tag":439,"props":4335,"children":4336},{"style":1105},[4337],{"type":36,"value":4338},"createPaidOrder",{"type":30,"tag":439,"props":4340,"children":4341},{"style":462},[4342],{"type":36,"value":944},{"type":30,"tag":439,"props":4344,"children":4345},{"style":922},[4346],{"type":36,"value":2297},{"type":30,"tag":439,"props":4348,"children":4349},{"style":462},[4350],{"type":36,"value":4351}," total",{"type":30,"tag":439,"props":4353,"children":4354},{"style":3480},[4355],{"type":36,"value":1504},{"type":30,"tag":439,"props":4357,"children":4358},{"style":1022},[4359],{"type":36,"value":4360}," 1000",{"type":30,"tag":439,"props":4362,"children":4363},{"style":922},[4364],{"type":36,"value":1010},{"type":30,"tag":439,"props":4366,"children":4367},{"style":462},[4368],{"type":36,"value":3214},{"type":30,"tag":439,"props":4370,"children":4371},{"style":3480},[4372],{"type":36,"value":1504},{"type":30,"tag":439,"props":4374,"children":4375},{"style":1002},[4376],{"type":36,"value":4377}," 'EUR'",{"type":30,"tag":439,"props":4379,"children":4380},{"style":922},[4381],{"type":36,"value":4111},{"type":30,"tag":439,"props":4383,"children":4384},{"style":462},[4385],{"type":36,"value":1030},{"type":30,"tag":439,"props":4387,"children":4388},{"style":922},[4389],{"type":36,"value":3082},{"type":30,"tag":439,"props":4391,"children":4393},{"class":441,"line":4392},53,[4394],{"type":30,"tag":439,"props":4395,"children":4396},{"emptyLinePlaceholder":13},[4397],{"type":36,"value":514},{"type":30,"tag":439,"props":4399,"children":4401},{"class":441,"line":4400},54,[4402,4407,4412,4416,4421,4425,4430,4434,4439,4443],{"type":30,"tag":439,"props":4403,"children":4404},{"style":1105},[4405],{"type":36,"value":4406},"        expect",{"type":30,"tag":439,"props":4408,"children":4409},{"style":462},[4410],{"type":36,"value":4411},"(order",{"type":30,"tag":439,"props":4413,"children":4414},{"style":3480},[4415],{"type":36,"value":969},{"type":30,"tag":439,"props":4417,"children":4418},{"style":462},[4419],{"type":36,"value":4420},"status)",{"type":30,"tag":439,"props":4422,"children":4423},{"style":3480},[4424],{"type":36,"value":969},{"type":30,"tag":439,"props":4426,"children":4427},{"style":1105},[4428],{"type":36,"value":4429},"toBe",{"type":30,"tag":439,"props":4431,"children":4432},{"style":462},[4433],{"type":36,"value":944},{"type":30,"tag":439,"props":4435,"children":4436},{"style":1002},[4437],{"type":36,"value":4438},"'paid'",{"type":30,"tag":439,"props":4440,"children":4441},{"style":462},[4442],{"type":36,"value":1030},{"type":30,"tag":439,"props":4444,"children":4445},{"style":922},[4446],{"type":36,"value":3082},{"type":30,"tag":439,"props":4448,"children":4450},{"class":441,"line":4449},55,[4451,4456,4460],{"type":30,"tag":439,"props":4452,"children":4453},{"style":922},[4454],{"type":36,"value":4455},"    }",{"type":30,"tag":439,"props":4457,"children":4458},{"style":462},[4459],{"type":36,"value":1030},{"type":30,"tag":439,"props":4461,"children":4462},{"style":922},[4463],{"type":36,"value":3082},{"type":30,"tag":439,"props":4465,"children":4467},{"class":441,"line":4466},56,[4468,4473,4477],{"type":30,"tag":439,"props":4469,"children":4470},{"style":922},[4471],{"type":36,"value":4472},"}",{"type":30,"tag":439,"props":4474,"children":4475},{"style":462},[4476],{"type":36,"value":1030},{"type":30,"tag":439,"props":4478,"children":4479},{"style":922},[4480],{"type":36,"value":3082},{"type":30,"tag":38,"props":4482,"children":4483},{},[4484,4489],{"type":30,"tag":44,"props":4485,"children":4486},{},[4487],{"type":36,"value":4488},"L'avantage TypeScript :",{"type":36,"value":4490}," les interfaces sont vérifiées à la compilation. Si une implémentation ne respecte pas l'interface, le compilateur rejette le code avant même l'exécution.",{"type":30,"tag":67,"props":4492,"children":4493},{},[],{"type":30,"tag":71,"props":4495,"children":4497},{"id":4496},"exemple-3-java-le-dip-avec-spring",[4498],{"type":36,"value":4499},"Exemple 3 : Java, le DIP avec Spring",{"type":30,"tag":185,"props":4501,"children":4505},{"code":4502,"language":4503,"meta":8,"className":4504,"style":8},"// L'abstraction\npublic interface NotificationService {\n    void sendOrderConfirmation(String userId, String orderId);\n    void sendShippingUpdate(String userId, String orderId, String trackingCode);\n}\n\n// Le module de haut niveau (service métier)\n@Service\npublic class OrderFulfillmentService {\n\n    private final OrderRepository orderRepository;\n    private final NotificationService notificationService;\n\n    public OrderFulfillmentService(\n            OrderRepository orderRepository,\n            NotificationService notificationService) {\n        this.orderRepository = orderRepository;\n        this.notificationService = notificationService;\n    }\n\n    public void fulfillOrder(String orderId) {\n        Order order = orderRepository.findById(orderId)\n            .orElseThrow(() -> new OrderNotFoundException(orderId));\n\n        order.setStatus(OrderStatus.FULFILLING);\n        orderRepository.save(order);\n\n        notificationService.sendOrderConfirmation(order.getUserId(), orderId);\n    }\n}\n\n// Implémentation de test\n@Service\n@Profile(\"test\")\npublic class MockNotificationService implements NotificationService {\n\n    private final List\u003CString> sentNotifications = new ArrayList\u003C>();\n\n    @Override\n    public void sendOrderConfirmation(String userId, String orderId) {\n        sentNotifications.add(\"confirmation:\" + userId + \":\" + orderId);\n    }\n}\n","java","language-java shiki shiki-themes catppuccin-frappe github-dark",[4506],{"type":30,"tag":98,"props":4507,"children":4508},{"__ignoreMap":8},[4509,4517,4539,4585,4638,4645,4652,4660,4675,4696,4703,4729,4753,4760,4776,4792,4812,4841,4869,4876,4883,4920,4961,5005,5012,5051,5080,5087,5134,5141,5148,5155,5163,5174,5199,5228,5235,5286,5293,5306,5353,5411,5418],{"type":30,"tag":439,"props":4510,"children":4511},{"class":441,"line":442},[4512],{"type":30,"tag":439,"props":4513,"children":4514},{"style":446},[4515],{"type":36,"value":4516},"// L'abstraction\n",{"type":30,"tag":439,"props":4518,"children":4519},{"class":441,"line":452},[4520,4525,4530,4535],{"type":30,"tag":439,"props":4521,"children":4522},{"style":456},[4523],{"type":36,"value":4524},"public",{"type":30,"tag":439,"props":4526,"children":4527},{"style":456},[4528],{"type":36,"value":4529}," interface",{"type":30,"tag":439,"props":4531,"children":4532},{"style":916},[4533],{"type":36,"value":4534}," NotificationService",{"type":30,"tag":439,"props":4536,"children":4537},{"style":922},[4538],{"type":36,"value":3022},{"type":30,"tag":439,"props":4540,"children":4541},{"class":441,"line":478},[4542,4547,4552,4556,4562,4567,4571,4576,4580],{"type":30,"tag":439,"props":4543,"children":4544},{"style":456},[4545],{"type":36,"value":4546},"    void",{"type":30,"tag":439,"props":4548,"children":4549},{"style":1105},[4550],{"type":36,"value":4551}," sendOrderConfirmation",{"type":30,"tag":439,"props":4553,"children":4554},{"style":922},[4555],{"type":36,"value":944},{"type":30,"tag":439,"props":4557,"children":4559},{"style":4558},"--shiki-default:#CA9EE6;--shiki-dark:#E1E4E8",[4560],{"type":36,"value":4561},"String",{"type":30,"tag":439,"props":4563,"children":4564},{"style":992},[4565],{"type":36,"value":4566}," userId",{"type":30,"tag":439,"props":4568,"children":4569},{"style":922},[4570],{"type":36,"value":1010},{"type":30,"tag":439,"props":4572,"children":4573},{"style":4558},[4574],{"type":36,"value":4575}," String",{"type":30,"tag":439,"props":4577,"children":4578},{"style":992},[4579],{"type":36,"value":3677},{"type":30,"tag":439,"props":4581,"children":4582},{"style":922},[4583],{"type":36,"value":4584},");\n",{"type":30,"tag":439,"props":4586,"children":4587},{"class":441,"line":508},[4588,4592,4597,4601,4605,4609,4613,4617,4621,4625,4629,4634],{"type":30,"tag":439,"props":4589,"children":4590},{"style":456},[4591],{"type":36,"value":4546},{"type":30,"tag":439,"props":4593,"children":4594},{"style":1105},[4595],{"type":36,"value":4596}," sendShippingUpdate",{"type":30,"tag":439,"props":4598,"children":4599},{"style":922},[4600],{"type":36,"value":944},{"type":30,"tag":439,"props":4602,"children":4603},{"style":4558},[4604],{"type":36,"value":4561},{"type":30,"tag":439,"props":4606,"children":4607},{"style":992},[4608],{"type":36,"value":4566},{"type":30,"tag":439,"props":4610,"children":4611},{"style":922},[4612],{"type":36,"value":1010},{"type":30,"tag":439,"props":4614,"children":4615},{"style":4558},[4616],{"type":36,"value":4575},{"type":30,"tag":439,"props":4618,"children":4619},{"style":992},[4620],{"type":36,"value":3677},{"type":30,"tag":439,"props":4622,"children":4623},{"style":922},[4624],{"type":36,"value":1010},{"type":30,"tag":439,"props":4626,"children":4627},{"style":4558},[4628],{"type":36,"value":4575},{"type":30,"tag":439,"props":4630,"children":4631},{"style":992},[4632],{"type":36,"value":4633}," trackingCode",{"type":30,"tag":439,"props":4635,"children":4636},{"style":922},[4637],{"type":36,"value":4584},{"type":30,"tag":439,"props":4639,"children":4640},{"class":441,"line":517},[4641],{"type":30,"tag":439,"props":4642,"children":4643},{"style":922},[4644],{"type":36,"value":3156},{"type":30,"tag":439,"props":4646,"children":4647},{"class":441,"line":526},[4648],{"type":30,"tag":439,"props":4649,"children":4650},{"emptyLinePlaceholder":13},[4651],{"type":36,"value":514},{"type":30,"tag":439,"props":4653,"children":4654},{"class":441,"line":547},[4655],{"type":30,"tag":439,"props":4656,"children":4657},{"style":446},[4658],{"type":36,"value":4659},"// Le module de haut niveau (service métier)\n",{"type":30,"tag":439,"props":4661,"children":4662},{"class":441,"line":1097},[4663,4669],{"type":30,"tag":439,"props":4664,"children":4666},{"style":4665},"--shiki-default:#EF9F76;--shiki-dark:#E1E4E8",[4667],{"type":36,"value":4668},"@",{"type":30,"tag":439,"props":4670,"children":4672},{"style":4671},"--shiki-default:#EF9F76;--shiki-dark:#F97583",[4673],{"type":36,"value":4674},"Service\n",{"type":30,"tag":439,"props":4676,"children":4677},{"class":441,"line":1133},[4678,4682,4687,4692],{"type":30,"tag":439,"props":4679,"children":4680},{"style":456},[4681],{"type":36,"value":4524},{"type":30,"tag":439,"props":4683,"children":4684},{"style":456},[4685],{"type":36,"value":4686}," class",{"type":30,"tag":439,"props":4688,"children":4689},{"style":916},[4690],{"type":36,"value":4691}," OrderFulfillmentService",{"type":30,"tag":439,"props":4693,"children":4694},{"style":922},[4695],{"type":36,"value":3022},{"type":30,"tag":439,"props":4697,"children":4698},{"class":441,"line":1183},[4699],{"type":30,"tag":439,"props":4700,"children":4701},{"emptyLinePlaceholder":13},[4702],{"type":36,"value":514},{"type":30,"tag":439,"props":4704,"children":4705},{"class":441,"line":1250},[4706,4711,4716,4720,4725],{"type":30,"tag":439,"props":4707,"children":4708},{"style":456},[4709],{"type":36,"value":4710},"    private",{"type":30,"tag":439,"props":4712,"children":4713},{"style":456},[4714],{"type":36,"value":4715}," final",{"type":30,"tag":439,"props":4717,"children":4718},{"style":4558},[4719],{"type":36,"value":1449},{"type":30,"tag":439,"props":4721,"children":4722},{"style":462},[4723],{"type":36,"value":4724}," orderRepository",{"type":30,"tag":439,"props":4726,"children":4727},{"style":922},[4728],{"type":36,"value":3082},{"type":30,"tag":439,"props":4730,"children":4731},{"class":441,"line":1655},[4732,4736,4740,4744,4749],{"type":30,"tag":439,"props":4733,"children":4734},{"style":456},[4735],{"type":36,"value":4710},{"type":30,"tag":439,"props":4737,"children":4738},{"style":456},[4739],{"type":36,"value":4715},{"type":30,"tag":439,"props":4741,"children":4742},{"style":4558},[4743],{"type":36,"value":4534},{"type":30,"tag":439,"props":4745,"children":4746},{"style":462},[4747],{"type":36,"value":4748}," notificationService",{"type":30,"tag":439,"props":4750,"children":4751},{"style":922},[4752],{"type":36,"value":3082},{"type":30,"tag":439,"props":4754,"children":4755},{"class":441,"line":1663},[4756],{"type":30,"tag":439,"props":4757,"children":4758},{"emptyLinePlaceholder":13},[4759],{"type":36,"value":514},{"type":30,"tag":439,"props":4761,"children":4762},{"class":441,"line":1672},[4763,4768,4772],{"type":30,"tag":439,"props":4764,"children":4765},{"style":456},[4766],{"type":36,"value":4767},"    public",{"type":30,"tag":439,"props":4769,"children":4770},{"style":1105},[4771],{"type":36,"value":4691},{"type":30,"tag":439,"props":4773,"children":4774},{"style":922},[4775],{"type":36,"value":3318},{"type":30,"tag":439,"props":4777,"children":4778},{"class":441,"line":1688},[4779,4784,4788],{"type":30,"tag":439,"props":4780,"children":4781},{"style":4558},[4782],{"type":36,"value":4783},"            OrderRepository",{"type":30,"tag":439,"props":4785,"children":4786},{"style":992},[4787],{"type":36,"value":4724},{"type":30,"tag":439,"props":4789,"children":4790},{"style":922},[4791],{"type":36,"value":3348},{"type":30,"tag":439,"props":4793,"children":4794},{"class":441,"line":1747},[4795,4800,4804,4808],{"type":30,"tag":439,"props":4796,"children":4797},{"style":4558},[4798],{"type":36,"value":4799},"            NotificationService",{"type":30,"tag":439,"props":4801,"children":4802},{"style":992},[4803],{"type":36,"value":4748},{"type":30,"tag":439,"props":4805,"children":4806},{"style":922},[4807],{"type":36,"value":1030},{"type":30,"tag":439,"props":4809,"children":4810},{"style":922},[4811],{"type":36,"value":3022},{"type":30,"tag":439,"props":4813,"children":4814},{"class":441,"line":1773},[4815,4820,4824,4829,4833,4837],{"type":30,"tag":439,"props":4816,"children":4817},{"style":3474},[4818],{"type":36,"value":4819},"        this",{"type":30,"tag":439,"props":4821,"children":4822},{"style":922},[4823],{"type":36,"value":969},{"type":30,"tag":439,"props":4825,"children":4826},{"style":462},[4827],{"type":36,"value":4828},"orderRepository ",{"type":30,"tag":439,"props":4830,"children":4831},{"style":487},[4832],{"type":36,"value":979},{"type":30,"tag":439,"props":4834,"children":4835},{"style":462},[4836],{"type":36,"value":4724},{"type":30,"tag":439,"props":4838,"children":4839},{"style":922},[4840],{"type":36,"value":3082},{"type":30,"tag":439,"props":4842,"children":4843},{"class":441,"line":1799},[4844,4848,4852,4857,4861,4865],{"type":30,"tag":439,"props":4845,"children":4846},{"style":3474},[4847],{"type":36,"value":4819},{"type":30,"tag":439,"props":4849,"children":4850},{"style":922},[4851],{"type":36,"value":969},{"type":30,"tag":439,"props":4853,"children":4854},{"style":462},[4855],{"type":36,"value":4856},"notificationService ",{"type":30,"tag":439,"props":4858,"children":4859},{"style":487},[4860],{"type":36,"value":979},{"type":30,"tag":439,"props":4862,"children":4863},{"style":462},[4864],{"type":36,"value":4748},{"type":30,"tag":439,"props":4866,"children":4867},{"style":922},[4868],{"type":36,"value":3082},{"type":30,"tag":439,"props":4870,"children":4871},{"class":441,"line":1807},[4872],{"type":30,"tag":439,"props":4873,"children":4874},{"style":922},[4875],{"type":36,"value":3843},{"type":30,"tag":439,"props":4877,"children":4878},{"class":441,"line":1859},[4879],{"type":30,"tag":439,"props":4880,"children":4881},{"emptyLinePlaceholder":13},[4882],{"type":36,"value":514},{"type":30,"tag":439,"props":4884,"children":4885},{"class":441,"line":1904},[4886,4890,4895,4900,4904,4908,4912,4916],{"type":30,"tag":439,"props":4887,"children":4888},{"style":456},[4889],{"type":36,"value":4767},{"type":30,"tag":439,"props":4891,"children":4892},{"style":456},[4893],{"type":36,"value":4894}," void",{"type":30,"tag":439,"props":4896,"children":4897},{"style":1105},[4898],{"type":36,"value":4899}," fulfillOrder",{"type":30,"tag":439,"props":4901,"children":4902},{"style":922},[4903],{"type":36,"value":944},{"type":30,"tag":439,"props":4905,"children":4906},{"style":4558},[4907],{"type":36,"value":4561},{"type":30,"tag":439,"props":4909,"children":4910},{"style":992},[4911],{"type":36,"value":3677},{"type":30,"tag":439,"props":4913,"children":4914},{"style":922},[4915],{"type":36,"value":1030},{"type":30,"tag":439,"props":4917,"children":4918},{"style":922},[4919],{"type":36,"value":3022},{"type":30,"tag":439,"props":4921,"children":4922},{"class":441,"line":1965},[4923,4928,4933,4937,4941,4945,4949,4953,4957],{"type":30,"tag":439,"props":4924,"children":4925},{"style":4558},[4926],{"type":36,"value":4927},"        Order",{"type":30,"tag":439,"props":4929,"children":4930},{"style":462},[4931],{"type":36,"value":4932}," order ",{"type":30,"tag":439,"props":4934,"children":4935},{"style":487},[4936],{"type":36,"value":979},{"type":30,"tag":439,"props":4938,"children":4939},{"style":462},[4940],{"type":36,"value":4724},{"type":30,"tag":439,"props":4942,"children":4943},{"style":922},[4944],{"type":36,"value":969},{"type":30,"tag":439,"props":4946,"children":4947},{"style":1105},[4948],{"type":36,"value":3826},{"type":30,"tag":439,"props":4950,"children":4951},{"style":922},[4952],{"type":36,"value":944},{"type":30,"tag":439,"props":4954,"children":4955},{"style":462},[4956],{"type":36,"value":3099},{"type":30,"tag":439,"props":4958,"children":4959},{"style":922},[4960],{"type":36,"value":1180},{"type":30,"tag":439,"props":4962,"children":4963},{"class":441,"line":1977},[4964,4969,4974,4979,4983,4987,4992,4996,5000],{"type":30,"tag":439,"props":4965,"children":4966},{"style":922},[4967],{"type":36,"value":4968},"            .",{"type":30,"tag":439,"props":4970,"children":4971},{"style":1105},[4972],{"type":36,"value":4973},"orElseThrow",{"type":30,"tag":439,"props":4975,"children":4976},{"style":922},[4977],{"type":36,"value":4978},"(()",{"type":30,"tag":439,"props":4980,"children":4981},{"style":456},[4982],{"type":36,"value":1519},{"type":30,"tag":439,"props":4984,"children":4985},{"style":456},[4986],{"type":36,"value":3627},{"type":30,"tag":439,"props":4988,"children":4989},{"style":1105},[4990],{"type":36,"value":4991}," OrderNotFoundException",{"type":30,"tag":439,"props":4993,"children":4994},{"style":922},[4995],{"type":36,"value":944},{"type":30,"tag":439,"props":4997,"children":4998},{"style":462},[4999],{"type":36,"value":3099},{"type":30,"tag":439,"props":5001,"children":5002},{"style":922},[5003],{"type":36,"value":5004},"));\n",{"type":30,"tag":439,"props":5006,"children":5007},{"class":441,"line":1985},[5008],{"type":30,"tag":439,"props":5009,"children":5010},{"emptyLinePlaceholder":13},[5011],{"type":36,"value":514},{"type":30,"tag":439,"props":5013,"children":5014},{"class":441,"line":1994},[5015,5020,5024,5029,5033,5038,5042,5047],{"type":30,"tag":439,"props":5016,"children":5017},{"style":462},[5018],{"type":36,"value":5019},"        order",{"type":30,"tag":439,"props":5021,"children":5022},{"style":922},[5023],{"type":36,"value":969},{"type":30,"tag":439,"props":5025,"children":5026},{"style":1105},[5027],{"type":36,"value":5028},"setStatus",{"type":30,"tag":439,"props":5030,"children":5031},{"style":922},[5032],{"type":36,"value":944},{"type":30,"tag":439,"props":5034,"children":5035},{"style":462},[5036],{"type":36,"value":5037},"OrderStatus",{"type":30,"tag":439,"props":5039,"children":5040},{"style":922},[5041],{"type":36,"value":969},{"type":30,"tag":439,"props":5043,"children":5044},{"style":462},[5045],{"type":36,"value":5046},"FULFILLING",{"type":30,"tag":439,"props":5048,"children":5049},{"style":922},[5050],{"type":36,"value":4584},{"type":30,"tag":439,"props":5052,"children":5053},{"class":441,"line":2019},[5054,5059,5063,5067,5071,5076],{"type":30,"tag":439,"props":5055,"children":5056},{"style":462},[5057],{"type":36,"value":5058},"        orderRepository",{"type":30,"tag":439,"props":5060,"children":5061},{"style":922},[5062],{"type":36,"value":969},{"type":30,"tag":439,"props":5064,"children":5065},{"style":1105},[5066],{"type":36,"value":1166},{"type":30,"tag":439,"props":5068,"children":5069},{"style":922},[5070],{"type":36,"value":944},{"type":30,"tag":439,"props":5072,"children":5073},{"style":462},[5074],{"type":36,"value":5075},"order",{"type":30,"tag":439,"props":5077,"children":5078},{"style":922},[5079],{"type":36,"value":4584},{"type":30,"tag":439,"props":5081,"children":5082},{"class":441,"line":2071},[5083],{"type":30,"tag":439,"props":5084,"children":5085},{"emptyLinePlaceholder":13},[5086],{"type":36,"value":514},{"type":30,"tag":439,"props":5088,"children":5089},{"class":441,"line":2121},[5090,5095,5099,5104,5108,5112,5116,5121,5126,5130],{"type":30,"tag":439,"props":5091,"children":5092},{"style":462},[5093],{"type":36,"value":5094},"        notificationService",{"type":30,"tag":439,"props":5096,"children":5097},{"style":922},[5098],{"type":36,"value":969},{"type":30,"tag":439,"props":5100,"children":5101},{"style":1105},[5102],{"type":36,"value":5103},"sendOrderConfirmation",{"type":30,"tag":439,"props":5105,"children":5106},{"style":922},[5107],{"type":36,"value":944},{"type":30,"tag":439,"props":5109,"children":5110},{"style":462},[5111],{"type":36,"value":5075},{"type":30,"tag":439,"props":5113,"children":5114},{"style":922},[5115],{"type":36,"value":969},{"type":30,"tag":439,"props":5117,"children":5118},{"style":1105},[5119],{"type":36,"value":5120},"getUserId",{"type":30,"tag":439,"props":5122,"children":5123},{"style":922},[5124],{"type":36,"value":5125},"(),",{"type":30,"tag":439,"props":5127,"children":5128},{"style":462},[5129],{"type":36,"value":3677},{"type":30,"tag":439,"props":5131,"children":5132},{"style":922},[5133],{"type":36,"value":4584},{"type":30,"tag":439,"props":5135,"children":5136},{"class":441,"line":2129},[5137],{"type":30,"tag":439,"props":5138,"children":5139},{"style":922},[5140],{"type":36,"value":3843},{"type":30,"tag":439,"props":5142,"children":5143},{"class":441,"line":820},[5144],{"type":30,"tag":439,"props":5145,"children":5146},{"style":922},[5147],{"type":36,"value":3156},{"type":30,"tag":439,"props":5149,"children":5150},{"class":441,"line":2221},[5151],{"type":30,"tag":439,"props":5152,"children":5153},{"emptyLinePlaceholder":13},[5154],{"type":36,"value":514},{"type":30,"tag":439,"props":5156,"children":5157},{"class":441,"line":2318},[5158],{"type":30,"tag":439,"props":5159,"children":5160},{"style":446},[5161],{"type":36,"value":5162},"// Implémentation de test\n",{"type":30,"tag":439,"props":5164,"children":5165},{"class":441,"line":2326},[5166,5170],{"type":30,"tag":439,"props":5167,"children":5168},{"style":4665},[5169],{"type":36,"value":4668},{"type":30,"tag":439,"props":5171,"children":5172},{"style":4671},[5173],{"type":36,"value":4674},{"type":30,"tag":439,"props":5175,"children":5176},{"class":441,"line":2335},[5177,5181,5186,5190,5195],{"type":30,"tag":439,"props":5178,"children":5179},{"style":4665},[5180],{"type":36,"value":4668},{"type":30,"tag":439,"props":5182,"children":5183},{"style":4671},[5184],{"type":36,"value":5185},"Profile",{"type":30,"tag":439,"props":5187,"children":5188},{"style":922},[5189],{"type":36,"value":944},{"type":30,"tag":439,"props":5191,"children":5192},{"style":1002},[5193],{"type":36,"value":5194},"\"test\"",{"type":30,"tag":439,"props":5196,"children":5197},{"style":922},[5198],{"type":36,"value":1180},{"type":30,"tag":439,"props":5200,"children":5201},{"class":441,"line":11},[5202,5206,5210,5215,5220,5224],{"type":30,"tag":439,"props":5203,"children":5204},{"style":456},[5205],{"type":36,"value":4524},{"type":30,"tag":439,"props":5207,"children":5208},{"style":456},[5209],{"type":36,"value":4686},{"type":30,"tag":439,"props":5211,"children":5212},{"style":916},[5213],{"type":36,"value":5214}," MockNotificationService",{"type":30,"tag":439,"props":5216,"children":5217},{"style":456},[5218],{"type":36,"value":5219}," implements",{"type":30,"tag":439,"props":5221,"children":5222},{"style":916},[5223],{"type":36,"value":4534},{"type":30,"tag":439,"props":5225,"children":5226},{"style":922},[5227],{"type":36,"value":3022},{"type":30,"tag":439,"props":5229,"children":5230},{"class":441,"line":2382},[5231],{"type":30,"tag":439,"props":5232,"children":5233},{"emptyLinePlaceholder":13},[5234],{"type":36,"value":514},{"type":30,"tag":439,"props":5236,"children":5237},{"class":441,"line":2439},[5238,5242,5246,5251,5255,5259,5263,5268,5272,5276,5281],{"type":30,"tag":439,"props":5239,"children":5240},{"style":456},[5241],{"type":36,"value":4710},{"type":30,"tag":439,"props":5243,"children":5244},{"style":456},[5245],{"type":36,"value":4715},{"type":30,"tag":439,"props":5247,"children":5248},{"style":4558},[5249],{"type":36,"value":5250}," List",{"type":30,"tag":439,"props":5252,"children":5253},{"style":922},[5254],{"type":36,"value":3067},{"type":30,"tag":439,"props":5256,"children":5257},{"style":456},[5258],{"type":36,"value":4561},{"type":30,"tag":439,"props":5260,"children":5261},{"style":922},[5262],{"type":36,"value":3077},{"type":30,"tag":439,"props":5264,"children":5265},{"style":462},[5266],{"type":36,"value":5267}," sentNotifications ",{"type":30,"tag":439,"props":5269,"children":5270},{"style":487},[5271],{"type":36,"value":979},{"type":30,"tag":439,"props":5273,"children":5274},{"style":456},[5275],{"type":36,"value":3627},{"type":30,"tag":439,"props":5277,"children":5278},{"style":4558},[5279],{"type":36,"value":5280}," ArrayList",{"type":30,"tag":439,"props":5282,"children":5283},{"style":922},[5284],{"type":36,"value":5285},"\u003C>();\n",{"type":30,"tag":439,"props":5287,"children":5288},{"class":441,"line":3853},[5289],{"type":30,"tag":439,"props":5290,"children":5291},{"emptyLinePlaceholder":13},[5292],{"type":36,"value":514},{"type":30,"tag":439,"props":5294,"children":5295},{"class":441,"line":3861},[5296,5301],{"type":30,"tag":439,"props":5297,"children":5298},{"style":4665},[5299],{"type":36,"value":5300},"    @",{"type":30,"tag":439,"props":5302,"children":5303},{"style":4671},[5304],{"type":36,"value":5305},"Override\n",{"type":30,"tag":439,"props":5307,"children":5308},{"class":441,"line":3870},[5309,5313,5317,5321,5325,5329,5333,5337,5341,5345,5349],{"type":30,"tag":439,"props":5310,"children":5311},{"style":456},[5312],{"type":36,"value":4767},{"type":30,"tag":439,"props":5314,"children":5315},{"style":456},[5316],{"type":36,"value":4894},{"type":30,"tag":439,"props":5318,"children":5319},{"style":1105},[5320],{"type":36,"value":4551},{"type":30,"tag":439,"props":5322,"children":5323},{"style":922},[5324],{"type":36,"value":944},{"type":30,"tag":439,"props":5326,"children":5327},{"style":4558},[5328],{"type":36,"value":4561},{"type":30,"tag":439,"props":5330,"children":5331},{"style":992},[5332],{"type":36,"value":4566},{"type":30,"tag":439,"props":5334,"children":5335},{"style":922},[5336],{"type":36,"value":1010},{"type":30,"tag":439,"props":5338,"children":5339},{"style":4558},[5340],{"type":36,"value":4575},{"type":30,"tag":439,"props":5342,"children":5343},{"style":992},[5344],{"type":36,"value":3677},{"type":30,"tag":439,"props":5346,"children":5347},{"style":922},[5348],{"type":36,"value":1030},{"type":30,"tag":439,"props":5350,"children":5351},{"style":922},[5352],{"type":36,"value":3022},{"type":30,"tag":439,"props":5354,"children":5355},{"class":441,"line":3906},[5356,5361,5365,5370,5374,5379,5384,5389,5394,5399,5403,5407],{"type":30,"tag":439,"props":5357,"children":5358},{"style":462},[5359],{"type":36,"value":5360},"        sentNotifications",{"type":30,"tag":439,"props":5362,"children":5363},{"style":922},[5364],{"type":36,"value":969},{"type":30,"tag":439,"props":5366,"children":5367},{"style":1105},[5368],{"type":36,"value":5369},"add",{"type":30,"tag":439,"props":5371,"children":5372},{"style":922},[5373],{"type":36,"value":944},{"type":30,"tag":439,"props":5375,"children":5376},{"style":1002},[5377],{"type":36,"value":5378},"\"confirmation:\"",{"type":30,"tag":439,"props":5380,"children":5381},{"style":487},[5382],{"type":36,"value":5383}," +",{"type":30,"tag":439,"props":5385,"children":5386},{"style":462},[5387],{"type":36,"value":5388}," userId ",{"type":30,"tag":439,"props":5390,"children":5391},{"style":487},[5392],{"type":36,"value":5393},"+",{"type":30,"tag":439,"props":5395,"children":5396},{"style":1002},[5397],{"type":36,"value":5398}," \":\"",{"type":30,"tag":439,"props":5400,"children":5401},{"style":487},[5402],{"type":36,"value":5383},{"type":30,"tag":439,"props":5404,"children":5405},{"style":462},[5406],{"type":36,"value":3677},{"type":30,"tag":439,"props":5408,"children":5409},{"style":922},[5410],{"type":36,"value":4584},{"type":30,"tag":439,"props":5412,"children":5413},{"class":441,"line":3945},[5414],{"type":30,"tag":439,"props":5415,"children":5416},{"style":922},[5417],{"type":36,"value":3843},{"type":30,"tag":439,"props":5419,"children":5420},{"class":441,"line":3974},[5421],{"type":30,"tag":439,"props":5422,"children":5423},{"style":922},[5424],{"type":36,"value":3156},{"type":30,"tag":38,"props":5426,"children":5427},{},[5428,5433,5435,5441],{"type":30,"tag":44,"props":5429,"children":5430},{},[5431],{"type":36,"value":5432},"L'avantage Spring :",{"type":36,"value":5434}," ",{"type":30,"tag":98,"props":5436,"children":5438},{"className":5437},[],[5439],{"type":36,"value":5440},"@Profile(\"test\")",{"type":36,"value":5442}," permet d'utiliser automatiquement le mock en environnement de test sans modifier le code métier. Le framework gère le wiring, le code métier reste pur.",{"type":30,"tag":67,"props":5444,"children":5445},{},[],{"type":30,"tag":71,"props":5447,"children":5449},{"id":5448},"quand-appliquer-le-dip-et-quand-ne-pas-le-faire",[5450],{"type":36,"value":5451},"Quand appliquer le DIP, et quand ne pas le faire",{"type":30,"tag":38,"props":5453,"children":5454},{},[5455],{"type":30,"tag":44,"props":5456,"children":5457},{},[5458],{"type":36,"value":5459},"Appliquer le DIP :",{"type":30,"tag":620,"props":5461,"children":5462},{},[5463,5468,5473],{"type":30,"tag":624,"props":5464,"children":5465},{},[5466],{"type":36,"value":5467},"Toute dépendance vers un système externe (base de données, API tierce, service d'email, service de paiement, file de message)",{"type":30,"tag":624,"props":5469,"children":5470},{},[5471],{"type":36,"value":5472},"Toute dépendance vers une infrastructure susceptible de changer",{"type":30,"tag":624,"props":5474,"children":5475},{},[5476],{"type":36,"value":5477},"Tout code que vous voulez unit-tester sans infrastructure",{"type":30,"tag":38,"props":5479,"children":5480},{},[5481],{"type":30,"tag":44,"props":5482,"children":5483},{},[5484],{"type":36,"value":5485},"Ne pas appliquer le DIP mécaniquement :",{"type":30,"tag":620,"props":5487,"children":5488},{},[5489,5494,5499],{"type":30,"tag":624,"props":5490,"children":5491},{},[5492],{"type":36,"value":5493},"Les entités et value objects du domaine (Order, User, Money) : pas de logique d'infrastructure",{"type":30,"tag":624,"props":5495,"children":5496},{},[5497],{"type":36,"value":5498},"Les utilitaires purs (calculs mathématiques, formatage de dates) : pas d'effet de bord",{"type":30,"tag":624,"props":5500,"children":5501},{},[5502],{"type":36,"value":5503},"Les dépendances stables et peu susceptibles de changer dans des petits projets",{"type":30,"tag":38,"props":5505,"children":5506},{},[5507,5512,5514,5520],{"type":30,"tag":44,"props":5508,"children":5509},{},[5510],{"type":36,"value":5511},"Le signal",{"type":36,"value":5513}," : si écrire un test unitaire pour votre classe nécessite de démarrer une base de données, une queue de messages, ou de configurer un service externe, appliquez le DIP. Cette question est aussi centrale dans les ",{"type":30,"tag":142,"props":5515,"children":5517},{"href":5516},"/fr/dette-technique/tests-integration-legacy-pieges",[5518],{"type":36,"value":5519},"tests d'intégration sur du code legacy",{"type":36,"value":5521}," : l'absence de DIP est souvent ce qui rend ces tests si difficiles et si fragiles à mettre en place.",{"type":30,"tag":67,"props":5523,"children":5524},{},[],{"type":30,"tag":71,"props":5526,"children":5528},{"id":5527},"faq-sur-le-dependency-inversion-principle",[5529],{"type":36,"value":5530},"FAQ sur le Dependency Inversion Principle",{"type":30,"tag":714,"props":5532,"children":5533},{},[5534,5539],{"type":30,"tag":718,"props":5535,"children":5536},{},[5537],{"type":36,"value":5538},"1. Quelle est la différence entre DIP et Dependency Injection ?",{"type":30,"tag":38,"props":5540,"children":5541},{},[5542],{"type":36,"value":5543},"Le DIP est un principe architectural : les modules doivent dépendre d'abstractions. La Dependency Injection (DI) est une technique d'implémentation du DIP : les dépendances sont fournies de l'extérieur plutôt que créées à l'intérieur. On peut respecter le DIP sans DI (ex : factory pattern). On peut utiliser la DI sans respecter le DIP (ex : injecter une classe concrète sans interface). En pratique, les deux vont généralement ensemble.",{"type":30,"tag":714,"props":5545,"children":5546},{},[5547,5552],{"type":30,"tag":718,"props":5548,"children":5549},{},[5550],{"type":36,"value":5551},"2. Les containers DI (Spring, .NET DI, Angular) font-ils le travail à notre place ?",{"type":30,"tag":38,"props":5553,"children":5554},{},[5555],{"type":36,"value":5556},"Ils automatisent le wiring (qui injecte quoi), mais ne créent pas les abstractions à notre place. Spring injecte les classes concrètes si vous ne créez pas d'interfaces. La discipline architecturale de créer des interfaces pour les dépendances d'infrastructure reste une décision de l'équipe. Le framework ne peut pas la prendre à votre place.",{"type":30,"tag":714,"props":5558,"children":5559},{},[5560,5565],{"type":30,"tag":718,"props":5561,"children":5562},{},[5563],{"type":36,"value":5564},"3. Le DIP ne crée-t-il pas trop d'abstractions ?",{"type":30,"tag":38,"props":5566,"children":5567},{},[5568],{"type":36,"value":5569},"Oui, si mal appliqué. Créer une interface pour chaque classe, même les plus stables, est du sur-engineering. La règle que Martin Fowler formule clairement : créer une abstraction quand il y a au moins 2 implémentations possibles (production + test, provider A + provider B) ou quand la dépendance est vers un système externe. \"Pas d'abstraction prématurée\" s'applique au DIP comme à tout principe.",{"type":30,"tag":714,"props":5571,"children":5572},{},[5573,5578],{"type":30,"tag":718,"props":5574,"children":5575},{},[5576],{"type":36,"value":5577},"4. Comment introduire le DIP progressivement dans un codebase existant ?",{"type":30,"tag":38,"props":5579,"children":5580},{},[5581,5583,5589],{"type":36,"value":5582},"Le strangler fig pattern appliqué au DIP : ne pas refactorer tout le code existant d'un coup. À chaque nouvelle fonctionnalité ou bug fix qui touche une classe avec dépendances directes, extraire l'interface et introduire l'injection. Après 6 mois de cette discipline, les zones les plus actives du codebase respectent le DIP. Les zones stables peuvent rester dans l'état existant si elles ne causent pas de problèmes. Dans un contexte de ",{"type":30,"tag":142,"props":5584,"children":5586},{"href":5585},"/fr/dette-technique/legacy-code-evaluer-risque",[5587],{"type":36,"value":5588},"code legacy fortement couplé",{"type":36,"value":5590},", cette approche progressive est souvent la seule viable sans bloquer les livraisons.",{"type":30,"tag":714,"props":5592,"children":5593},{},[5594,5599],{"type":30,"tag":718,"props":5595,"children":5596},{},[5597],{"type":36,"value":5598},"5. Le DIP s'applique-t-il aux frontends (React, Vue) ?",{"type":30,"tag":38,"props":5600,"children":5601},{},[5602,5604,5610,5612,5618,5620,5626],{"type":36,"value":5603},"Oui. En React : les composants ne doivent pas appeler directement ",{"type":30,"tag":98,"props":5605,"children":5607},{"className":5606},[],[5608],{"type":36,"value":5609},"fetch()",{"type":36,"value":5611}," ou ",{"type":30,"tag":98,"props":5613,"children":5615},{"className":5614},[],[5616],{"type":36,"value":5617},"axios",{"type":36,"value":5619}," : ils doivent dépendre d'une abstraction (un service, un hook custom, un context) qui peut être mockée dans les tests. ",{"type":30,"tag":98,"props":5621,"children":5623},{"className":5622},[],[5624],{"type":36,"value":5625},"useOrderService()",{"type":36,"value":5627}," hook qui cache l'implémentation HTTP, facilement mockable dans les tests. Le DIP s'applique partout où il y a des effets de bord, frontend inclus.",{"type":30,"tag":67,"props":5629,"children":5630},{},[],{"type":30,"tag":225,"props":5632,"children":5633},{"cta":785,"href":786,"title":787,"type":788},[5634],{"type":30,"tag":38,"props":5635,"children":5636},{},[5637],{"type":36,"value":5638},"L'Engineering Maturity Self-Assessment couvre le domaine Craft & Conception : évaluez votre niveau sur les principes SOLID, le couplage, et la testabilité. Obtenez un score de maturité et des recommandations concrètes en 10 minutes.",{"type":30,"tag":796,"props":5640,"children":5641},{},[5642],{"type":36,"value":800},{"title":8,"searchDepth":452,"depth":452,"links":5644},[5645,5646,5647,5648,5649,5650,5651],{"id":860,"depth":452,"text":863},{"id":1321,"depth":452,"text":1324},{"id":1387,"depth":452,"text":1390},{"id":2985,"depth":452,"text":2988},{"id":4496,"depth":452,"text":4499},{"id":5448,"depth":452,"text":5451},{"id":5527,"depth":452,"text":5530},"content:fr:architecture-craft:dependency-inversion-pratique.md","fr/architecture-craft/dependency-inversion-pratique.md","fr/architecture-craft/dependency-inversion-pratique",{"_path":200,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":5656,"description":5657,"id":1994,"date":5658,"listed":13,"nocomments":7,"hidden":7,"categories":5659,"tags":5660,"--cover":5664,"readingTime":5665,"body":5670,"_type":810,"_id":7248,"_source":812,"_file":7249,"_stem":7250,"_extension":815},"Patterns de résilience : circuit breaker, retry, timeout","Les 3 patterns de résilience fondamentaux que tout système distribué doit implémenter. Sans eux, la défaillance d'un service cascade et fait tomber toute l'architecture.","2026-03-02",[6],[5661,5662,5663,18],"Résilience","Circuit Breaker","Retry","covers/articles/patterns-resilience-circuit-breaker.jpg",{"text":5666,"minutes":5667,"time":5668,"words":5669},"7 min read",6.985,419100,1397,{"type":27,"children":5671,"toc":7239},[5672,5677,5682,5687,5692,5697,5700,5706,5717,5722,5734,5737,5743,5748,5758,5766,5784,5995,6005,6008,6014,6019,6029,6387,6395,6408,6417,6420,6426,6431,6439,6449,6459,6469,6789,7040,7043,7049,7059,7069,7087,7092,7095,7101,7135,7140,7143,7149,7169,7182,7203,7224,7227,7235],{"type":30,"tag":31,"props":5673,"children":5675},{"id":5674},"patterns-de-résilience-circuit-breaker-retry-timeout",[5676],{"type":36,"value":5656},{"type":30,"tag":38,"props":5678,"children":5679},{},[5680],{"type":36,"value":5681},"J'accompagnais un client dans le secteur du retail en ligne (25 développeurs). En six mois, trois incidents P1, à chaque fois pendant des pics de trafic. Le service de recommandations devenait lent, les threads du service produit s'accumulaient en attente, le pool de connexions s'épuisait, et le service produit tombait à son tour. La homepage devenait indisponible.",{"type":30,"tag":38,"props":5683,"children":5684},{},[5685],{"type":36,"value":5686},"Le service de recommandations avait une excuse valable : les pics de charge. Le service produit n'en avait pas. Il n'avait simplement aucun mécanisme pour se protéger d'un voisin défaillant.",{"type":30,"tag":38,"props":5688,"children":5689},{},[5690],{"type":36,"value":5691},"Deux heures de développement pour ajouter un timeout. Deux jours pour le circuit breaker. Zéro incident P1 lié aux recommandations depuis lors. Les recommandations continuent de tomber lors des pics, mais ça reste isolé.",{"type":30,"tag":38,"props":5693,"children":5694},{},[5695],{"type":36,"value":5696},"C'est ce que les patterns de résilience font : ils n'éliminent pas les pannes, ils empêchent qu'une panne locale devienne un effondrement global.",{"type":30,"tag":67,"props":5698,"children":5699},{},[],{"type":30,"tag":71,"props":5701,"children":5703},{"id":5702},"la-cascade-failure-pourquoi-les-systèmes-distribués-seffondrent-en-bloc",[5704],{"type":36,"value":5705},"La cascade failure : pourquoi les systèmes distribués s'effondrent en bloc",{"type":30,"tag":38,"props":5707,"children":5708},{},[5709,5711,5715],{"type":36,"value":5710},"Sans mécanisme de protection, les appels vers des services externes dans un système distribué ont une propriété dangereuse : si le service cible ralentit, les appelants attendent, leurs ressources se consomment, et ils deviennent eux-mêmes défaillants. Ce phénomène est aggravé par le ",{"type":30,"tag":142,"props":5712,"children":5713},{"href":218},[5714],{"type":36,"value":221},{"type":36,"value":5716}," entre services, qui crée des dépendances implicites d'ordre ou de timing difficiles à détecter jusqu'en production.",{"type":30,"tag":38,"props":5718,"children":5719},{},[5720],{"type":36,"value":5721},"Michael Nygard a documenté ce pattern en détail dans \"Release It!\" (2007), l'ouvrage de référence sur la résilience des systèmes en production. Sa conclusion : la stabilité ne se construit pas par l'optimisme (espérer que les services tiers restent disponibles), mais par la conception défensive (prévoir leur défaillance et la gérer).",{"type":30,"tag":38,"props":5723,"children":5724},{},[5725,5727,5732],{"type":36,"value":5726},"Selon Gartner, une heure de downtime coûte en moyenne ",{"type":30,"tag":44,"props":5728,"children":5729},{},[5730],{"type":36,"value":5731},"300 000€",{"type":36,"value":5733}," pour une application business critique. Les 3 patterns décrits ici sont des assurances contre ce coût.",{"type":30,"tag":67,"props":5735,"children":5736},{},[],{"type":30,"tag":71,"props":5738,"children":5740},{"id":5739},"pattern-1-timeout",[5741],{"type":36,"value":5742},"Pattern 1 : Timeout",{"type":30,"tag":38,"props":5744,"children":5745},{},[5746],{"type":36,"value":5747},"Le timeout est le premier niveau de protection, et le plus souvent absent. Un appel vers un service externe sans timeout peut attendre indéfiniment, bloquant les ressources de l'appelant.",{"type":30,"tag":38,"props":5749,"children":5750},{},[5751,5756],{"type":30,"tag":44,"props":5752,"children":5753},{},[5754],{"type":36,"value":5755},"Principe :",{"type":36,"value":5757}," tout appel vers un service externe (HTTP, base de données, cache, message broker) doit avoir un timeout explicite défini dans le code. Si le service ne répond pas dans ce délai, l'appel est considéré comme échoué et les ressources sont libérées.",{"type":30,"tag":38,"props":5759,"children":5760},{},[5761],{"type":30,"tag":44,"props":5762,"children":5763},{},[5764],{"type":36,"value":5765},"Comment définir la valeur du timeout :",{"type":30,"tag":620,"props":5767,"children":5768},{},[5769,5774,5779],{"type":30,"tag":624,"props":5770,"children":5771},{},[5772],{"type":36,"value":5773},"Mesurer le p99 (99ème percentile) de la latence du service cible sur les 30 derniers jours",{"type":30,"tag":624,"props":5775,"children":5776},{},[5777],{"type":36,"value":5778},"Multiplier par 2 à 3 pour le timeout",{"type":30,"tag":624,"props":5780,"children":5781},{},[5782],{"type":36,"value":5783},"Ne jamais utiliser la valeur par défaut du client HTTP (souvent 30 secondes à 2 minutes, beaucoup trop long)",{"type":30,"tag":185,"props":5785,"children":5787},{"className":4504,"code":5786,"language":4503,"meta":8,"style":8},"// Exemple avec OkHttp (Java)\nOkHttpClient client = new OkHttpClient.Builder()\n    .connectTimeout(2, TimeUnit.SECONDS)  // temps max pour établir la connexion\n    .readTimeout(5, TimeUnit.SECONDS)     // temps max pour recevoir la réponse\n    .writeTimeout(5, TimeUnit.SECONDS)    // temps max pour envoyer la requête\n    .build();\n",[5788],{"type":30,"tag":98,"props":5789,"children":5790},{"__ignoreMap":8},[5791,5799,5838,5887,5933,5978],{"type":30,"tag":439,"props":5792,"children":5793},{"class":441,"line":442},[5794],{"type":30,"tag":439,"props":5795,"children":5796},{"style":446},[5797],{"type":36,"value":5798},"// Exemple avec OkHttp (Java)\n",{"type":30,"tag":439,"props":5800,"children":5801},{"class":441,"line":452},[5802,5807,5812,5816,5820,5825,5829,5834],{"type":30,"tag":439,"props":5803,"children":5804},{"style":4558},[5805],{"type":36,"value":5806},"OkHttpClient",{"type":30,"tag":439,"props":5808,"children":5809},{"style":462},[5810],{"type":36,"value":5811}," client ",{"type":30,"tag":439,"props":5813,"children":5814},{"style":487},[5815],{"type":36,"value":979},{"type":30,"tag":439,"props":5817,"children":5818},{"style":456},[5819],{"type":36,"value":3627},{"type":30,"tag":439,"props":5821,"children":5822},{"style":462},[5823],{"type":36,"value":5824}," OkHttpClient",{"type":30,"tag":439,"props":5826,"children":5827},{"style":922},[5828],{"type":36,"value":969},{"type":30,"tag":439,"props":5830,"children":5831},{"style":1105},[5832],{"type":36,"value":5833},"Builder",{"type":30,"tag":439,"props":5835,"children":5836},{"style":922},[5837],{"type":36,"value":2753},{"type":30,"tag":439,"props":5839,"children":5840},{"class":441,"line":478},[5841,5846,5851,5855,5860,5864,5869,5873,5878,5882],{"type":30,"tag":439,"props":5842,"children":5843},{"style":922},[5844],{"type":36,"value":5845},"    .",{"type":30,"tag":439,"props":5847,"children":5848},{"style":1105},[5849],{"type":36,"value":5850},"connectTimeout",{"type":30,"tag":439,"props":5852,"children":5853},{"style":922},[5854],{"type":36,"value":944},{"type":30,"tag":439,"props":5856,"children":5857},{"style":1022},[5858],{"type":36,"value":5859},"2",{"type":30,"tag":439,"props":5861,"children":5862},{"style":922},[5863],{"type":36,"value":1010},{"type":30,"tag":439,"props":5865,"children":5866},{"style":462},[5867],{"type":36,"value":5868}," TimeUnit",{"type":30,"tag":439,"props":5870,"children":5871},{"style":922},[5872],{"type":36,"value":969},{"type":30,"tag":439,"props":5874,"children":5875},{"style":462},[5876],{"type":36,"value":5877},"SECONDS",{"type":30,"tag":439,"props":5879,"children":5880},{"style":922},[5881],{"type":36,"value":1030},{"type":30,"tag":439,"props":5883,"children":5884},{"style":446},[5885],{"type":36,"value":5886},"  // temps max pour établir la connexion\n",{"type":30,"tag":439,"props":5888,"children":5889},{"class":441,"line":508},[5890,5894,5899,5903,5908,5912,5916,5920,5924,5928],{"type":30,"tag":439,"props":5891,"children":5892},{"style":922},[5893],{"type":36,"value":5845},{"type":30,"tag":439,"props":5895,"children":5896},{"style":1105},[5897],{"type":36,"value":5898},"readTimeout",{"type":30,"tag":439,"props":5900,"children":5901},{"style":922},[5902],{"type":36,"value":944},{"type":30,"tag":439,"props":5904,"children":5905},{"style":1022},[5906],{"type":36,"value":5907},"5",{"type":30,"tag":439,"props":5909,"children":5910},{"style":922},[5911],{"type":36,"value":1010},{"type":30,"tag":439,"props":5913,"children":5914},{"style":462},[5915],{"type":36,"value":5868},{"type":30,"tag":439,"props":5917,"children":5918},{"style":922},[5919],{"type":36,"value":969},{"type":30,"tag":439,"props":5921,"children":5922},{"style":462},[5923],{"type":36,"value":5877},{"type":30,"tag":439,"props":5925,"children":5926},{"style":922},[5927],{"type":36,"value":1030},{"type":30,"tag":439,"props":5929,"children":5930},{"style":446},[5931],{"type":36,"value":5932},"     // temps max pour recevoir la réponse\n",{"type":30,"tag":439,"props":5934,"children":5935},{"class":441,"line":517},[5936,5940,5945,5949,5953,5957,5961,5965,5969,5973],{"type":30,"tag":439,"props":5937,"children":5938},{"style":922},[5939],{"type":36,"value":5845},{"type":30,"tag":439,"props":5941,"children":5942},{"style":1105},[5943],{"type":36,"value":5944},"writeTimeout",{"type":30,"tag":439,"props":5946,"children":5947},{"style":922},[5948],{"type":36,"value":944},{"type":30,"tag":439,"props":5950,"children":5951},{"style":1022},[5952],{"type":36,"value":5907},{"type":30,"tag":439,"props":5954,"children":5955},{"style":922},[5956],{"type":36,"value":1010},{"type":30,"tag":439,"props":5958,"children":5959},{"style":462},[5960],{"type":36,"value":5868},{"type":30,"tag":439,"props":5962,"children":5963},{"style":922},[5964],{"type":36,"value":969},{"type":30,"tag":439,"props":5966,"children":5967},{"style":462},[5968],{"type":36,"value":5877},{"type":30,"tag":439,"props":5970,"children":5971},{"style":922},[5972],{"type":36,"value":1030},{"type":30,"tag":439,"props":5974,"children":5975},{"style":446},[5976],{"type":36,"value":5977},"    // temps max pour envoyer la requête\n",{"type":30,"tag":439,"props":5979,"children":5980},{"class":441,"line":526},[5981,5985,5990],{"type":30,"tag":439,"props":5982,"children":5983},{"style":922},[5984],{"type":36,"value":5845},{"type":30,"tag":439,"props":5986,"children":5987},{"style":1105},[5988],{"type":36,"value":5989},"build",{"type":30,"tag":439,"props":5991,"children":5992},{"style":922},[5993],{"type":36,"value":5994},"();\n",{"type":30,"tag":38,"props":5996,"children":5997},{},[5998,6003],{"type":30,"tag":44,"props":5999,"children":6000},{},[6001],{"type":36,"value":6002},"À quelle couche définir le timeout :",{"type":36,"value":6004}," aussi près que possible de l'appel externe. Un timeout au niveau de l'API gateway seul ne protège pas les services internes.",{"type":30,"tag":67,"props":6006,"children":6007},{},[],{"type":30,"tag":71,"props":6009,"children":6011},{"id":6010},"pattern-2-retry-avec-backoff-exponentiel",[6012],{"type":36,"value":6013},"Pattern 2 : Retry avec backoff exponentiel",{"type":30,"tag":38,"props":6015,"children":6016},{},[6017],{"type":36,"value":6018},"Un service peut échouer de façon transitoire : surcharge momentanée, redémarrage d'instance, pic de latence. Un retry permet de retenter l'opération après un délai, sans inonder le service défaillant de requêtes supplémentaires.",{"type":30,"tag":38,"props":6020,"children":6021},{},[6022,6027],{"type":30,"tag":44,"props":6023,"children":6024},{},[6025],{"type":36,"value":6026},"Principe du backoff exponentiel :",{"type":36,"value":6028}," le délai entre les tentatives augmente exponentiellement. Avec un jitter (variation aléatoire) pour éviter que tous les appelants retentent exactement au même moment, ce qui recréerait la surcharge qu'on cherche à éviter.",{"type":30,"tag":185,"props":6030,"children":6032},{"className":886,"code":6031,"language":885,"meta":8,"style":8},"import time\nimport random\n\ndef call_with_retry(func, max_retries=3, base_delay=0.1):\n    for attempt in range(max_retries):\n        try:\n            return func()\n        except TransientError as e:\n            if attempt == max_retries - 1:\n                raise  # dernière tentative — propager l'erreur\n            delay = base_delay * (2 ** attempt) + random.uniform(0, 0.1)  # jitter\n            time.sleep(delay)\n",[6033],{"type":30,"tag":98,"props":6034,"children":6035},{"__ignoreMap":8},[6036,6048,6060,6067,6128,6163,6175,6192,6219,6255,6268,6357],{"type":30,"tag":439,"props":6037,"children":6038},{"class":441,"line":442},[6039,6043],{"type":30,"tag":439,"props":6040,"children":6041},{"style":456},[6042],{"type":36,"value":1415},{"type":30,"tag":439,"props":6044,"children":6045},{"style":462},[6046],{"type":36,"value":6047}," time\n",{"type":30,"tag":439,"props":6049,"children":6050},{"class":441,"line":452},[6051,6055],{"type":30,"tag":439,"props":6052,"children":6053},{"style":456},[6054],{"type":36,"value":1415},{"type":30,"tag":439,"props":6056,"children":6057},{"style":462},[6058],{"type":36,"value":6059}," random\n",{"type":30,"tag":439,"props":6061,"children":6062},{"class":441,"line":478},[6063],{"type":30,"tag":439,"props":6064,"children":6065},{"emptyLinePlaceholder":13},[6066],{"type":36,"value":514},{"type":30,"tag":439,"props":6068,"children":6069},{"class":441,"line":508},[6070,6074,6079,6083,6088,6092,6097,6101,6106,6110,6115,6119,6124],{"type":30,"tag":439,"props":6071,"children":6072},{"style":456},[6073],{"type":36,"value":2341},{"type":30,"tag":439,"props":6075,"children":6076},{"style":1105},[6077],{"type":36,"value":6078}," call_with_retry",{"type":30,"tag":439,"props":6080,"children":6081},{"style":922},[6082],{"type":36,"value":944},{"type":30,"tag":439,"props":6084,"children":6085},{"style":1123},[6086],{"type":36,"value":6087},"func",{"type":30,"tag":439,"props":6089,"children":6090},{"style":922},[6091],{"type":36,"value":1010},{"type":30,"tag":439,"props":6093,"children":6094},{"style":1123},[6095],{"type":36,"value":6096}," max_retries",{"type":30,"tag":439,"props":6098,"children":6099},{"style":487},[6100],{"type":36,"value":979},{"type":30,"tag":439,"props":6102,"children":6103},{"style":1022},[6104],{"type":36,"value":6105},"3",{"type":30,"tag":439,"props":6107,"children":6108},{"style":922},[6109],{"type":36,"value":1010},{"type":30,"tag":439,"props":6111,"children":6112},{"style":1123},[6113],{"type":36,"value":6114}," base_delay",{"type":30,"tag":439,"props":6116,"children":6117},{"style":487},[6118],{"type":36,"value":979},{"type":30,"tag":439,"props":6120,"children":6121},{"style":1022},[6122],{"type":36,"value":6123},"0.1",{"type":30,"tag":439,"props":6125,"children":6126},{"style":922},[6127],{"type":36,"value":955},{"type":30,"tag":439,"props":6129,"children":6130},{"class":441,"line":517},[6131,6136,6141,6145,6150,6154,6159],{"type":30,"tag":439,"props":6132,"children":6133},{"style":456},[6134],{"type":36,"value":6135},"    for",{"type":30,"tag":439,"props":6137,"children":6138},{"style":462},[6139],{"type":36,"value":6140}," attempt ",{"type":30,"tag":439,"props":6142,"children":6143},{"style":456},[6144],{"type":36,"value":2914},{"type":30,"tag":439,"props":6146,"children":6147},{"style":1507},[6148],{"type":36,"value":6149}," range",{"type":30,"tag":439,"props":6151,"children":6152},{"style":922},[6153],{"type":36,"value":944},{"type":30,"tag":439,"props":6155,"children":6156},{"style":462},[6157],{"type":36,"value":6158},"max_retries",{"type":30,"tag":439,"props":6160,"children":6161},{"style":922},[6162],{"type":36,"value":955},{"type":30,"tag":439,"props":6164,"children":6165},{"class":441,"line":526},[6166,6171],{"type":30,"tag":439,"props":6167,"children":6168},{"style":456},[6169],{"type":36,"value":6170},"        try",{"type":30,"tag":439,"props":6172,"children":6173},{"style":922},[6174],{"type":36,"value":925},{"type":30,"tag":439,"props":6176,"children":6177},{"class":441,"line":547},[6178,6183,6188],{"type":30,"tag":439,"props":6179,"children":6180},{"style":456},[6181],{"type":36,"value":6182},"            return",{"type":30,"tag":439,"props":6184,"children":6185},{"style":982},[6186],{"type":36,"value":6187}," func",{"type":30,"tag":439,"props":6189,"children":6190},{"style":922},[6191],{"type":36,"value":2753},{"type":30,"tag":439,"props":6193,"children":6194},{"class":441,"line":1097},[6195,6200,6205,6210,6215],{"type":30,"tag":439,"props":6196,"children":6197},{"style":456},[6198],{"type":36,"value":6199},"        except",{"type":30,"tag":439,"props":6201,"children":6202},{"style":462},[6203],{"type":36,"value":6204}," TransientError ",{"type":30,"tag":439,"props":6206,"children":6207},{"style":456},[6208],{"type":36,"value":6209},"as",{"type":30,"tag":439,"props":6211,"children":6212},{"style":462},[6213],{"type":36,"value":6214}," e",{"type":30,"tag":439,"props":6216,"children":6217},{"style":922},[6218],{"type":36,"value":925},{"type":30,"tag":439,"props":6220,"children":6221},{"class":441,"line":1133},[6222,6227,6231,6236,6241,6246,6251],{"type":30,"tag":439,"props":6223,"children":6224},{"style":456},[6225],{"type":36,"value":6226},"            if",{"type":30,"tag":439,"props":6228,"children":6229},{"style":462},[6230],{"type":36,"value":6140},{"type":30,"tag":439,"props":6232,"children":6233},{"style":487},[6234],{"type":36,"value":6235},"==",{"type":30,"tag":439,"props":6237,"children":6238},{"style":462},[6239],{"type":36,"value":6240}," max_retries ",{"type":30,"tag":439,"props":6242,"children":6243},{"style":487},[6244],{"type":36,"value":6245},"-",{"type":30,"tag":439,"props":6247,"children":6248},{"style":1022},[6249],{"type":36,"value":6250}," 1",{"type":30,"tag":439,"props":6252,"children":6253},{"style":922},[6254],{"type":36,"value":925},{"type":30,"tag":439,"props":6256,"children":6257},{"class":441,"line":1183},[6258,6263],{"type":30,"tag":439,"props":6259,"children":6260},{"style":456},[6261],{"type":36,"value":6262},"                raise",{"type":30,"tag":439,"props":6264,"children":6265},{"style":446},[6266],{"type":36,"value":6267},"  # dernière tentative — propager l'erreur\n",{"type":30,"tag":439,"props":6269,"children":6270},{"class":441,"line":1250},[6271,6276,6280,6285,6290,6294,6298,6303,6308,6312,6316,6321,6325,6330,6334,6339,6343,6348,6352],{"type":30,"tag":439,"props":6272,"children":6273},{"style":462},[6274],{"type":36,"value":6275},"            delay ",{"type":30,"tag":439,"props":6277,"children":6278},{"style":487},[6279],{"type":36,"value":979},{"type":30,"tag":439,"props":6281,"children":6282},{"style":462},[6283],{"type":36,"value":6284}," base_delay ",{"type":30,"tag":439,"props":6286,"children":6287},{"style":487},[6288],{"type":36,"value":6289},"*",{"type":30,"tag":439,"props":6291,"children":6292},{"style":922},[6293],{"type":36,"value":3589},{"type":30,"tag":439,"props":6295,"children":6296},{"style":1022},[6297],{"type":36,"value":5859},{"type":30,"tag":439,"props":6299,"children":6300},{"style":487},[6301],{"type":36,"value":6302}," **",{"type":30,"tag":439,"props":6304,"children":6305},{"style":462},[6306],{"type":36,"value":6307}," attempt",{"type":30,"tag":439,"props":6309,"children":6310},{"style":922},[6311],{"type":36,"value":1030},{"type":30,"tag":439,"props":6313,"children":6314},{"style":487},[6315],{"type":36,"value":5383},{"type":30,"tag":439,"props":6317,"children":6318},{"style":462},[6319],{"type":36,"value":6320}," random",{"type":30,"tag":439,"props":6322,"children":6323},{"style":922},[6324],{"type":36,"value":969},{"type":30,"tag":439,"props":6326,"children":6327},{"style":982},[6328],{"type":36,"value":6329},"uniform",{"type":30,"tag":439,"props":6331,"children":6332},{"style":922},[6333],{"type":36,"value":944},{"type":30,"tag":439,"props":6335,"children":6336},{"style":1022},[6337],{"type":36,"value":6338},"0",{"type":30,"tag":439,"props":6340,"children":6341},{"style":922},[6342],{"type":36,"value":1010},{"type":30,"tag":439,"props":6344,"children":6345},{"style":1022},[6346],{"type":36,"value":6347}," 0.1",{"type":30,"tag":439,"props":6349,"children":6350},{"style":922},[6351],{"type":36,"value":1030},{"type":30,"tag":439,"props":6353,"children":6354},{"style":446},[6355],{"type":36,"value":6356},"  # jitter\n",{"type":30,"tag":439,"props":6358,"children":6359},{"class":441,"line":1655},[6360,6365,6369,6374,6378,6383],{"type":30,"tag":439,"props":6361,"children":6362},{"style":462},[6363],{"type":36,"value":6364},"            time",{"type":30,"tag":439,"props":6366,"children":6367},{"style":922},[6368],{"type":36,"value":969},{"type":30,"tag":439,"props":6370,"children":6371},{"style":982},[6372],{"type":36,"value":6373},"sleep",{"type":30,"tag":439,"props":6375,"children":6376},{"style":922},[6377],{"type":36,"value":944},{"type":30,"tag":439,"props":6379,"children":6380},{"style":462},[6381],{"type":36,"value":6382},"delay",{"type":30,"tag":439,"props":6384,"children":6385},{"style":922},[6386],{"type":36,"value":1180},{"type":30,"tag":38,"props":6388,"children":6389},{},[6390],{"type":30,"tag":44,"props":6391,"children":6392},{},[6393],{"type":36,"value":6394},"Quand ne pas retenter :",{"type":30,"tag":620,"props":6396,"children":6397},{},[6398,6403],{"type":30,"tag":624,"props":6399,"children":6400},{},[6401],{"type":36,"value":6402},"Erreurs non-transientes (404, 401, 403, validation error) : retenter ne changera rien",{"type":30,"tag":624,"props":6404,"children":6405},{},[6406],{"type":36,"value":6407},"Opérations non-idempotentes : retenter peut dupliquer l'effet (créer deux commandes au lieu d'une)",{"type":30,"tag":225,"props":6409,"children":6411},{"cta":227,"href":228,"title":6410,"type":230},"Votre système distribué tombe en cascade lors des incidents et vous ne savez pas par où commencer pour le renforcer ?",[6412],{"type":30,"tag":38,"props":6413,"children":6414},{},[6415],{"type":36,"value":6416},"L'implémentation des patterns de résilience nécessite un audit des points de défaillance critiques et un plan de priorisation. Je l'ai fait pour des équipes dans la finance, l'assurance et le e-commerce. En 30 minutes, on peut identifier les 3 flux les plus à risque dans votre architecture et définir le plan d'implémentation.",{"type":30,"tag":67,"props":6418,"children":6419},{},[],{"type":30,"tag":71,"props":6421,"children":6423},{"id":6422},"pattern-3-circuit-breaker",[6424],{"type":36,"value":6425},"Pattern 3 : Circuit Breaker",{"type":30,"tag":38,"props":6427,"children":6428},{},[6429],{"type":36,"value":6430},"Le circuit breaker est le pattern le plus important pour prévenir les cascade failures. Son principe est emprunté à l'électricité : quand un circuit est en surcharge, le disjoncteur coupe le courant pour protéger le reste du circuit.",{"type":30,"tag":38,"props":6432,"children":6433},{},[6434],{"type":30,"tag":44,"props":6435,"children":6436},{},[6437],{"type":36,"value":6438},"Les 3 états :",{"type":30,"tag":38,"props":6440,"children":6441},{},[6442,6447],{"type":30,"tag":44,"props":6443,"children":6444},{},[6445],{"type":36,"value":6446},"Closed (normal)",{"type":36,"value":6448}," : les appels passent normalement. Le circuit breaker monitore le taux d'échec.",{"type":30,"tag":38,"props":6450,"children":6451},{},[6452,6457],{"type":30,"tag":44,"props":6453,"children":6454},{},[6455],{"type":36,"value":6456},"Open",{"type":36,"value":6458}," : le taux d'échec a dépassé le seuil (par exemple, plus de 50% d'échecs sur les 10 derniers appels). Les appels échouent immédiatement sans atteindre le service défaillant. Cela protège le service défaillant de la surcharge et protège l'appelant d'une accumulation de threads.",{"type":30,"tag":38,"props":6460,"children":6461},{},[6462,6467],{"type":30,"tag":44,"props":6463,"children":6464},{},[6465],{"type":36,"value":6466},"Half-Open",{"type":36,"value":6468}," : après un délai (30 secondes par exemple), le circuit breaker laisse passer quelques appels de test. Si ils réussissent, retour à Closed. Sinon, retour à Open.",{"type":30,"tag":185,"props":6470,"children":6472},{"className":4504,"code":6471,"language":4503,"meta":8,"style":8},"// Exemple avec Resilience4j (Java)\nCircuitBreakerConfig config = CircuitBreakerConfig.custom()\n    .failureRateThreshold(50)\n    .waitDurationInOpenState(Duration.ofSeconds(30))\n    .slidingWindowSize(10)\n    .build();\n\nCircuitBreaker circuitBreaker = CircuitBreaker.of(\"paymentService\", config);\n\nSupplier\u003CPayment> decoratedCall = CircuitBreaker\n    .decorateSupplier(circuitBreaker, () -> paymentService.processPayment(request));\n",[6473],{"type":30,"tag":98,"props":6474,"children":6475},{"__ignoreMap":8},[6476,6484,6519,6544,6587,6612,6627,6634,6687,6694,6729],{"type":30,"tag":439,"props":6477,"children":6478},{"class":441,"line":442},[6479],{"type":30,"tag":439,"props":6480,"children":6481},{"style":446},[6482],{"type":36,"value":6483},"// Exemple avec Resilience4j (Java)\n",{"type":30,"tag":439,"props":6485,"children":6486},{"class":441,"line":452},[6487,6492,6497,6501,6506,6510,6515],{"type":30,"tag":439,"props":6488,"children":6489},{"style":4558},[6490],{"type":36,"value":6491},"CircuitBreakerConfig",{"type":30,"tag":439,"props":6493,"children":6494},{"style":462},[6495],{"type":36,"value":6496}," config ",{"type":30,"tag":439,"props":6498,"children":6499},{"style":487},[6500],{"type":36,"value":979},{"type":30,"tag":439,"props":6502,"children":6503},{"style":462},[6504],{"type":36,"value":6505}," CircuitBreakerConfig",{"type":30,"tag":439,"props":6507,"children":6508},{"style":922},[6509],{"type":36,"value":969},{"type":30,"tag":439,"props":6511,"children":6512},{"style":1105},[6513],{"type":36,"value":6514},"custom",{"type":30,"tag":439,"props":6516,"children":6517},{"style":922},[6518],{"type":36,"value":2753},{"type":30,"tag":439,"props":6520,"children":6521},{"class":441,"line":478},[6522,6526,6531,6535,6540],{"type":30,"tag":439,"props":6523,"children":6524},{"style":922},[6525],{"type":36,"value":5845},{"type":30,"tag":439,"props":6527,"children":6528},{"style":1105},[6529],{"type":36,"value":6530},"failureRateThreshold",{"type":30,"tag":439,"props":6532,"children":6533},{"style":922},[6534],{"type":36,"value":944},{"type":30,"tag":439,"props":6536,"children":6537},{"style":1022},[6538],{"type":36,"value":6539},"50",{"type":30,"tag":439,"props":6541,"children":6542},{"style":922},[6543],{"type":36,"value":1180},{"type":30,"tag":439,"props":6545,"children":6546},{"class":441,"line":508},[6547,6551,6556,6560,6565,6569,6574,6578,6583],{"type":30,"tag":439,"props":6548,"children":6549},{"style":922},[6550],{"type":36,"value":5845},{"type":30,"tag":439,"props":6552,"children":6553},{"style":1105},[6554],{"type":36,"value":6555},"waitDurationInOpenState",{"type":30,"tag":439,"props":6557,"children":6558},{"style":922},[6559],{"type":36,"value":944},{"type":30,"tag":439,"props":6561,"children":6562},{"style":462},[6563],{"type":36,"value":6564},"Duration",{"type":30,"tag":439,"props":6566,"children":6567},{"style":922},[6568],{"type":36,"value":969},{"type":30,"tag":439,"props":6570,"children":6571},{"style":1105},[6572],{"type":36,"value":6573},"ofSeconds",{"type":30,"tag":439,"props":6575,"children":6576},{"style":922},[6577],{"type":36,"value":944},{"type":30,"tag":439,"props":6579,"children":6580},{"style":1022},[6581],{"type":36,"value":6582},"30",{"type":30,"tag":439,"props":6584,"children":6585},{"style":922},[6586],{"type":36,"value":2436},{"type":30,"tag":439,"props":6588,"children":6589},{"class":441,"line":517},[6590,6594,6599,6603,6608],{"type":30,"tag":439,"props":6591,"children":6592},{"style":922},[6593],{"type":36,"value":5845},{"type":30,"tag":439,"props":6595,"children":6596},{"style":1105},[6597],{"type":36,"value":6598},"slidingWindowSize",{"type":30,"tag":439,"props":6600,"children":6601},{"style":922},[6602],{"type":36,"value":944},{"type":30,"tag":439,"props":6604,"children":6605},{"style":1022},[6606],{"type":36,"value":6607},"10",{"type":30,"tag":439,"props":6609,"children":6610},{"style":922},[6611],{"type":36,"value":1180},{"type":30,"tag":439,"props":6613,"children":6614},{"class":441,"line":526},[6615,6619,6623],{"type":30,"tag":439,"props":6616,"children":6617},{"style":922},[6618],{"type":36,"value":5845},{"type":30,"tag":439,"props":6620,"children":6621},{"style":1105},[6622],{"type":36,"value":5989},{"type":30,"tag":439,"props":6624,"children":6625},{"style":922},[6626],{"type":36,"value":5994},{"type":30,"tag":439,"props":6628,"children":6629},{"class":441,"line":547},[6630],{"type":30,"tag":439,"props":6631,"children":6632},{"emptyLinePlaceholder":13},[6633],{"type":36,"value":514},{"type":30,"tag":439,"props":6635,"children":6636},{"class":441,"line":1097},[6637,6642,6647,6651,6656,6660,6665,6669,6674,6678,6683],{"type":30,"tag":439,"props":6638,"children":6639},{"style":4558},[6640],{"type":36,"value":6641},"CircuitBreaker",{"type":30,"tag":439,"props":6643,"children":6644},{"style":462},[6645],{"type":36,"value":6646}," circuitBreaker ",{"type":30,"tag":439,"props":6648,"children":6649},{"style":487},[6650],{"type":36,"value":979},{"type":30,"tag":439,"props":6652,"children":6653},{"style":462},[6654],{"type":36,"value":6655}," CircuitBreaker",{"type":30,"tag":439,"props":6657,"children":6658},{"style":922},[6659],{"type":36,"value":969},{"type":30,"tag":439,"props":6661,"children":6662},{"style":1105},[6663],{"type":36,"value":6664},"of",{"type":30,"tag":439,"props":6666,"children":6667},{"style":922},[6668],{"type":36,"value":944},{"type":30,"tag":439,"props":6670,"children":6671},{"style":1002},[6672],{"type":36,"value":6673},"\"paymentService\"",{"type":30,"tag":439,"props":6675,"children":6676},{"style":922},[6677],{"type":36,"value":1010},{"type":30,"tag":439,"props":6679,"children":6680},{"style":462},[6681],{"type":36,"value":6682}," config",{"type":30,"tag":439,"props":6684,"children":6685},{"style":922},[6686],{"type":36,"value":4584},{"type":30,"tag":439,"props":6688,"children":6689},{"class":441,"line":1133},[6690],{"type":30,"tag":439,"props":6691,"children":6692},{"emptyLinePlaceholder":13},[6693],{"type":36,"value":514},{"type":30,"tag":439,"props":6695,"children":6696},{"class":441,"line":1183},[6697,6702,6706,6711,6715,6720,6724],{"type":30,"tag":439,"props":6698,"children":6699},{"style":4558},[6700],{"type":36,"value":6701},"Supplier",{"type":30,"tag":439,"props":6703,"children":6704},{"style":922},[6705],{"type":36,"value":3067},{"type":30,"tag":439,"props":6707,"children":6708},{"style":456},[6709],{"type":36,"value":6710},"Payment",{"type":30,"tag":439,"props":6712,"children":6713},{"style":922},[6714],{"type":36,"value":3077},{"type":30,"tag":439,"props":6716,"children":6717},{"style":462},[6718],{"type":36,"value":6719}," decoratedCall ",{"type":30,"tag":439,"props":6721,"children":6722},{"style":487},[6723],{"type":36,"value":979},{"type":30,"tag":439,"props":6725,"children":6726},{"style":4558},[6727],{"type":36,"value":6728}," CircuitBreaker\n",{"type":30,"tag":439,"props":6730,"children":6731},{"class":441,"line":1250},[6732,6736,6741,6745,6750,6754,6758,6762,6767,6771,6776,6780,6785],{"type":30,"tag":439,"props":6733,"children":6734},{"style":922},[6735],{"type":36,"value":5845},{"type":30,"tag":439,"props":6737,"children":6738},{"style":1105},[6739],{"type":36,"value":6740},"decorateSupplier",{"type":30,"tag":439,"props":6742,"children":6743},{"style":922},[6744],{"type":36,"value":944},{"type":30,"tag":439,"props":6746,"children":6747},{"style":462},[6748],{"type":36,"value":6749},"circuitBreaker",{"type":30,"tag":439,"props":6751,"children":6752},{"style":922},[6753],{"type":36,"value":1010},{"type":30,"tag":439,"props":6755,"children":6756},{"style":922},[6757],{"type":36,"value":3894},{"type":30,"tag":439,"props":6759,"children":6760},{"style":456},[6761],{"type":36,"value":1519},{"type":30,"tag":439,"props":6763,"children":6764},{"style":462},[6765],{"type":36,"value":6766}," paymentService",{"type":30,"tag":439,"props":6768,"children":6769},{"style":922},[6770],{"type":36,"value":969},{"type":30,"tag":439,"props":6772,"children":6773},{"style":1105},[6774],{"type":36,"value":6775},"processPayment",{"type":30,"tag":439,"props":6777,"children":6778},{"style":922},[6779],{"type":36,"value":944},{"type":30,"tag":439,"props":6781,"children":6782},{"style":462},[6783],{"type":36,"value":6784},"request",{"type":30,"tag":439,"props":6786,"children":6787},{"style":922},[6788],{"type":36,"value":5004},{"type":30,"tag":185,"props":6790,"children":6794},{"className":6791,"code":6792,"language":6793,"meta":8,"style":8},"language-javascript shiki shiki-themes catppuccin-frappe github-dark","// Exemple avec opossum (Node.js)\nconst CircuitBreaker = require('opossum');\n\nconst breaker = new CircuitBreaker(paymentService.processPayment, {\n    timeout: 3000,\n    errorThresholdPercentage: 50,\n    resetTimeout: 30000\n});\n\nbreaker.fallback(() => ({ status: 'payment_service_unavailable' }));\n","javascript",[6795],{"type":30,"tag":98,"props":6796,"children":6797},{"__ignoreMap":8},[6798,6806,6844,6851,6896,6917,6938,6955,6970,6977],{"type":30,"tag":439,"props":6799,"children":6800},{"class":441,"line":442},[6801],{"type":30,"tag":439,"props":6802,"children":6803},{"style":446},[6804],{"type":36,"value":6805},"// Exemple avec opossum (Node.js)\n",{"type":30,"tag":439,"props":6807,"children":6808},{"class":441,"line":452},[6809,6814,6818,6822,6827,6831,6836,6840],{"type":30,"tag":439,"props":6810,"children":6811},{"style":456},[6812],{"type":36,"value":6813},"const",{"type":30,"tag":439,"props":6815,"children":6816},{"style":1418},[6817],{"type":36,"value":6655},{"type":30,"tag":439,"props":6819,"children":6820},{"style":487},[6821],{"type":36,"value":2694},{"type":30,"tag":439,"props":6823,"children":6824},{"style":1105},[6825],{"type":36,"value":6826}," require",{"type":30,"tag":439,"props":6828,"children":6829},{"style":462},[6830],{"type":36,"value":944},{"type":30,"tag":439,"props":6832,"children":6833},{"style":1002},[6834],{"type":36,"value":6835},"'opossum'",{"type":30,"tag":439,"props":6837,"children":6838},{"style":462},[6839],{"type":36,"value":1030},{"type":30,"tag":439,"props":6841,"children":6842},{"style":922},[6843],{"type":36,"value":3082},{"type":30,"tag":439,"props":6845,"children":6846},{"class":441,"line":478},[6847],{"type":30,"tag":439,"props":6848,"children":6849},{"emptyLinePlaceholder":13},[6850],{"type":36,"value":514},{"type":30,"tag":439,"props":6852,"children":6853},{"class":441,"line":508},[6854,6858,6863,6867,6871,6875,6880,6884,6888,6892],{"type":30,"tag":439,"props":6855,"children":6856},{"style":456},[6857],{"type":36,"value":6813},{"type":30,"tag":439,"props":6859,"children":6860},{"style":1418},[6861],{"type":36,"value":6862}," breaker",{"type":30,"tag":439,"props":6864,"children":6865},{"style":487},[6866],{"type":36,"value":2694},{"type":30,"tag":439,"props":6868,"children":6869},{"style":3624},[6870],{"type":36,"value":3627},{"type":30,"tag":439,"props":6872,"children":6873},{"style":1105},[6874],{"type":36,"value":6655},{"type":30,"tag":439,"props":6876,"children":6877},{"style":462},[6878],{"type":36,"value":6879},"(paymentService",{"type":30,"tag":439,"props":6881,"children":6882},{"style":3480},[6883],{"type":36,"value":969},{"type":30,"tag":439,"props":6885,"children":6886},{"style":462},[6887],{"type":36,"value":6775},{"type":30,"tag":439,"props":6889,"children":6890},{"style":922},[6891],{"type":36,"value":1010},{"type":30,"tag":439,"props":6893,"children":6894},{"style":922},[6895],{"type":36,"value":3022},{"type":30,"tag":439,"props":6897,"children":6898},{"class":441,"line":517},[6899,6904,6908,6913],{"type":30,"tag":439,"props":6900,"children":6901},{"style":462},[6902],{"type":36,"value":6903},"    timeout",{"type":30,"tag":439,"props":6905,"children":6906},{"style":3480},[6907],{"type":36,"value":1504},{"type":30,"tag":439,"props":6909,"children":6910},{"style":1022},[6911],{"type":36,"value":6912}," 3000",{"type":30,"tag":439,"props":6914,"children":6915},{"style":922},[6916],{"type":36,"value":3348},{"type":30,"tag":439,"props":6918,"children":6919},{"class":441,"line":526},[6920,6925,6929,6934],{"type":30,"tag":439,"props":6921,"children":6922},{"style":462},[6923],{"type":36,"value":6924},"    errorThresholdPercentage",{"type":30,"tag":439,"props":6926,"children":6927},{"style":3480},[6928],{"type":36,"value":1504},{"type":30,"tag":439,"props":6930,"children":6931},{"style":1022},[6932],{"type":36,"value":6933}," 50",{"type":30,"tag":439,"props":6935,"children":6936},{"style":922},[6937],{"type":36,"value":3348},{"type":30,"tag":439,"props":6939,"children":6940},{"class":441,"line":547},[6941,6946,6950],{"type":30,"tag":439,"props":6942,"children":6943},{"style":462},[6944],{"type":36,"value":6945},"    resetTimeout",{"type":30,"tag":439,"props":6947,"children":6948},{"style":3480},[6949],{"type":36,"value":1504},{"type":30,"tag":439,"props":6951,"children":6952},{"style":1022},[6953],{"type":36,"value":6954}," 30000\n",{"type":30,"tag":439,"props":6956,"children":6957},{"class":441,"line":1097},[6958,6962,6966],{"type":30,"tag":439,"props":6959,"children":6960},{"style":922},[6961],{"type":36,"value":4472},{"type":30,"tag":439,"props":6963,"children":6964},{"style":462},[6965],{"type":36,"value":1030},{"type":30,"tag":439,"props":6967,"children":6968},{"style":922},[6969],{"type":36,"value":3082},{"type":30,"tag":439,"props":6971,"children":6972},{"class":441,"line":1133},[6973],{"type":30,"tag":439,"props":6974,"children":6975},{"emptyLinePlaceholder":13},[6976],{"type":36,"value":514},{"type":30,"tag":439,"props":6978,"children":6979},{"class":441,"line":1183},[6980,6985,6989,6994,6998,7002,7006,7010,7014,7018,7022,7027,7031,7036],{"type":30,"tag":439,"props":6981,"children":6982},{"style":462},[6983],{"type":36,"value":6984},"breaker",{"type":30,"tag":439,"props":6986,"children":6987},{"style":3480},[6988],{"type":36,"value":969},{"type":30,"tag":439,"props":6990,"children":6991},{"style":1105},[6992],{"type":36,"value":6993},"fallback",{"type":30,"tag":439,"props":6995,"children":6996},{"style":462},[6997],{"type":36,"value":944},{"type":30,"tag":439,"props":6999,"children":7000},{"style":922},[7001],{"type":36,"value":4003},{"type":30,"tag":439,"props":7003,"children":7004},{"style":456},[7005],{"type":36,"value":3899},{"type":30,"tag":439,"props":7007,"children":7008},{"style":462},[7009],{"type":36,"value":3589},{"type":30,"tag":439,"props":7011,"children":7012},{"style":922},[7013],{"type":36,"value":2297},{"type":30,"tag":439,"props":7015,"children":7016},{"style":462},[7017],{"type":36,"value":4097},{"type":30,"tag":439,"props":7019,"children":7020},{"style":3480},[7021],{"type":36,"value":1504},{"type":30,"tag":439,"props":7023,"children":7024},{"style":1002},[7025],{"type":36,"value":7026}," 'payment_service_unavailable'",{"type":30,"tag":439,"props":7028,"children":7029},{"style":922},[7030],{"type":36,"value":4111},{"type":30,"tag":439,"props":7032,"children":7033},{"style":462},[7034],{"type":36,"value":7035},"))",{"type":30,"tag":439,"props":7037,"children":7038},{"style":922},[7039],{"type":36,"value":3082},{"type":30,"tag":67,"props":7041,"children":7042},{},[],{"type":30,"tag":71,"props":7044,"children":7046},{"id":7045},"les-compléments-bulkhead-et-fallback",[7047],{"type":36,"value":7048},"Les compléments : bulkhead et fallback",{"type":30,"tag":38,"props":7050,"children":7051},{},[7052,7057],{"type":30,"tag":44,"props":7053,"children":7054},{},[7055],{"type":36,"value":7056},"Bulkhead :",{"type":36,"value":7058}," isoler les ressources par service externe. Chaque service externe a son propre pool de threads ou de connexions : une dégradation d'un service ne consomme pas toutes les ressources de l'appelant.",{"type":30,"tag":38,"props":7060,"children":7061},{},[7062,7067],{"type":30,"tag":44,"props":7063,"children":7064},{},[7065],{"type":36,"value":7066},"Fallback :",{"type":36,"value":7068}," définir un comportement de repli quand le circuit est ouvert. Pas \"retourner une erreur 500\", mais retourner un résultat dégradé acceptable.",{"type":30,"tag":620,"props":7070,"children":7071},{},[7072,7077,7082],{"type":30,"tag":624,"props":7073,"children":7074},{},[7075],{"type":36,"value":7076},"Service de recommandations indisponible → retourner des recommandations statiques ou populaires",{"type":30,"tag":624,"props":7078,"children":7079},{},[7080],{"type":36,"value":7081},"Service de notation indisponible → afficher la note moyenne sans recalcul temps-réel",{"type":30,"tag":624,"props":7083,"children":7084},{},[7085],{"type":36,"value":7086},"Service de paiement indisponible → mettre la commande en queue pour traitement différé",{"type":30,"tag":38,"props":7088,"children":7089},{},[7090],{"type":36,"value":7091},"Votre système doit fonctionner de façon dégradée quand un service non-critique est indisponible. Il ne doit pas être entièrement indisponible.",{"type":30,"tag":67,"props":7093,"children":7094},{},[],{"type":30,"tag":71,"props":7096,"children":7098},{"id":7097},"ordre-dimplémentation",[7099],{"type":36,"value":7100},"Ordre d'implémentation",{"type":30,"tag":7102,"props":7103,"children":7104},"ol",{},[7105,7115,7125],{"type":30,"tag":624,"props":7106,"children":7107},{},[7108,7113],{"type":30,"tag":44,"props":7109,"children":7110},{},[7111],{"type":36,"value":7112},"Timeout d'abord",{"type":36,"value":7114}," (1 journée) : ajouter des timeouts explicites sur tous les appels vers des services externes. C'est la correction la plus rapide et la plus impactante.",{"type":30,"tag":624,"props":7116,"children":7117},{},[7118,7123],{"type":30,"tag":44,"props":7119,"children":7120},{},[7121],{"type":36,"value":7122},"Retry sur les erreurs transientes",{"type":36,"value":7124}," (2 à 3 jours) : identifier les erreurs transientes dans votre système et ajouter un retry avec backoff exponentiel. S'assurer que les opérations retriées sont idempotentes.",{"type":30,"tag":624,"props":7126,"children":7127},{},[7128,7133],{"type":30,"tag":44,"props":7129,"children":7130},{},[7131],{"type":36,"value":7132},"Circuit breaker sur les services critiques",{"type":36,"value":7134}," (1 semaine) : identifier les 5 à 10 dépendances externes les plus critiques et implémenter le circuit breaker avec monitoring.",{"type":30,"tag":38,"props":7136,"children":7137},{},[7138],{"type":36,"value":7139},"En moins de 2 semaines, votre système est significativement plus résilient.",{"type":30,"tag":67,"props":7141,"children":7142},{},[],{"type":30,"tag":71,"props":7144,"children":7146},{"id":7145},"faq-sur-les-patterns-de-résilience",[7147],{"type":36,"value":7148},"FAQ sur les patterns de résilience",{"type":30,"tag":714,"props":7150,"children":7151},{},[7152,7157],{"type":30,"tag":718,"props":7153,"children":7154},{},[7155],{"type":36,"value":7156},"1. Les patterns de résilience s'appliquent-ils aux monolithes ou seulement aux microservices ?",{"type":30,"tag":38,"props":7158,"children":7159},{},[7160,7162,7167],{"type":36,"value":7161},"Aux deux. Tout système qui appelle des services externes (base de données, cache, API tierce, service d'email) bénéficie des timeouts, retries, et circuit breakers. Dans un monolithe, les patterns s'appliquent sur les appels vers les dépendances externes. Dans les ",{"type":30,"tag":142,"props":7163,"children":7164},{"href":5},[7165],{"type":36,"value":7166},"microservices avec une base de données par service",{"type":36,"value":7168},", ils s'appliquent aussi aux appels inter-services, et sont même indispensables pour gérer la cohérence éventuelle entre services. J'ai implémenté ces patterns dans des monolithes bancaires aussi bien que dans des architectures microservices de e-commerce.",{"type":30,"tag":714,"props":7170,"children":7171},{},[7172,7177],{"type":30,"tag":718,"props":7173,"children":7174},{},[7175],{"type":36,"value":7176},"2. Quel outil choisir pour implémenter les patterns de résilience ?",{"type":30,"tag":38,"props":7178,"children":7179},{},[7180],{"type":36,"value":7181},"Par écosystème : Java/Kotlin → Resilience4j (successeur de Hystrix, maintenu activement). Node.js → opossum. Python → tenacity pour le retry. Go → gobreaker. Spring Boot → intégration native de Resilience4j via Spring Cloud Circuit Breaker. À l'infrastructure : Istio et Envoy permettent d'implémenter ces patterns au niveau du service mesh, sans modification du code applicatif, utile pour les équipes qui gèrent de nombreux services.",{"type":30,"tag":714,"props":7183,"children":7184},{},[7185,7190],{"type":30,"tag":718,"props":7186,"children":7187},{},[7188],{"type":36,"value":7189},"3. Comment monitorer l'état des circuit breakers en production ?",{"type":30,"tag":38,"props":7191,"children":7192},{},[7193,7195,7201],{"type":36,"value":7194},"Les circuit breakers ouverts sont des alertes : ils signalent qu'un service est en difficulté. Exposer l'état des circuit breakers dans votre système de monitoring (Prometheus + Grafana, Datadog, New Relic). Configurer une alerte quand un circuit breaker passe à l'état Open : c'est un signal d'action, pas seulement d'information. Un circuit breaker qui reste ouvert en permanence indique un service en difficulté structurelle. Ces métriques de disponibilité font partie des ",{"type":30,"tag":142,"props":7196,"children":7198},{"href":7197},"/fr/management/metriques-management-developpeurs-motivation",[7199],{"type":36,"value":7200},"indicateurs clés à suivre pour les équipes engineering",{"type":36,"value":7202}," : elles objectivent l'état de santé de l'architecture et rendent visibles les progrès réalisés.",{"type":30,"tag":714,"props":7204,"children":7205},{},[7206,7211],{"type":30,"tag":718,"props":7207,"children":7208},{},[7209],{"type":36,"value":7210},"4. Comment tester les patterns de résilience sans attendre un incident en production ?",{"type":30,"tag":38,"props":7212,"children":7213},{},[7214,7216,7222],{"type":36,"value":7215},"Le chaos engineering est la méthode de référence. Outils : Chaos Monkey (Netflix), Gremlin, ou des scripts simples qui introduisent des latences artificielles en staging. Pour commencer sans outil spécifique : injecter un ",{"type":30,"tag":98,"props":7217,"children":7219},{"className":7218},[],[7220],{"type":36,"value":7221},"sleep(5000)",{"type":36,"value":7223}," dans le service cible en environnement de test et vérifier que les timeouts et circuit breakers fonctionnent comme attendu. Ce test prend 30 minutes et devrait être dans votre suite de tests de non-régression.",{"type":30,"tag":67,"props":7225,"children":7226},{},[],{"type":30,"tag":225,"props":7228,"children":7229},{"cta":785,"href":786,"title":787,"type":788},[7230],{"type":30,"tag":38,"props":7231,"children":7232},{},[7233],{"type":36,"value":7234},"L'Engineering Maturity Self-Assessment couvre le domaine Résilience & Architecture : évaluez en 10 minutes votre niveau sur les patterns de résilience, la robustesse de vos services, et la gestion des pannes. Obtenez un score et un plan d'action personnalisé.",{"type":30,"tag":796,"props":7236,"children":7237},{},[7238],{"type":36,"value":800},{"title":8,"searchDepth":452,"depth":452,"links":7240},[7241,7242,7243,7244,7245,7246,7247],{"id":5702,"depth":452,"text":5705},{"id":5739,"depth":452,"text":5742},{"id":6010,"depth":452,"text":6013},{"id":6422,"depth":452,"text":6425},{"id":7045,"depth":452,"text":7048},{"id":7097,"depth":452,"text":7100},{"id":7145,"depth":452,"text":7148},"content:fr:architecture-craft:patterns-resilience-circuit-breaker-retry.md","fr/architecture-craft/patterns-resilience-circuit-breaker-retry.md","fr/architecture-craft/patterns-resilience-circuit-breaker-retry",{"_path":7252,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":7253,"description":7254,"id":1859,"date":7255,"listed":13,"nocomments":7,"hidden":7,"categories":7256,"tags":7257,"--cover":7262,"readingTime":7263,"body":7267,"_type":810,"_id":7940,"_source":812,"_file":7941,"_stem":7942,"_extension":815},"/fr/architecture-craft/event-storming-atelier-modelisation","Modélisation du domaine : l'atelier Event Storming en 3h","L'Event Storming est l'atelier le plus efficace pour aligner développeurs et experts métier en quelques heures. Le guide de facilitation sans formation préalable.","2026-02-18",[6],[7258,7259,7260,7261],"Event Storming","DDD","Modélisation","Atelier","covers/articles/event-storming-atelier.jpg",{"text":22,"minutes":7264,"time":7265,"words":7266},7.4,444000,1480,{"type":27,"children":7268,"toc":7930},[7269,7274,7279,7284,7289,7294,7297,7303,7308,7313,7318,7326,7354,7357,7363,7368,7376,7394,7402,7420,7430,7433,7439,7444,7451,7469,7477,7495,7504,7507,7513,7518,7525,7543,7553,7556,7562,7567,7574,7592,7600,7618,7635,7638,7644,7649,7672,7677,7705,7716,7719,7725,7845,7848,7854,7867,7880,7893,7906,7919,7922],{"type":30,"tag":31,"props":7270,"children":7272},{"id":7271},"modélisation-du-domaine-latelier-event-storming-en-3h",[7273],{"type":36,"value":7253},{"type":30,"tag":38,"props":7275,"children":7276},{},[7277],{"type":36,"value":7278},"Chez un client dans la logistique (40 développeurs, 8 équipes, 4 ans de code accumulé), j'ai posé une question simple en réunion de lancement : \"Qu'est-ce qu'une commande dans votre système ?\"",{"type":30,"tag":38,"props":7280,"children":7281},{},[7282],{"type":36,"value":7283},"Quinze minutes de débat. Cinq définitions différentes. Deux équipes qui découvraient ce soir-là qu'elles ne parlaient pas du même objet depuis des mois.",{"type":30,"tag":38,"props":7285,"children":7286},{},[7287],{"type":36,"value":7288},"Pour l'équipe transport, une \"commande\" était un bon de préparation entrepôt. Pour l'équipe facturation, c'était une transaction client. Pour l'équipe livraison, c'était un shipment carrier. Ces trois concepts vivaient dans la même table de base de données. C'était la source de couplage et de bugs depuis 18 mois.",{"type":30,"tag":38,"props":7290,"children":7291},{},[7292],{"type":36,"value":7293},"Un atelier Event Storming de 3 heures a produit la clarté que 18 mois de réunions n'avaient pas donnée. Voici comment le reproduire.",{"type":30,"tag":67,"props":7295,"children":7296},{},[],{"type":30,"tag":71,"props":7298,"children":7300},{"id":7299},"pourquoi-levent-storming-fonctionne-là-où-les-réunions-échouent",[7301],{"type":36,"value":7302},"Pourquoi l'Event Storming fonctionne là où les réunions échouent",{"type":30,"tag":38,"props":7304,"children":7305},{},[7306],{"type":36,"value":7307},"Le problème numéro un dans le développement de systèmes complexes n'est pas technique. C'est que les développeurs et les experts métier parlent des langages différents pendant des mois, et que cette incompréhension se retrouve encodée dans l'architecture.",{"type":30,"tag":38,"props":7309,"children":7310},{},[7311],{"type":36,"value":7312},"Alberto Brandolini a créé l'Event Storming en 2013 comme une alternative aux longues sessions de recueil de besoins qui produisent des spécifications incompréhensibles. L'idée est radicale dans sa simplicité : rassembler toutes les parties prenantes dans une pièce avec des post-its de couleur, et modéliser le domaine par les événements qui s'y produisent, pas par les entités, pas par les processus, pas par les données.",{"type":30,"tag":38,"props":7314,"children":7315},{},[7316],{"type":36,"value":7317},"En 3 heures, une équipe peut atteindre une compréhension partagée qu'elle n'aurait pas obtenue en 3 mois de réunions classiques. J'ai facilité plus de 30 sessions de ce type dans des secteurs aussi différents que la finance (Crédit Agricole), les médias (Canal+) et la logistique.",{"type":30,"tag":38,"props":7319,"children":7320},{},[7321],{"type":30,"tag":44,"props":7322,"children":7323},{},[7324],{"type":36,"value":7325},"Ce dont vous avez besoin :",{"type":30,"tag":620,"props":7327,"children":7328},{},[7329,7334,7339,7344,7349],{"type":30,"tag":624,"props":7330,"children":7331},{},[7332],{"type":36,"value":7333},"Une grande surface murale (minimum 3 mètres linéaires) ou Miro/Mural pour le remote",{"type":30,"tag":624,"props":7335,"children":7336},{},[7337],{"type":36,"value":7338},"Post-its de 5 couleurs : orange (domain events), bleu (commands), jaune (aggregates), rose (external systems), rouge (hotspots/questions)",{"type":30,"tag":624,"props":7340,"children":7341},{},[7342],{"type":36,"value":7343},"5 à 12 participants : développeurs, domain experts, PO, et éventuellement le CTO",{"type":30,"tag":624,"props":7345,"children":7346},{},[7347],{"type":36,"value":7348},"Un facilitateur (qui peut être vous) qui connaît les étapes",{"type":30,"tag":624,"props":7350,"children":7351},{},[7352],{"type":36,"value":7353},"3 heures sans interruption",{"type":30,"tag":67,"props":7355,"children":7356},{},[],{"type":30,"tag":71,"props":7358,"children":7360},{"id":7359},"étape-1-les-domain-events-en-orange-30-min",[7361],{"type":36,"value":7362},"Étape 1 : Les Domain Events en orange (30 min)",{"type":30,"tag":38,"props":7364,"children":7365},{},[7366],{"type":36,"value":7367},"Un Domain Event est quelque chose qui s'est passé dans le domaine et qui a de l'importance business. Il se formule toujours au passé : \"Commande passée\", \"Paiement reçu\", \"Stock mis à jour\", \"Email de confirmation envoyé\".",{"type":30,"tag":38,"props":7369,"children":7370},{},[7371],{"type":30,"tag":44,"props":7372,"children":7373},{},[7374],{"type":36,"value":7375},"Déroulé :",{"type":30,"tag":7102,"props":7377,"children":7378},{},[7379,7384,7389],{"type":30,"tag":624,"props":7380,"children":7381},{},[7382],{"type":36,"value":7383},"Chaque participant écrit des événements sur des post-its orange (5 à 10 minutes en silence)",{"type":30,"tag":624,"props":7385,"children":7386},{},[7387],{"type":36,"value":7388},"Tout le monde colle ses post-its sur le mur",{"type":30,"tag":624,"props":7390,"children":7391},{},[7392],{"type":36,"value":7393},"Le facilitateur regroupe les événements chronologiquement, de gauche à droite",{"type":30,"tag":38,"props":7395,"children":7396},{},[7397],{"type":30,"tag":44,"props":7398,"children":7399},{},[7400],{"type":36,"value":7401},"Ce que je cherche en tant que facilitateur :",{"type":30,"tag":620,"props":7403,"children":7404},{},[7405,7410,7415],{"type":30,"tag":624,"props":7406,"children":7407},{},[7408],{"type":36,"value":7409},"Les doublons (deux termes pour le même événement → discussion sur le terme canonique)",{"type":30,"tag":624,"props":7411,"children":7412},{},[7413],{"type":36,"value":7414},"Les contradictions (même nom pour des événements différents → signal de problème de domaine)",{"type":30,"tag":624,"props":7416,"children":7417},{},[7418],{"type":36,"value":7419},"Les trous dans la timeline (il manque des événements entre deux étapes)",{"type":30,"tag":38,"props":7421,"children":7422},{},[7423,7428],{"type":30,"tag":44,"props":7424,"children":7425},{},[7426],{"type":36,"value":7427},"Règle absolue :",{"type":36,"value":7429}," à cette étape, pas de jugement. Tout ce qui peut se passer est bienvenu, même les événements d'erreur et les cas limites. Les \"ce n'est pas un vrai événement\" de la première heure sont souvent les plus révélateurs.",{"type":30,"tag":67,"props":7431,"children":7432},{},[],{"type":30,"tag":71,"props":7434,"children":7436},{"id":7435},"étape-2-les-commands-en-bleu-30-min",[7437],{"type":36,"value":7438},"Étape 2 : Les Commands en bleu (30 min)",{"type":30,"tag":38,"props":7440,"children":7441},{},[7442],{"type":36,"value":7443},"Une Command est ce qui déclenche un Domain Event. C'est une intention, une décision : \"Passer une commande\", \"Valider le paiement\", \"Envoyer la notification\".",{"type":30,"tag":38,"props":7445,"children":7446},{},[7447],{"type":30,"tag":44,"props":7448,"children":7449},{},[7450],{"type":36,"value":7375},{"type":30,"tag":7102,"props":7452,"children":7453},{},[7454,7459,7464],{"type":30,"tag":624,"props":7455,"children":7456},{},[7457],{"type":36,"value":7458},"Pour chaque Domain Event sur le mur, je demande : \"Qu'est-ce qui déclenche cet événement ?\"",{"type":30,"tag":624,"props":7460,"children":7461},{},[7462],{"type":36,"value":7463},"Ajouter le post-it bleu (Command) à gauche du post-it orange correspondant",{"type":30,"tag":624,"props":7465,"children":7466},{},[7467],{"type":36,"value":7468},"Identifier qui émet la Command : un utilisateur, un système automatique, un timer",{"type":30,"tag":38,"props":7470,"children":7471},{},[7472],{"type":30,"tag":44,"props":7473,"children":7474},{},[7475],{"type":36,"value":7476},"Ce que cette étape révèle :",{"type":30,"tag":620,"props":7478,"children":7479},{},[7480,7485,7490],{"type":30,"tag":624,"props":7481,"children":7482},{},[7483],{"type":36,"value":7484},"Les décisions implicites du domaine (certains événements se produisent sans command explicite → probable automatisme ou logique métier cachée)",{"type":30,"tag":624,"props":7486,"children":7487},{},[7488],{"type":36,"value":7489},"Les acteurs du système (qui décide quoi)",{"type":30,"tag":624,"props":7491,"children":7492},{},[7493],{"type":36,"value":7494},"Les règles métier qui gouvernent les commands",{"type":30,"tag":225,"props":7496,"children":7498},{"cta":227,"href":228,"title":7497,"type":230},"Votre équipe développe sans modèle de domaine partagé et les malentendus métier se retrouvent dans le code ?",[7499],{"type":30,"tag":38,"props":7500,"children":7501},{},[7502],{"type":36,"value":7503},"Faciliter un Event Storming pour la première fois sans expérience, c'est prendre le risque d'un atelier qui part dans tous les sens. En 30 minutes, on peut définir la stratégie d'atelier adaptée à votre contexte et votre domaine, et préparer le facilitateur pour une session qui produit de vrais résultats.",{"type":30,"tag":67,"props":7505,"children":7506},{},[],{"type":30,"tag":71,"props":7508,"children":7510},{"id":7509},"étape-3-les-aggregates-en-jaune-45-min",[7511],{"type":36,"value":7512},"Étape 3 : Les Aggregates en jaune (45 min)",{"type":30,"tag":38,"props":7514,"children":7515},{},[7516],{"type":36,"value":7517},"Un Aggregate est l'entité ou le concept qui traite une Command et produit un Domain Event. C'est l'unité de cohérence du domaine, le concept central du Domain-Driven Design tel que formalisé par Eric Evans.",{"type":30,"tag":38,"props":7519,"children":7520},{},[7521],{"type":30,"tag":44,"props":7522,"children":7523},{},[7524],{"type":36,"value":7375},{"type":30,"tag":7102,"props":7526,"children":7527},{},[7528,7533,7538],{"type":30,"tag":624,"props":7529,"children":7530},{},[7531],{"type":36,"value":7532},"Pour chaque paire Command → Domain Event, demander : \"Quelle entité traite cette commande et garantit que l'événement est produit correctement ?\"",{"type":30,"tag":624,"props":7534,"children":7535},{},[7536],{"type":36,"value":7537},"Placer un post-it jaune entre la Command et le Domain Event avec le nom de l'Aggregate",{"type":30,"tag":624,"props":7539,"children":7540},{},[7541],{"type":36,"value":7542},"Regrouper les paires Command/Event qui partagent le même Aggregate",{"type":30,"tag":38,"props":7544,"children":7545},{},[7546,7551],{"type":30,"tag":44,"props":7547,"children":7548},{},[7549],{"type":36,"value":7550},"L'exercice révélateur :",{"type":36,"value":7552},"\nQuand deux participants donnent des noms différents pour le même Aggregate, c'est un signal fort : soit c'est un problème de vocabulaire à résoudre, soit ce sont deux Aggregates distincts qui ont été confondus et qu'il faut séparer. Ce moment-là est souvent le plus productif de l'atelier.",{"type":30,"tag":67,"props":7554,"children":7555},{},[],{"type":30,"tag":71,"props":7557,"children":7559},{"id":7558},"étape-4-les-bounded-contexts-45-min",[7560],{"type":36,"value":7561},"Étape 4 : Les Bounded Contexts (45 min)",{"type":30,"tag":38,"props":7563,"children":7564},{},[7565],{"type":36,"value":7566},"Un Bounded Context est une frontière dans le modèle de domaine où le même terme peut avoir un sens différent, ou où des équipes différentes travaillent de façon indépendante.",{"type":30,"tag":38,"props":7568,"children":7569},{},[7570],{"type":30,"tag":44,"props":7571,"children":7572},{},[7573],{"type":36,"value":7375},{"type":30,"tag":7102,"props":7575,"children":7576},{},[7577,7582,7587],{"type":30,"tag":624,"props":7578,"children":7579},{},[7580],{"type":36,"value":7581},"Observer le mur et identifier les clusters naturels d'Aggregates qui travaillent ensemble",{"type":30,"tag":624,"props":7583,"children":7584},{},[7585],{"type":36,"value":7586},"Tracer des frontières visuelles entre les clusters",{"type":30,"tag":624,"props":7588,"children":7589},{},[7590],{"type":36,"value":7591},"Nommer chaque zone : \"Catalog\", \"Ordering\", \"Payment\", \"Fulfillment\"...",{"type":30,"tag":38,"props":7593,"children":7594},{},[7595],{"type":30,"tag":44,"props":7596,"children":7597},{},[7598],{"type":36,"value":7599},"Les questions qui révèlent les frontières :",{"type":30,"tag":620,"props":7601,"children":7602},{},[7603,7608,7613],{"type":30,"tag":624,"props":7604,"children":7605},{},[7606],{"type":36,"value":7607},"\"Est-ce que cette équipe parlerait à cette équipe directement, ou via un événement ?\"",{"type":30,"tag":624,"props":7609,"children":7610},{},[7611],{"type":36,"value":7612},"\"Est-ce que le mot 'Customer' a le même sens dans ces deux zones ?\"",{"type":30,"tag":624,"props":7614,"children":7615},{},[7616],{"type":36,"value":7617},"\"Ces deux zones peuvent-elles évoluer indépendamment ?\"",{"type":30,"tag":38,"props":7619,"children":7620},{},[7621,7626,7628,7633],{"type":30,"tag":44,"props":7622,"children":7623},{},[7624],{"type":36,"value":7625},"Les post-its rouges (Hotspots) :",{"type":36,"value":7627}," pendant tout l'atelier, quand une question reste sans réponse, je colle un post-it rouge. Ces hotspots sont les sujets à approfondir après l'atelier : ils révèlent les zones d'incertitude du domaine et deviennent la backlog des décisions architecturales à prendre. Ces décisions méritent d'être formalisées dans des ",{"type":30,"tag":142,"props":7629,"children":7630},{"href":144},[7631],{"type":36,"value":7632},"Architecture Decision Records",{"type":36,"value":7634}," pour garder la trace du contexte et des choix effectués.",{"type":30,"tag":67,"props":7636,"children":7637},{},[],{"type":30,"tag":71,"props":7639,"children":7641},{"id":7640},"ce-que-latelier-produit",[7642],{"type":36,"value":7643},"Ce que l'atelier produit",{"type":30,"tag":38,"props":7645,"children":7646},{},[7647],{"type":36,"value":7648},"À l'issue des 3 heures, vous disposez de :",{"type":30,"tag":620,"props":7650,"children":7651},{},[7652,7657,7662,7667],{"type":30,"tag":624,"props":7653,"children":7654},{},[7655],{"type":36,"value":7656},"Une timeline des Domain Events qui représente le flux complet du domaine",{"type":30,"tag":624,"props":7658,"children":7659},{},[7660],{"type":36,"value":7661},"Une carte des Commands et des acteurs",{"type":30,"tag":624,"props":7663,"children":7664},{},[7665],{"type":36,"value":7666},"Une carte des Aggregates et de leurs responsabilités",{"type":30,"tag":624,"props":7668,"children":7669},{},[7670],{"type":36,"value":7671},"Des Bounded Contexts qui suggèrent les frontières de services",{"type":30,"tag":38,"props":7673,"children":7674},{},[7675],{"type":36,"value":7676},"Ce modèle n'est pas une spec technique. C'est une langue commune entre développeurs et experts métier. Il guide directement :",{"type":30,"tag":620,"props":7678,"children":7679},{},[7680,7690,7695,7700],{"type":30,"tag":624,"props":7681,"children":7682},{},[7683,7685],{"type":36,"value":7684},"La définition des microservices (1 service par Bounded Context dans la plupart des cas), et par conséquent les décisions sur le ",{"type":30,"tag":142,"props":7686,"children":7687},{"href":5},[7688],{"type":36,"value":7689},"découpage des bases de données par service",{"type":30,"tag":624,"props":7691,"children":7692},{},[7693],{"type":36,"value":7694},"Le design des APIs (les Commands deviennent les endpoints)",{"type":30,"tag":624,"props":7696,"children":7697},{},[7698],{"type":36,"value":7699},"La modélisation de la base de données (les Aggregates deviennent les tables ou collections)",{"type":30,"tag":624,"props":7701,"children":7702},{},[7703],{"type":36,"value":7704},"L'écriture des tests (les Domain Events deviennent les scénarios de test)",{"type":30,"tag":38,"props":7706,"children":7707},{},[7708,7710,7714],{"type":36,"value":7709},"Chez le client logistique que j'évoquais en ouverture, l'atelier a révélé que l'\"Order\" était conceptuellement 3 choses différentes. Ces 3 concepts ont été extraits en 3 services distincts, chacun avec son architecture bien délimitée, en s'appuyant notamment sur les principes de la ",{"type":30,"tag":142,"props":7711,"children":7712},{"href":871},[7713],{"type":36,"value":874},{"type":36,"value":7715}," pour garantir l'indépendance des couches. Les bugs liés au couplage ont disparu dans les 6 semaines suivantes.",{"type":30,"tag":67,"props":7717,"children":7718},{},[],{"type":30,"tag":71,"props":7720,"children":7722},{"id":7721},"récapitulatif",[7723],{"type":36,"value":7724},"Récapitulatif",{"type":30,"tag":252,"props":7726,"children":7727},{},[7728,7754],{"type":30,"tag":256,"props":7729,"children":7730},{},[7731],{"type":30,"tag":260,"props":7732,"children":7733},{},[7734,7739,7744,7749],{"type":30,"tag":264,"props":7735,"children":7736},{},[7737],{"type":36,"value":7738},"Étape",{"type":30,"tag":264,"props":7740,"children":7741},{},[7742],{"type":36,"value":7743},"Durée",{"type":30,"tag":264,"props":7745,"children":7746},{},[7747],{"type":36,"value":7748},"Post-it",{"type":30,"tag":264,"props":7750,"children":7751},{},[7752],{"type":36,"value":7753},"Résultat",{"type":30,"tag":280,"props":7755,"children":7756},{},[7757,7780,7801,7823],{"type":30,"tag":260,"props":7758,"children":7759},{},[7760,7765,7770,7775],{"type":30,"tag":287,"props":7761,"children":7762},{},[7763],{"type":36,"value":7764},"1",{"type":30,"tag":287,"props":7766,"children":7767},{},[7768],{"type":36,"value":7769},"30 min",{"type":30,"tag":287,"props":7771,"children":7772},{},[7773],{"type":36,"value":7774},"Orange : Domain Events",{"type":30,"tag":287,"props":7776,"children":7777},{},[7778],{"type":36,"value":7779},"Timeline du domaine",{"type":30,"tag":260,"props":7781,"children":7782},{},[7783,7787,7791,7796],{"type":30,"tag":287,"props":7784,"children":7785},{},[7786],{"type":36,"value":5859},{"type":30,"tag":287,"props":7788,"children":7789},{},[7790],{"type":36,"value":7769},{"type":30,"tag":287,"props":7792,"children":7793},{},[7794],{"type":36,"value":7795},"Bleu : Commands",{"type":30,"tag":287,"props":7797,"children":7798},{},[7799],{"type":36,"value":7800},"Déclencheurs et acteurs",{"type":30,"tag":260,"props":7802,"children":7803},{},[7804,7808,7813,7818],{"type":30,"tag":287,"props":7805,"children":7806},{},[7807],{"type":36,"value":6105},{"type":30,"tag":287,"props":7809,"children":7810},{},[7811],{"type":36,"value":7812},"45 min",{"type":30,"tag":287,"props":7814,"children":7815},{},[7816],{"type":36,"value":7817},"Jaune : Aggregates",{"type":30,"tag":287,"props":7819,"children":7820},{},[7821],{"type":36,"value":7822},"Entités et responsabilités",{"type":30,"tag":260,"props":7824,"children":7825},{},[7826,7831,7835,7840],{"type":30,"tag":287,"props":7827,"children":7828},{},[7829],{"type":36,"value":7830},"4",{"type":30,"tag":287,"props":7832,"children":7833},{},[7834],{"type":36,"value":7812},{"type":30,"tag":287,"props":7836,"children":7837},{},[7838],{"type":36,"value":7839},"Frontières visuelles",{"type":30,"tag":287,"props":7841,"children":7842},{},[7843],{"type":36,"value":7844},"Bounded Contexts",{"type":30,"tag":67,"props":7846,"children":7847},{},[],{"type":30,"tag":71,"props":7849,"children":7851},{"id":7850},"faq-sur-levent-storming",[7852],{"type":36,"value":7853},"FAQ sur l'Event Storming",{"type":30,"tag":714,"props":7855,"children":7856},{},[7857,7862],{"type":30,"tag":718,"props":7858,"children":7859},{},[7860],{"type":36,"value":7861},"1. Faut-il connaître DDD pour faciliter un Event Storming ?",{"type":30,"tag":38,"props":7863,"children":7864},{},[7865],{"type":36,"value":7866},"Non. Les concepts fondamentaux (Domain Events, Commands, Aggregates, Bounded Contexts) peuvent s'expliquer en 5 minutes en début de session. L'Event Storming est conçu pour être accessible aux non-DDD. C'est d'ailleurs sa force : il permet aux domain experts, qui ne connaissent pas DDD, de contribuer activement. Un facilitateur qui comprend l'objectif de chaque étape est suffisant pour un premier atelier.",{"type":30,"tag":714,"props":7868,"children":7869},{},[7870,7875],{"type":30,"tag":718,"props":7871,"children":7872},{},[7873],{"type":36,"value":7874},"2. L'Event Storming fonctionne-t-il en remote ?",{"type":30,"tag":38,"props":7876,"children":7877},{},[7878],{"type":36,"value":7879},"Oui, avec des outils comme Miro ou Mural. Les ateliers en remote sont légèrement moins dynamiques (les conversations parallèles sont plus difficiles à gérer), mais produisent des résultats comparables. Mes conseils pour le remote : prévoir des breakout rooms pour les sous-groupes, utiliser le timer visible de l'outil, et allouer 30 minutes supplémentaires pour les latences de communication. J'ai facilité des sessions jusqu'à 10 participants en remote avec de bons résultats.",{"type":30,"tag":714,"props":7881,"children":7882},{},[7883,7888],{"type":30,"tag":718,"props":7884,"children":7885},{},[7886],{"type":36,"value":7887},"3. Quelle est la taille idéale du groupe ?",{"type":30,"tag":38,"props":7889,"children":7890},{},[7891],{"type":36,"value":7892},"5 à 12 personnes. En dessous de 5, on manque de perspectives et les angles morts sont nombreux. Au-dessus de 12, la coordination devient difficile et certains participants restent passifs. Pour les grands domaines, plusieurs sessions Event Storming sur des sous-domaines sont préférables à une seule session avec 20 personnes.",{"type":30,"tag":714,"props":7894,"children":7895},{},[7896,7901],{"type":30,"tag":718,"props":7897,"children":7898},{},[7899],{"type":36,"value":7900},"4. Que faire avec le résultat de l'atelier ? Comment le maintenir à jour ?",{"type":30,"tag":38,"props":7902,"children":7903},{},[7904],{"type":36,"value":7905},"Je recommande de photographier et numériser le mur immédiatement après l'atelier. Créer une page de documentation dans Confluence ou Notion avec les photos annotées et les décisions clés. Le modèle de domaine doit évoluer avec le domaine : prévoir un Event Storming de mise à jour tous les 6 à 12 mois, ou quand une refonte significative du domaine est envisagée.",{"type":30,"tag":714,"props":7907,"children":7908},{},[7909,7914],{"type":30,"tag":718,"props":7910,"children":7911},{},[7912],{"type":36,"value":7913},"5. L'Event Storming peut-il s'appliquer à un domaine technique (infrastructure, CI/CD) plutôt que métier ?",{"type":30,"tag":38,"props":7915,"children":7916},{},[7917],{"type":36,"value":7918},"Oui, avec adaptation. On parle alors de \"Process Modelling Event Storm\", la même technique appliquée aux processus techniques. Les Domain Events deviennent des événements de pipeline (\"Build triggered\", \"Tests failed\", \"Deployment rolled back\"). C'est utile pour modéliser et améliorer les processus de delivery, et j'ai utilisé ce format pour restructurer des pipelines CI/CD chez plusieurs clients.",{"type":30,"tag":67,"props":7920,"children":7921},{},[],{"type":30,"tag":225,"props":7923,"children":7924},{"cta":785,"href":786,"title":787,"type":788},[7925],{"type":30,"tag":38,"props":7926,"children":7927},{},[7928],{"type":36,"value":7929},"L'Engineering Maturity Self-Assessment couvre le domaine Architecture & Modélisation : évaluez votre niveau de maturité sur la conception du domaine, le découplage, et les pratiques de modélisation. Résultat en 10 minutes, plan d'action inclus.",{"title":8,"searchDepth":452,"depth":452,"links":7931},[7932,7933,7934,7935,7936,7937,7938,7939],{"id":7299,"depth":452,"text":7302},{"id":7359,"depth":452,"text":7362},{"id":7435,"depth":452,"text":7438},{"id":7509,"depth":452,"text":7512},{"id":7558,"depth":452,"text":7561},{"id":7640,"depth":452,"text":7643},{"id":7721,"depth":452,"text":7724},{"id":7850,"depth":452,"text":7853},"content:fr:architecture-craft:event-storming-atelier-modelisation.md","fr/architecture-craft/event-storming-atelier-modelisation.md","fr/architecture-craft/event-storming-atelier-modelisation",{"_path":218,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":7944,"description":7945,"id":1688,"date":7946,"listed":13,"nocomments":7,"hidden":7,"categories":7947,"tags":7948,"--cover":7952,"readingTime":7953,"body":7957,"_type":810,"_id":9370,"_source":812,"_file":9371,"_stem":9372,"_extension":815},"Le couplage temporel : la dette cachée dans votre code asynchrone","Le couplage temporel est invisible dans les revues de code et catastrophique en production. Comment l'identifier, le mesurer et l'éliminer progressivement.","2026-02-06",[6],[7949,7950,826,7951],"Couplage Temporel","Asynchrone","Dette Technique","covers/articles/couplage-temporel-asynchrone.jpg",{"text":22,"minutes":7954,"time":7955,"words":7956},7.505,450300,1501,{"type":27,"children":7958,"toc":9354},[7959,7964,7969,7974,7979,7984,7987,7993,8005,8010,8020,8151,8163,8166,8172,8179,8184,8194,8199,8205,8210,8731,8737,8742,8754,8763,8766,8772,8790,8813,8823,8841,8844,8850,8856,8861,9187,9193,9206,9211,9217,9222,9239,9242,9248,9258,9270,9273,9279,9292,9313,9326,9339,9342,9350],{"type":30,"tag":31,"props":7960,"children":7962},{"id":7961},"le-couplage-temporel-la-dette-cachée-dans-votre-code-asynchrone",[7963],{"type":36,"value":7944},{"type":30,"tag":38,"props":7965,"children":7966},{},[7967],{"type":36,"value":7968},"Un vendredi soir, 22h. J'étais en astreinte pour une plateforme de paiement dans le secteur bancaire. La chaîne de traitement des virements tombait de façon intermittente depuis 48 heures : 2 à 3 fois par jour, toujours \"résolue\" par un retry manuel ou une correction en base. Le bug semblait se résoudre de lui-même.",{"type":30,"tag":38,"props":7970,"children":7971},{},[7972],{"type":36,"value":7973},"Trois heures plus tard, j'avais trouvé : une contrainte d'ordre implicite entre deux services asynchrones. L'un devait recevoir un événement \"CompteValidé\" avant de traiter un événement \"VirementInitié\". Lors des pics de charge, les deux événements arrivaient dans le désordre. Le service traitait le virement sur un compte qui n'existait pas encore dans son état local.",{"type":30,"tag":38,"props":7975,"children":7976},{},[7977],{"type":36,"value":7978},"Rien dans le code ne signalait cette contrainte. Elle était documentée dans un commentaire Confluence que personne ne lisait plus.",{"type":30,"tag":38,"props":7980,"children":7981},{},[7982],{"type":36,"value":7983},"Ce problème avait un nom : le couplage temporel. Et il était présent depuis 18 mois.",{"type":30,"tag":67,"props":7985,"children":7986},{},[],{"type":30,"tag":71,"props":7988,"children":7990},{"id":7989},"ce-quest-vraiment-le-couplage-temporel",[7991],{"type":36,"value":7992},"Ce qu'est vraiment le couplage temporel",{"type":30,"tag":38,"props":7994,"children":7995},{},[7996,7998,8003],{"type":36,"value":7997},"Le couplage temporel existe quand deux opérations ",{"type":30,"tag":44,"props":7999,"children":8000},{},[8001],{"type":36,"value":8002},"doivent se dérouler dans un ordre spécifique ou dans une fenêtre temporelle précise",{"type":36,"value":8004},", et que cette contrainte n'est pas rendue explicite dans le code.",{"type":30,"tag":38,"props":8006,"children":8007},{},[8008],{"type":36,"value":8009},"C'est une contrainte implicite : rien dans le code ne vous dit que A doit se terminer avant B. Vous le savez parce que vous avez écrit le code, ou parce que vous avez lu la documentation. Le prochain développeur ne le saura peut-être pas.",{"type":30,"tag":38,"props":8011,"children":8012},{},[8013,8018],{"type":30,"tag":44,"props":8014,"children":8015},{},[8016],{"type":36,"value":8017},"Exemple basique",{"type":36,"value":8019}," :",{"type":30,"tag":185,"props":8021,"children":8023},{"className":6791,"code":8022,"language":6793,"meta":8,"style":8},"// Couplage temporel implicite :\nawait initializeDatabase();   // doit s'exécuter en premier\nawait loadConfiguration();    // dépend de la DB\nawait startHttpServer();       // dépend de la configuration\n\n// Si initializeDatabase() échoue silencieusement,\n// loadConfiguration() peut \"fonctionner\" avec des données partielles\n// et startHttpServer() démarrera avec une configuration corrompue.\n// Le bug se manifeste plus tard, dans une feature spécifique.\n",[8024],{"type":30,"tag":98,"props":8025,"children":8026},{"__ignoreMap":8},[8027,8035,8062,8087,8112,8119,8127,8135,8143],{"type":30,"tag":439,"props":8028,"children":8029},{"class":441,"line":442},[8030],{"type":30,"tag":439,"props":8031,"children":8032},{"style":446},[8033],{"type":36,"value":8034},"// Couplage temporel implicite :\n",{"type":30,"tag":439,"props":8036,"children":8037},{"class":441,"line":452},[8038,8043,8048,8052,8057],{"type":30,"tag":439,"props":8039,"children":8040},{"style":456},[8041],{"type":36,"value":8042},"await",{"type":30,"tag":439,"props":8044,"children":8045},{"style":1105},[8046],{"type":36,"value":8047}," initializeDatabase",{"type":30,"tag":439,"props":8049,"children":8050},{"style":462},[8051],{"type":36,"value":4003},{"type":30,"tag":439,"props":8053,"children":8054},{"style":922},[8055],{"type":36,"value":8056},";",{"type":30,"tag":439,"props":8058,"children":8059},{"style":446},[8060],{"type":36,"value":8061},"   // doit s'exécuter en premier\n",{"type":30,"tag":439,"props":8063,"children":8064},{"class":441,"line":478},[8065,8069,8074,8078,8082],{"type":30,"tag":439,"props":8066,"children":8067},{"style":456},[8068],{"type":36,"value":8042},{"type":30,"tag":439,"props":8070,"children":8071},{"style":1105},[8072],{"type":36,"value":8073}," loadConfiguration",{"type":30,"tag":439,"props":8075,"children":8076},{"style":462},[8077],{"type":36,"value":4003},{"type":30,"tag":439,"props":8079,"children":8080},{"style":922},[8081],{"type":36,"value":8056},{"type":30,"tag":439,"props":8083,"children":8084},{"style":446},[8085],{"type":36,"value":8086},"    // dépend de la DB\n",{"type":30,"tag":439,"props":8088,"children":8089},{"class":441,"line":508},[8090,8094,8099,8103,8107],{"type":30,"tag":439,"props":8091,"children":8092},{"style":456},[8093],{"type":36,"value":8042},{"type":30,"tag":439,"props":8095,"children":8096},{"style":1105},[8097],{"type":36,"value":8098}," startHttpServer",{"type":30,"tag":439,"props":8100,"children":8101},{"style":462},[8102],{"type":36,"value":4003},{"type":30,"tag":439,"props":8104,"children":8105},{"style":922},[8106],{"type":36,"value":8056},{"type":30,"tag":439,"props":8108,"children":8109},{"style":446},[8110],{"type":36,"value":8111},"       // dépend de la configuration\n",{"type":30,"tag":439,"props":8113,"children":8114},{"class":441,"line":517},[8115],{"type":30,"tag":439,"props":8116,"children":8117},{"emptyLinePlaceholder":13},[8118],{"type":36,"value":514},{"type":30,"tag":439,"props":8120,"children":8121},{"class":441,"line":526},[8122],{"type":30,"tag":439,"props":8123,"children":8124},{"style":446},[8125],{"type":36,"value":8126},"// Si initializeDatabase() échoue silencieusement,\n",{"type":30,"tag":439,"props":8128,"children":8129},{"class":441,"line":547},[8130],{"type":30,"tag":439,"props":8131,"children":8132},{"style":446},[8133],{"type":36,"value":8134},"// loadConfiguration() peut \"fonctionner\" avec des données partielles\n",{"type":30,"tag":439,"props":8136,"children":8137},{"class":441,"line":1097},[8138],{"type":30,"tag":439,"props":8139,"children":8140},{"style":446},[8141],{"type":36,"value":8142},"// et startHttpServer() démarrera avec une configuration corrompue.\n",{"type":30,"tag":439,"props":8144,"children":8145},{"class":441,"line":1133},[8146],{"type":30,"tag":439,"props":8147,"children":8148},{"style":446},[8149],{"type":36,"value":8150},"// Le bug se manifeste plus tard, dans une feature spécifique.\n",{"type":30,"tag":38,"props":8152,"children":8153},{},[8154,8156,8161],{"type":36,"value":8155},"Selon les données que j'observe dans mes missions, ",{"type":30,"tag":44,"props":8157,"children":8158},{},[8159],{"type":36,"value":8160},"les incidents intermittents en production représentent 40 à 60% du temps de debugging des équipes senior",{"type":36,"value":8162},", et le couplage temporel en est l'une des causes les plus fréquentes et les moins visibles.",{"type":30,"tag":67,"props":8164,"children":8165},{},[],{"type":30,"tag":71,"props":8167,"children":8169},{"id":8168},"les-3-formes-les-plus-courantes",[8170],{"type":36,"value":8171},"Les 3 formes les plus courantes",{"type":30,"tag":8173,"props":8174,"children":8176},"h3",{"id":8175},"forme-1-la-séquence-obligatoire-non-documentée",[8177],{"type":36,"value":8178},"Forme 1 : La séquence obligatoire non documentée",{"type":30,"tag":38,"props":8180,"children":8181},{},[8182],{"type":36,"value":8183},"Des opérations qui doivent être exécutées dans un ordre précis, sans que cet ordre soit rendu explicite ni protégé par du code.",{"type":30,"tag":38,"props":8185,"children":8186},{},[8187,8192],{"type":30,"tag":44,"props":8188,"children":8189},{},[8190],{"type":36,"value":8191},"Exemple",{"type":36,"value":8193}," : un service qui doit recevoir un événement \"UserCreated\" avant de pouvoir traiter un événement \"UserProfileUpdated\". Si les deux arrivent simultanément (race condition), le traitement échoue ou produit un état incohérent.",{"type":30,"tag":38,"props":8195,"children":8196},{},[8197],{"type":36,"value":8198},"Ce type d'incident coûte en moyenne 2 à 5 heures de debugging pour un développeur senior, et laisse un résidu de méfiance dans le système. L'équipe commence à \"monitorer manuellement\" ce flux, ce qui est un signal fort que la contrainte n'est pas correctement encodée.",{"type":30,"tag":8173,"props":8200,"children":8202},{"id":8201},"forme-2-les-appels-asynchrones-séquentiels-inutiles",[8203],{"type":36,"value":8204},"Forme 2 : Les appels asynchrones séquentiels inutiles",{"type":30,"tag":38,"props":8206,"children":8207},{},[8208],{"type":36,"value":8209},"Des appels asynchrones imbriqués où chaque niveau dépend du précédent, même quand ce n'est pas nécessaire. Même avec async/await, le pattern peut rester présent sous une forme différente.",{"type":30,"tag":185,"props":8211,"children":8213},{"className":6791,"code":8212,"language":6793,"meta":8,"style":8},"// ❌ Couplage temporel implicite même avec async/await\nasync function processOrder(orderId) {\n    const order = await getOrder(orderId);\n    const customer = await getCustomer(order.customerId); // dépend de order\n    const inventory = await checkInventory(order.items);  // pourrait être parallèle !\n    const payment = await processPayment(customer, order);\n    await sendConfirmation(customer, order);\n}\n\n// ✅ Parallélisation explicite pour les opérations indépendantes\nasync function processOrder(orderId) {\n    const order = await getOrder(orderId);\n    const [customer, inventory] = await Promise.all([\n        getCustomer(order.customerId),\n        checkInventory(order.items)    // pas de dépendance sur customer\n    ]);\n    const payment = await processPayment(customer, order);\n    await sendConfirmation(customer, order);\n}\n",[8214],{"type":30,"tag":98,"props":8215,"children":8216},{"__ignoreMap":8},[8217,8225,8259,8292,8339,8386,8428,8457,8464,8471,8479,8510,8541,8596,8620,8646,8658,8697,8724],{"type":30,"tag":439,"props":8218,"children":8219},{"class":441,"line":442},[8220],{"type":30,"tag":439,"props":8221,"children":8222},{"style":446},[8223],{"type":36,"value":8224},"// ❌ Couplage temporel implicite même avec async/await\n",{"type":30,"tag":439,"props":8226,"children":8227},{"class":441,"line":452},[8228,8233,8238,8243,8247,8251,8255],{"type":30,"tag":439,"props":8229,"children":8230},{"style":456},[8231],{"type":36,"value":8232},"async",{"type":30,"tag":439,"props":8234,"children":8235},{"style":456},[8236],{"type":36,"value":8237}," function",{"type":30,"tag":439,"props":8239,"children":8240},{"style":1105},[8241],{"type":36,"value":8242}," processOrder",{"type":30,"tag":439,"props":8244,"children":8245},{"style":922},[8246],{"type":36,"value":944},{"type":30,"tag":439,"props":8248,"children":8249},{"style":992},[8250],{"type":36,"value":3099},{"type":30,"tag":439,"props":8252,"children":8253},{"style":922},[8254],{"type":36,"value":1030},{"type":30,"tag":439,"props":8256,"children":8257},{"style":922},[8258],{"type":36,"value":3022},{"type":30,"tag":439,"props":8260,"children":8261},{"class":441,"line":478},[8262,8267,8271,8275,8279,8284,8288],{"type":30,"tag":439,"props":8263,"children":8264},{"style":456},[8265],{"type":36,"value":8266},"    const",{"type":30,"tag":439,"props":8268,"children":8269},{"style":1418},[8270],{"type":36,"value":4317},{"type":30,"tag":439,"props":8272,"children":8273},{"style":487},[8274],{"type":36,"value":2694},{"type":30,"tag":439,"props":8276,"children":8277},{"style":456},[8278],{"type":36,"value":3471},{"type":30,"tag":439,"props":8280,"children":8281},{"style":1105},[8282],{"type":36,"value":8283}," getOrder",{"type":30,"tag":439,"props":8285,"children":8286},{"style":462},[8287],{"type":36,"value":3831},{"type":30,"tag":439,"props":8289,"children":8290},{"style":922},[8291],{"type":36,"value":3082},{"type":30,"tag":439,"props":8293,"children":8294},{"class":441,"line":508},[8295,8299,8304,8308,8312,8317,8321,8325,8330,8334],{"type":30,"tag":439,"props":8296,"children":8297},{"style":456},[8298],{"type":36,"value":8266},{"type":30,"tag":439,"props":8300,"children":8301},{"style":1418},[8302],{"type":36,"value":8303}," customer",{"type":30,"tag":439,"props":8305,"children":8306},{"style":487},[8307],{"type":36,"value":2694},{"type":30,"tag":439,"props":8309,"children":8310},{"style":456},[8311],{"type":36,"value":3471},{"type":30,"tag":439,"props":8313,"children":8314},{"style":1105},[8315],{"type":36,"value":8316}," getCustomer",{"type":30,"tag":439,"props":8318,"children":8319},{"style":462},[8320],{"type":36,"value":4411},{"type":30,"tag":439,"props":8322,"children":8323},{"style":3480},[8324],{"type":36,"value":969},{"type":30,"tag":439,"props":8326,"children":8327},{"style":462},[8328],{"type":36,"value":8329},"customerId)",{"type":30,"tag":439,"props":8331,"children":8332},{"style":922},[8333],{"type":36,"value":8056},{"type":30,"tag":439,"props":8335,"children":8336},{"style":446},[8337],{"type":36,"value":8338}," // dépend de order\n",{"type":30,"tag":439,"props":8340,"children":8341},{"class":441,"line":517},[8342,8346,8351,8355,8359,8364,8368,8372,8377,8381],{"type":30,"tag":439,"props":8343,"children":8344},{"style":456},[8345],{"type":36,"value":8266},{"type":30,"tag":439,"props":8347,"children":8348},{"style":1418},[8349],{"type":36,"value":8350}," inventory",{"type":30,"tag":439,"props":8352,"children":8353},{"style":487},[8354],{"type":36,"value":2694},{"type":30,"tag":439,"props":8356,"children":8357},{"style":456},[8358],{"type":36,"value":3471},{"type":30,"tag":439,"props":8360,"children":8361},{"style":1105},[8362],{"type":36,"value":8363}," checkInventory",{"type":30,"tag":439,"props":8365,"children":8366},{"style":462},[8367],{"type":36,"value":4411},{"type":30,"tag":439,"props":8369,"children":8370},{"style":3480},[8371],{"type":36,"value":969},{"type":30,"tag":439,"props":8373,"children":8374},{"style":462},[8375],{"type":36,"value":8376},"items)",{"type":30,"tag":439,"props":8378,"children":8379},{"style":922},[8380],{"type":36,"value":8056},{"type":30,"tag":439,"props":8382,"children":8383},{"style":446},[8384],{"type":36,"value":8385},"  // pourrait être parallèle !\n",{"type":30,"tag":439,"props":8387,"children":8388},{"class":441,"line":526},[8389,8393,8397,8401,8405,8410,8415,8419,8424],{"type":30,"tag":439,"props":8390,"children":8391},{"style":456},[8392],{"type":36,"value":8266},{"type":30,"tag":439,"props":8394,"children":8395},{"style":1418},[8396],{"type":36,"value":3364},{"type":30,"tag":439,"props":8398,"children":8399},{"style":487},[8400],{"type":36,"value":2694},{"type":30,"tag":439,"props":8402,"children":8403},{"style":456},[8404],{"type":36,"value":3471},{"type":30,"tag":439,"props":8406,"children":8407},{"style":1105},[8408],{"type":36,"value":8409}," processPayment",{"type":30,"tag":439,"props":8411,"children":8412},{"style":462},[8413],{"type":36,"value":8414},"(customer",{"type":30,"tag":439,"props":8416,"children":8417},{"style":922},[8418],{"type":36,"value":1010},{"type":30,"tag":439,"props":8420,"children":8421},{"style":462},[8422],{"type":36,"value":8423}," order)",{"type":30,"tag":439,"props":8425,"children":8426},{"style":922},[8427],{"type":36,"value":3082},{"type":30,"tag":439,"props":8429,"children":8430},{"class":441,"line":547},[8431,8436,8441,8445,8449,8453],{"type":30,"tag":439,"props":8432,"children":8433},{"style":456},[8434],{"type":36,"value":8435},"    await",{"type":30,"tag":439,"props":8437,"children":8438},{"style":1105},[8439],{"type":36,"value":8440}," sendConfirmation",{"type":30,"tag":439,"props":8442,"children":8443},{"style":462},[8444],{"type":36,"value":8414},{"type":30,"tag":439,"props":8446,"children":8447},{"style":922},[8448],{"type":36,"value":1010},{"type":30,"tag":439,"props":8450,"children":8451},{"style":462},[8452],{"type":36,"value":8423},{"type":30,"tag":439,"props":8454,"children":8455},{"style":922},[8456],{"type":36,"value":3082},{"type":30,"tag":439,"props":8458,"children":8459},{"class":441,"line":1097},[8460],{"type":30,"tag":439,"props":8461,"children":8462},{"style":922},[8463],{"type":36,"value":3156},{"type":30,"tag":439,"props":8465,"children":8466},{"class":441,"line":1133},[8467],{"type":30,"tag":439,"props":8468,"children":8469},{"emptyLinePlaceholder":13},[8470],{"type":36,"value":514},{"type":30,"tag":439,"props":8472,"children":8473},{"class":441,"line":1183},[8474],{"type":30,"tag":439,"props":8475,"children":8476},{"style":446},[8477],{"type":36,"value":8478},"// ✅ Parallélisation explicite pour les opérations indépendantes\n",{"type":30,"tag":439,"props":8480,"children":8481},{"class":441,"line":1250},[8482,8486,8490,8494,8498,8502,8506],{"type":30,"tag":439,"props":8483,"children":8484},{"style":456},[8485],{"type":36,"value":8232},{"type":30,"tag":439,"props":8487,"children":8488},{"style":456},[8489],{"type":36,"value":8237},{"type":30,"tag":439,"props":8491,"children":8492},{"style":1105},[8493],{"type":36,"value":8242},{"type":30,"tag":439,"props":8495,"children":8496},{"style":922},[8497],{"type":36,"value":944},{"type":30,"tag":439,"props":8499,"children":8500},{"style":992},[8501],{"type":36,"value":3099},{"type":30,"tag":439,"props":8503,"children":8504},{"style":922},[8505],{"type":36,"value":1030},{"type":30,"tag":439,"props":8507,"children":8508},{"style":922},[8509],{"type":36,"value":3022},{"type":30,"tag":439,"props":8511,"children":8512},{"class":441,"line":1655},[8513,8517,8521,8525,8529,8533,8537],{"type":30,"tag":439,"props":8514,"children":8515},{"style":456},[8516],{"type":36,"value":8266},{"type":30,"tag":439,"props":8518,"children":8519},{"style":1418},[8520],{"type":36,"value":4317},{"type":30,"tag":439,"props":8522,"children":8523},{"style":487},[8524],{"type":36,"value":2694},{"type":30,"tag":439,"props":8526,"children":8527},{"style":456},[8528],{"type":36,"value":3471},{"type":30,"tag":439,"props":8530,"children":8531},{"style":1105},[8532],{"type":36,"value":8283},{"type":30,"tag":439,"props":8534,"children":8535},{"style":462},[8536],{"type":36,"value":3831},{"type":30,"tag":439,"props":8538,"children":8539},{"style":922},[8540],{"type":36,"value":3082},{"type":30,"tag":439,"props":8542,"children":8543},{"class":441,"line":1663},[8544,8548,8552,8557,8561,8565,8569,8573,8577,8582,8586,8591],{"type":30,"tag":439,"props":8545,"children":8546},{"style":456},[8547],{"type":36,"value":8266},{"type":30,"tag":439,"props":8549,"children":8550},{"style":922},[8551],{"type":36,"value":2879},{"type":30,"tag":439,"props":8553,"children":8554},{"style":1418},[8555],{"type":36,"value":8556},"customer",{"type":30,"tag":439,"props":8558,"children":8559},{"style":922},[8560],{"type":36,"value":1010},{"type":30,"tag":439,"props":8562,"children":8563},{"style":1418},[8564],{"type":36,"value":8350},{"type":30,"tag":439,"props":8566,"children":8567},{"style":922},[8568],{"type":36,"value":2689},{"type":30,"tag":439,"props":8570,"children":8571},{"style":487},[8572],{"type":36,"value":2694},{"type":30,"tag":439,"props":8574,"children":8575},{"style":456},[8576],{"type":36,"value":3471},{"type":30,"tag":439,"props":8578,"children":8580},{"style":8579},"--shiki-default:#E5C890;--shiki-default-font-style:italic;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit",[8581],{"type":36,"value":3061},{"type":30,"tag":439,"props":8583,"children":8584},{"style":3480},[8585],{"type":36,"value":969},{"type":30,"tag":439,"props":8587,"children":8588},{"style":1105},[8589],{"type":36,"value":8590},"all",{"type":30,"tag":439,"props":8592,"children":8593},{"style":462},[8594],{"type":36,"value":8595},"([\n",{"type":30,"tag":439,"props":8597,"children":8598},{"class":441,"line":1672},[8599,8604,8608,8612,8616],{"type":30,"tag":439,"props":8600,"children":8601},{"style":1105},[8602],{"type":36,"value":8603},"        getCustomer",{"type":30,"tag":439,"props":8605,"children":8606},{"style":462},[8607],{"type":36,"value":4411},{"type":30,"tag":439,"props":8609,"children":8610},{"style":3480},[8611],{"type":36,"value":969},{"type":30,"tag":439,"props":8613,"children":8614},{"style":462},[8615],{"type":36,"value":8329},{"type":30,"tag":439,"props":8617,"children":8618},{"style":922},[8619],{"type":36,"value":3348},{"type":30,"tag":439,"props":8621,"children":8622},{"class":441,"line":1688},[8623,8628,8632,8636,8641],{"type":30,"tag":439,"props":8624,"children":8625},{"style":1105},[8626],{"type":36,"value":8627},"        checkInventory",{"type":30,"tag":439,"props":8629,"children":8630},{"style":462},[8631],{"type":36,"value":4411},{"type":30,"tag":439,"props":8633,"children":8634},{"style":3480},[8635],{"type":36,"value":969},{"type":30,"tag":439,"props":8637,"children":8638},{"style":462},[8639],{"type":36,"value":8640},"items)    ",{"type":30,"tag":439,"props":8642,"children":8643},{"style":446},[8644],{"type":36,"value":8645},"// pas de dépendance sur customer\n",{"type":30,"tag":439,"props":8647,"children":8648},{"class":441,"line":1747},[8649,8654],{"type":30,"tag":439,"props":8650,"children":8651},{"style":462},[8652],{"type":36,"value":8653},"    ])",{"type":30,"tag":439,"props":8655,"children":8656},{"style":922},[8657],{"type":36,"value":3082},{"type":30,"tag":439,"props":8659,"children":8660},{"class":441,"line":1773},[8661,8665,8669,8673,8677,8681,8685,8689,8693],{"type":30,"tag":439,"props":8662,"children":8663},{"style":456},[8664],{"type":36,"value":8266},{"type":30,"tag":439,"props":8666,"children":8667},{"style":1418},[8668],{"type":36,"value":3364},{"type":30,"tag":439,"props":8670,"children":8671},{"style":487},[8672],{"type":36,"value":2694},{"type":30,"tag":439,"props":8674,"children":8675},{"style":456},[8676],{"type":36,"value":3471},{"type":30,"tag":439,"props":8678,"children":8679},{"style":1105},[8680],{"type":36,"value":8409},{"type":30,"tag":439,"props":8682,"children":8683},{"style":462},[8684],{"type":36,"value":8414},{"type":30,"tag":439,"props":8686,"children":8687},{"style":922},[8688],{"type":36,"value":1010},{"type":30,"tag":439,"props":8690,"children":8691},{"style":462},[8692],{"type":36,"value":8423},{"type":30,"tag":439,"props":8694,"children":8695},{"style":922},[8696],{"type":36,"value":3082},{"type":30,"tag":439,"props":8698,"children":8699},{"class":441,"line":1799},[8700,8704,8708,8712,8716,8720],{"type":30,"tag":439,"props":8701,"children":8702},{"style":456},[8703],{"type":36,"value":8435},{"type":30,"tag":439,"props":8705,"children":8706},{"style":1105},[8707],{"type":36,"value":8440},{"type":30,"tag":439,"props":8709,"children":8710},{"style":462},[8711],{"type":36,"value":8414},{"type":30,"tag":439,"props":8713,"children":8714},{"style":922},[8715],{"type":36,"value":1010},{"type":30,"tag":439,"props":8717,"children":8718},{"style":462},[8719],{"type":36,"value":8423},{"type":30,"tag":439,"props":8721,"children":8722},{"style":922},[8723],{"type":36,"value":3082},{"type":30,"tag":439,"props":8725,"children":8726},{"class":441,"line":1807},[8727],{"type":30,"tag":439,"props":8728,"children":8729},{"style":922},[8730],{"type":36,"value":3156},{"type":30,"tag":8173,"props":8732,"children":8734},{"id":8733},"forme-3-les-timeouts-implicites-ou-absents",[8735],{"type":36,"value":8736},"Forme 3 : Les timeouts implicites ou absents",{"type":30,"tag":38,"props":8738,"children":8739},{},[8740],{"type":36,"value":8741},"Des appels vers des services externes (API, base de données, message broker) sans timeout défini. Le comportement par défaut de la plupart des clients HTTP est d'attendre indéfiniment, ou avec un timeout système de plusieurs minutes.",{"type":30,"tag":38,"props":8743,"children":8744},{},[8745,8747,8752],{"type":36,"value":8746},"Ce qui se passe ensuite est prévisible : un service tiers commence à répondre lentement. Votre service attend. Les connexions s'accumulent. Le pool de connexions s'épuise. Votre service commence à rejeter des requêtes. La cascade se propage vers les services upstream. C'est précisément le scénario que les ",{"type":30,"tag":142,"props":8748,"children":8749},{"href":200},[8750],{"type":36,"value":8751},"patterns de résilience comme le circuit breaker et le retry",{"type":36,"value":8753}," sont conçus pour prévenir.",{"type":30,"tag":225,"props":8755,"children":8757},{"cta":227,"href":228,"title":8756,"type":230},"Vous avez des incidents de prod intermittents dont vous n'arrivez pas à identifier la cause ?",[8758],{"type":30,"tag":38,"props":8759,"children":8760},{},[8761],{"type":36,"value":8762},"Les incidents intermittents en production sont souvent la signature d'un couplage temporel mal géré. Je l'ai vu se répéter dans des organisations aussi différentes que BNP Paribas et des scale-ups de 20 développeurs. En 30 minutes, on peut identifier les patterns à risque dans votre architecture et définir les corrections prioritaires.",{"type":30,"tag":67,"props":8764,"children":8765},{},[],{"type":30,"tag":71,"props":8767,"children":8769},{"id":8768},"comment-le-détecter-dans-votre-code",[8770],{"type":36,"value":8771},"Comment le détecter dans votre code",{"type":30,"tag":38,"props":8773,"children":8774},{},[8775,8780,8782,8788],{"type":30,"tag":44,"props":8776,"children":8777},{},[8778],{"type":36,"value":8779},"Dans le code",{"type":36,"value":8781},", je cherche ces patterns comme indicateurs potentiels, et les ",{"type":30,"tag":142,"props":8783,"children":8785},{"href":8784},"/fr/dette-technique/outils-analyse-statique-2026",[8786],{"type":36,"value":8787},"outils d'analyse statique",{"type":36,"value":8789}," permettent d'automatiser une partie de cette détection à grande échelle :",{"type":30,"tag":620,"props":8791,"children":8792},{},[8793,8798,8803,8808],{"type":30,"tag":624,"props":8794,"children":8795},{},[8796],{"type":36,"value":8797},"Appels asynchrones séquentiels qui pourraient être parallèles : vérifiez si chacun dépend vraiment du précédent",{"type":30,"tag":624,"props":8799,"children":8800},{},[8801],{"type":36,"value":8802},"Absence de timeout sur les appels vers des services externes",{"type":30,"tag":624,"props":8804,"children":8805},{},[8806],{"type":36,"value":8807},"Commentaires du type \"// doit être appelé après X\" : la contrainte est documentée mais non protégée par du code",{"type":30,"tag":624,"props":8809,"children":8810},{},[8811],{"type":36,"value":8812},"Messages ou événements qui doivent être traités dans un ordre précis sans mécanisme de garantie",{"type":30,"tag":38,"props":8814,"children":8815},{},[8816,8821],{"type":30,"tag":44,"props":8817,"children":8818},{},[8819],{"type":36,"value":8820},"En production",{"type":36,"value":8822},", les signaux sont différents :",{"type":30,"tag":620,"props":8824,"children":8825},{},[8826,8831,8836],{"type":30,"tag":624,"props":8827,"children":8828},{},[8829],{"type":36,"value":8830},"Incidents intermittents qui se résolvent avec un retry → indicateur fort de race condition ou de contrainte temporelle non respectée",{"type":30,"tag":624,"props":8832,"children":8833},{},[8834],{"type":36,"value":8835},"Latence qui augmente linéairement avec la charge → indicateur d'opérations séquentielles qui pourraient être parallèles",{"type":30,"tag":624,"props":8837,"children":8838},{},[8839],{"type":36,"value":8840},"Timeouts qui se propagent en cascade → indicateur d'absence de timeout et de circuit breaker",{"type":30,"tag":67,"props":8842,"children":8843},{},[],{"type":30,"tag":71,"props":8845,"children":8847},{"id":8846},"les-3-corrections-prioritaires",[8848],{"type":36,"value":8849},"Les 3 corrections prioritaires",{"type":30,"tag":8173,"props":8851,"children":8853},{"id":8852},"idempotence-opérations-retriables-sans-effet-de-bord",[8854],{"type":36,"value":8855},"Idempotence : opérations retriables sans effet de bord",{"type":30,"tag":38,"props":8857,"children":8858},{},[8859],{"type":36,"value":8860},"Chaque opération qui peut échouer et être retentée doit être idempotente : la ré-exécuter avec le même input produit le même résultat, sans effets de bord additionnels.",{"type":30,"tag":185,"props":8862,"children":8864},{"className":4504,"code":8863,"language":4503,"meta":8,"style":8},"// ❌ Non-idempotent : chaque appel crée un enregistrement\npublic void createOrder(OrderCommand cmd) {\n    Order order = new Order(cmd);\n    orderRepository.save(order);  // crée un nouveau record à chaque appel\n}\n\n// ✅ Idempotent : même résultat quel que soit le nombre d'appels\npublic void createOrder(OrderCommand cmd) {\n    if (orderRepository.existsById(cmd.getIdempotencyKey())) {\n        return;  // déjà traité\n    }\n    Order order = new Order(cmd);\n    orderRepository.save(order);\n}\n",[8865],{"type":30,"tag":98,"props":8866,"children":8867},{"__ignoreMap":8},[8868,8876,8914,8952,8986,8993,9000,9008,9043,9095,9111,9118,9153,9180],{"type":30,"tag":439,"props":8869,"children":8870},{"class":441,"line":442},[8871],{"type":30,"tag":439,"props":8872,"children":8873},{"style":446},[8874],{"type":36,"value":8875},"// ❌ Non-idempotent : chaque appel crée un enregistrement\n",{"type":30,"tag":439,"props":8877,"children":8878},{"class":441,"line":452},[8879,8883,8887,8892,8896,8901,8906,8910],{"type":30,"tag":439,"props":8880,"children":8881},{"style":456},[8882],{"type":36,"value":4524},{"type":30,"tag":439,"props":8884,"children":8885},{"style":456},[8886],{"type":36,"value":4894},{"type":30,"tag":439,"props":8888,"children":8889},{"style":1105},[8890],{"type":36,"value":8891}," createOrder",{"type":30,"tag":439,"props":8893,"children":8894},{"style":922},[8895],{"type":36,"value":944},{"type":30,"tag":439,"props":8897,"children":8898},{"style":4558},[8899],{"type":36,"value":8900},"OrderCommand",{"type":30,"tag":439,"props":8902,"children":8903},{"style":462},[8904],{"type":36,"value":8905}," cmd",{"type":30,"tag":439,"props":8907,"children":8908},{"style":922},[8909],{"type":36,"value":1030},{"type":30,"tag":439,"props":8911,"children":8912},{"style":922},[8913],{"type":36,"value":3022},{"type":30,"tag":439,"props":8915,"children":8916},{"class":441,"line":478},[8917,8922,8926,8930,8934,8939,8943,8948],{"type":30,"tag":439,"props":8918,"children":8919},{"style":4558},[8920],{"type":36,"value":8921},"    Order",{"type":30,"tag":439,"props":8923,"children":8924},{"style":462},[8925],{"type":36,"value":4932},{"type":30,"tag":439,"props":8927,"children":8928},{"style":487},[8929],{"type":36,"value":979},{"type":30,"tag":439,"props":8931,"children":8932},{"style":456},[8933],{"type":36,"value":3627},{"type":30,"tag":439,"props":8935,"children":8936},{"style":1105},[8937],{"type":36,"value":8938}," Order",{"type":30,"tag":439,"props":8940,"children":8941},{"style":922},[8942],{"type":36,"value":944},{"type":30,"tag":439,"props":8944,"children":8945},{"style":462},[8946],{"type":36,"value":8947},"cmd",{"type":30,"tag":439,"props":8949,"children":8950},{"style":922},[8951],{"type":36,"value":4584},{"type":30,"tag":439,"props":8953,"children":8954},{"class":441,"line":508},[8955,8960,8964,8968,8972,8976,8981],{"type":30,"tag":439,"props":8956,"children":8957},{"style":462},[8958],{"type":36,"value":8959},"    orderRepository",{"type":30,"tag":439,"props":8961,"children":8962},{"style":922},[8963],{"type":36,"value":969},{"type":30,"tag":439,"props":8965,"children":8966},{"style":1105},[8967],{"type":36,"value":1166},{"type":30,"tag":439,"props":8969,"children":8970},{"style":922},[8971],{"type":36,"value":944},{"type":30,"tag":439,"props":8973,"children":8974},{"style":462},[8975],{"type":36,"value":5075},{"type":30,"tag":439,"props":8977,"children":8978},{"style":922},[8979],{"type":36,"value":8980},");",{"type":30,"tag":439,"props":8982,"children":8983},{"style":446},[8984],{"type":36,"value":8985},"  // crée un nouveau record à chaque appel\n",{"type":30,"tag":439,"props":8987,"children":8988},{"class":441,"line":517},[8989],{"type":30,"tag":439,"props":8990,"children":8991},{"style":922},[8992],{"type":36,"value":3156},{"type":30,"tag":439,"props":8994,"children":8995},{"class":441,"line":526},[8996],{"type":30,"tag":439,"props":8997,"children":8998},{"emptyLinePlaceholder":13},[8999],{"type":36,"value":514},{"type":30,"tag":439,"props":9001,"children":9002},{"class":441,"line":547},[9003],{"type":30,"tag":439,"props":9004,"children":9005},{"style":446},[9006],{"type":36,"value":9007},"// ✅ Idempotent : même résultat quel que soit le nombre d'appels\n",{"type":30,"tag":439,"props":9009,"children":9010},{"class":441,"line":1097},[9011,9015,9019,9023,9027,9031,9035,9039],{"type":30,"tag":439,"props":9012,"children":9013},{"style":456},[9014],{"type":36,"value":4524},{"type":30,"tag":439,"props":9016,"children":9017},{"style":456},[9018],{"type":36,"value":4894},{"type":30,"tag":439,"props":9020,"children":9021},{"style":1105},[9022],{"type":36,"value":8891},{"type":30,"tag":439,"props":9024,"children":9025},{"style":922},[9026],{"type":36,"value":944},{"type":30,"tag":439,"props":9028,"children":9029},{"style":4558},[9030],{"type":36,"value":8900},{"type":30,"tag":439,"props":9032,"children":9033},{"style":462},[9034],{"type":36,"value":8905},{"type":30,"tag":439,"props":9036,"children":9037},{"style":922},[9038],{"type":36,"value":1030},{"type":30,"tag":439,"props":9040,"children":9041},{"style":922},[9042],{"type":36,"value":3022},{"type":30,"tag":439,"props":9044,"children":9045},{"class":441,"line":1133},[9046,9051,9055,9060,9064,9069,9073,9077,9081,9086,9091],{"type":30,"tag":439,"props":9047,"children":9048},{"style":456},[9049],{"type":36,"value":9050},"    if",{"type":30,"tag":439,"props":9052,"children":9053},{"style":922},[9054],{"type":36,"value":3589},{"type":30,"tag":439,"props":9056,"children":9057},{"style":462},[9058],{"type":36,"value":9059},"orderRepository",{"type":30,"tag":439,"props":9061,"children":9062},{"style":922},[9063],{"type":36,"value":969},{"type":30,"tag":439,"props":9065,"children":9066},{"style":1105},[9067],{"type":36,"value":9068},"existsById",{"type":30,"tag":439,"props":9070,"children":9071},{"style":922},[9072],{"type":36,"value":944},{"type":30,"tag":439,"props":9074,"children":9075},{"style":462},[9076],{"type":36,"value":8947},{"type":30,"tag":439,"props":9078,"children":9079},{"style":922},[9080],{"type":36,"value":969},{"type":30,"tag":439,"props":9082,"children":9083},{"style":1105},[9084],{"type":36,"value":9085},"getIdempotencyKey",{"type":30,"tag":439,"props":9087,"children":9088},{"style":922},[9089],{"type":36,"value":9090},"()))",{"type":30,"tag":439,"props":9092,"children":9093},{"style":922},[9094],{"type":36,"value":3022},{"type":30,"tag":439,"props":9096,"children":9097},{"class":441,"line":1183},[9098,9102,9106],{"type":30,"tag":439,"props":9099,"children":9100},{"style":456},[9101],{"type":36,"value":1256},{"type":30,"tag":439,"props":9103,"children":9104},{"style":922},[9105],{"type":36,"value":8056},{"type":30,"tag":439,"props":9107,"children":9108},{"style":446},[9109],{"type":36,"value":9110},"  // déjà traité\n",{"type":30,"tag":439,"props":9112,"children":9113},{"class":441,"line":1250},[9114],{"type":30,"tag":439,"props":9115,"children":9116},{"style":922},[9117],{"type":36,"value":3843},{"type":30,"tag":439,"props":9119,"children":9120},{"class":441,"line":1655},[9121,9125,9129,9133,9137,9141,9145,9149],{"type":30,"tag":439,"props":9122,"children":9123},{"style":4558},[9124],{"type":36,"value":8921},{"type":30,"tag":439,"props":9126,"children":9127},{"style":462},[9128],{"type":36,"value":4932},{"type":30,"tag":439,"props":9130,"children":9131},{"style":487},[9132],{"type":36,"value":979},{"type":30,"tag":439,"props":9134,"children":9135},{"style":456},[9136],{"type":36,"value":3627},{"type":30,"tag":439,"props":9138,"children":9139},{"style":1105},[9140],{"type":36,"value":8938},{"type":30,"tag":439,"props":9142,"children":9143},{"style":922},[9144],{"type":36,"value":944},{"type":30,"tag":439,"props":9146,"children":9147},{"style":462},[9148],{"type":36,"value":8947},{"type":30,"tag":439,"props":9150,"children":9151},{"style":922},[9152],{"type":36,"value":4584},{"type":30,"tag":439,"props":9154,"children":9155},{"class":441,"line":1663},[9156,9160,9164,9168,9172,9176],{"type":30,"tag":439,"props":9157,"children":9158},{"style":462},[9159],{"type":36,"value":8959},{"type":30,"tag":439,"props":9161,"children":9162},{"style":922},[9163],{"type":36,"value":969},{"type":30,"tag":439,"props":9165,"children":9166},{"style":1105},[9167],{"type":36,"value":1166},{"type":30,"tag":439,"props":9169,"children":9170},{"style":922},[9171],{"type":36,"value":944},{"type":30,"tag":439,"props":9173,"children":9174},{"style":462},[9175],{"type":36,"value":5075},{"type":30,"tag":439,"props":9177,"children":9178},{"style":922},[9179],{"type":36,"value":4584},{"type":30,"tag":439,"props":9181,"children":9182},{"class":441,"line":1672},[9183],{"type":30,"tag":439,"props":9184,"children":9185},{"style":922},[9186],{"type":36,"value":3156},{"type":30,"tag":8173,"props":9188,"children":9190},{"id":9189},"sagas-orchestration-explicite-des-séquences-longues",[9191],{"type":36,"value":9192},"Sagas : orchestration explicite des séquences longues",{"type":30,"tag":38,"props":9194,"children":9195},{},[9196,9198,9204],{"type":36,"value":9197},"Pour les transactions distribuées qui s'étendent sur plusieurs services, le pattern Saga, décrit par Vernon Vaughn dans \"Implementing Domain-Driven Design\", rend la séquence explicite et gère les compensations en cas d'échec partiel. Réduire ce couplage est aussi un objectif à inscrire dans un ",{"type":30,"tag":142,"props":9199,"children":9201},{"href":9200},"/fr/dette-technique/programme-refactoring-approuve-business",[9202],{"type":36,"value":9203},"programme de refactoring validé par le business",{"type":36,"value":9205},", pour que ces améliorations soient planifiées et financées correctement.",{"type":30,"tag":38,"props":9207,"children":9208},{},[9209],{"type":36,"value":9210},"Chorégraphié ou orchestré, le pattern Saga transforme une contrainte implicite d'ordre en protocole explicite avec gestion des cas d'échec.",{"type":30,"tag":8173,"props":9212,"children":9214},{"id":9213},"timeouts-explicites-sur-tous-les-appels-externes",[9215],{"type":36,"value":9216},"Timeouts explicites sur tous les appels externes",{"type":30,"tag":38,"props":9218,"children":9219},{},[9220],{"type":36,"value":9221},"Tout appel vers un service externe doit avoir un timeout défini par le code, pas par la valeur par défaut du client HTTP. Ce seul changement élimine la propagation en cascade lors des dégradations de services.",{"type":30,"tag":9223,"props":9224,"children":9225},"blockquote",{},[9226],{"type":30,"tag":38,"props":9227,"children":9228},{},[9229,9231,9237],{"type":36,"value":9230},"Dans la plateforme bancaire que j'évoquais en ouverture, l'introduction de l'idempotence sur le service de virement a résolu le problème définitivement en 3 jours de développement. Le bug était présent depuis 18 mois. 18 mois de corrections manuelles, de runbooks, de post-mortems, résolus par un ",{"type":30,"tag":98,"props":9232,"children":9234},{"className":9233},[],[9235],{"type":36,"value":9236},"idempotencyKey",{"type":36,"value":9238}," et une vérification de doublon.",{"type":30,"tag":67,"props":9240,"children":9241},{},[],{"type":30,"tag":71,"props":9243,"children":9245},{"id":9244},"la-règle-dor",[9246],{"type":36,"value":9247},"La règle d'or",{"type":30,"tag":38,"props":9249,"children":9250},{},[9251,9256],{"type":30,"tag":44,"props":9252,"children":9253},{},[9254],{"type":36,"value":9255},"Chaque opération asynchrone doit être idempotente et retriable.",{"type":36,"value":9257}," Si elle ne l'est pas, la contrainte temporelle qui en découle doit être rendue explicite et protégée par du code, pas par de la documentation.",{"type":30,"tag":38,"props":9259,"children":9260},{},[9261,9263,9268],{"type":36,"value":9262},"L'introduction de l'idempotence et des timeouts explicites sur les 5 à 10 flux les plus critiques réduit de ",{"type":30,"tag":44,"props":9264,"children":9265},{},[9266],{"type":36,"value":9267},"70 à 80%",{"type":36,"value":9269}," le risque associé au couplage temporel existant dans un système.",{"type":30,"tag":67,"props":9271,"children":9272},{},[],{"type":30,"tag":71,"props":9274,"children":9276},{"id":9275},"faq-sur-le-couplage-temporel",[9277],{"type":36,"value":9278},"FAQ sur le couplage temporel",{"type":30,"tag":714,"props":9280,"children":9281},{},[9282,9287],{"type":30,"tag":718,"props":9283,"children":9284},{},[9285],{"type":36,"value":9286},"1. Quelle est la différence entre couplage temporel et couplage spatial ?",{"type":30,"tag":38,"props":9288,"children":9289},{},[9290],{"type":36,"value":9291},"Le couplage spatial (structural coupling) concerne la dépendance directe entre deux composants : A connaît et appelle B. Le couplage temporel concerne la dépendance sur l'ordre ou le timing : A doit s'exécuter avant B, ou A et B doivent s'exécuter dans la même fenêtre temporelle. Les deux sont des formes de couplage, mais le temporel est plus difficile à détecter car il n'apparaît pas dans les dépendances statiques du code. C'est précisément ce qui le rend dangereux.",{"type":30,"tag":714,"props":9293,"children":9294},{},[9295,9300],{"type":30,"tag":718,"props":9296,"children":9297},{},[9298],{"type":36,"value":9299},"2. Comment tester les problèmes de couplage temporel ?",{"type":30,"tag":38,"props":9301,"children":9302},{},[9303,9305,9311],{"type":36,"value":9304},"Le chaos engineering est la méthode la plus efficace : injecter des délais aléatoires sur les appels vers les services dépendants et observer si le système se comporte correctement. Les outils comme Chaos Monkey, Gremlin, ou Pumba permettent d'introduire des latences contrôlées en environnement de staging. Pour commencer sans outil spécifique : injecter un ",{"type":30,"tag":98,"props":9306,"children":9308},{"className":9307},[],[9309],{"type":36,"value":9310},"sleep(aléatoire)",{"type":36,"value":9312}," en environnement de test et vérifier que les timeouts et les garanties d'ordre fonctionnent comme attendu.",{"type":30,"tag":714,"props":9314,"children":9315},{},[9316,9321],{"type":30,"tag":718,"props":9317,"children":9318},{},[9319],{"type":36,"value":9320},"3. L'idempotence est-elle possible sur tous les types d'opérations ?",{"type":30,"tag":38,"props":9322,"children":9323},{},[9324],{"type":36,"value":9325},"Presque toujours, avec la bonne conception. La clé est l'idempotency key : un identifiant unique de la requête que le client génère et que le serveur utilise pour détecter les doublons. Les opérations financières, les envois d'emails, et les modifications d'état peuvent toutes être rendues idempotentes avec ce pattern. La seule limitation réelle concerne les opérations dont le résultat dépend de l'heure exacte, mais celles-ci peuvent être gérées avec un mécanisme de verrou temporel.",{"type":30,"tag":714,"props":9327,"children":9328},{},[9329,9334],{"type":30,"tag":718,"props":9330,"children":9331},{},[9332],{"type":36,"value":9333},"4. Quelle est la différence entre couplage temporel et race condition ?",{"type":30,"tag":38,"props":9335,"children":9336},{},[9337],{"type":36,"value":9338},"Une race condition est un cas particulier de couplage temporel : deux opérations concurrentes accèdent au même état partagé et le résultat dépend de l'ordre d'exécution. Le couplage temporel est plus large : il inclut aussi les séquences non-concurrentes qui doivent se dérouler dans un ordre précis. Toutes les race conditions sont des couplages temporels, mais tous les couplages temporels ne sont pas des race conditions.",{"type":30,"tag":67,"props":9340,"children":9341},{},[],{"type":30,"tag":225,"props":9343,"children":9344},{"cta":785,"href":786,"title":787,"type":788},[9345],{"type":30,"tag":38,"props":9346,"children":9347},{},[9348],{"type":36,"value":9349},"L'Engineering Maturity Self-Assessment couvre le domaine Architecture & Résilience : évaluez votre niveau sur la gestion du couplage, les patterns asynchrones, et la robustesse de vos services. Score et recommandations en 10 minutes.",{"type":30,"tag":796,"props":9351,"children":9352},{},[9353],{"type":36,"value":800},{"title":8,"searchDepth":452,"depth":452,"links":9355},[9356,9357,9362,9363,9368,9369],{"id":7989,"depth":452,"text":7992},{"id":8168,"depth":452,"text":8171,"children":9358},[9359,9360,9361],{"id":8175,"depth":478,"text":8178},{"id":8201,"depth":478,"text":8204},{"id":8733,"depth":478,"text":8736},{"id":8768,"depth":452,"text":8771},{"id":8846,"depth":452,"text":8849,"children":9364},[9365,9366,9367],{"id":8852,"depth":478,"text":8855},{"id":9189,"depth":478,"text":9192},{"id":9213,"depth":478,"text":9216},{"id":9244,"depth":452,"text":9247},{"id":9275,"depth":452,"text":9278},"content:fr:architecture-craft:couplage-temporel-code-asynchrone.md","fr/architecture-craft/couplage-temporel-code-asynchrone.md","fr/architecture-craft/couplage-temporel-code-asynchrone",{"_path":871,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9374,"description":9375,"id":1183,"date":9376,"listed":13,"nocomments":7,"hidden":7,"categories":9377,"tags":9378,"--cover":9380,"readingTime":9381,"body":9385,"_type":810,"_id":11604,"_source":812,"_file":11605,"_stem":11606,"_extension":815},"Clean Architecture : les 3 règles qui suffisent","La Clean Architecture est souvent sur-implémentée. 3 règles fondamentales couvrent 80% de la valeur sans la complexité qui fait fuir les équipes.","2026-01-26",[6],[874,824,9379],"Architecture Logicielle","covers/articles/clean-architecture-regles.jpg",{"text":22,"minutes":9382,"time":9383,"words":9384},7.835,470100,1567,{"type":27,"children":9386,"toc":11595},[9387,9392,9397,9402,9407,9412,9415,9421,9426,9431,9436,9447,9450,9456,9461,9480,9488,9506,9959,9975,9978,9984,9989,9996,10037,10580,10590,10599,10602,10608,10613,10623,10630,10663,11430,11433,11439,11449,11459,11469,11472,11478,11488,11498,11501,11507,11520,11533,11546,11559,11580,11583,11591],{"type":30,"tag":31,"props":9388,"children":9390},{"id":9389},"clean-architecture-les-3-règles-qui-suffisent",[9391],{"type":36,"value":9374},{"type":30,"tag":38,"props":9393,"children":9394},{},[9395],{"type":36,"value":9396},"J'accompagnais une équipe engineering chez Canal+ (20 développeurs). Une API de 45 endpoints, 4 ans de code accumulé, une couverture de tests à 28%. Les nouveaux développeurs atteignaient leur autonomie en 7 semaines. Pas parce qu'ils étaient mauvais, parce que le code était impossible à naviguer.",{"type":30,"tag":38,"props":9398,"children":9399},{},[9400],{"type":36,"value":9401},"Ils m'ont demandé si la Clean Architecture pouvait les aider. Ma réponse : oui, mais pas telle qu'on l'applique généralement.",{"type":30,"tag":38,"props":9403,"children":9404},{},[9405],{"type":36,"value":9406},"J'ai introduit 3 règles, pas les 8 couches du diagramme en cercles de Robert Martin. En 4 mois : couverture de tests passée de 28% à 67%, lead time baissé de 35%, onboarding raccourci de 7 à 3 semaines.",{"type":30,"tag":38,"props":9408,"children":9409},{},[9410],{"type":36,"value":9411},"La Clean Architecture n'est pas une religion. C'est un outil. Et comme tout outil, c'est la maîtrise qui compte, pas la conformité.",{"type":30,"tag":67,"props":9413,"children":9414},{},[],{"type":30,"tag":71,"props":9416,"children":9418},{"id":9417},"pourquoi-la-clean-architecture-divise-les-équipes",[9419],{"type":36,"value":9420},"Pourquoi la Clean Architecture divise les équipes",{"type":30,"tag":38,"props":9422,"children":9423},{},[9424],{"type":36,"value":9425},"L'architecture \"full Clean\" avec des entités, des use cases, des interfaces presenters, des controllers, des gateways se justifie dans des contextes spécifiques : applications complexes avec des règles métier riches, systèmes qui doivent changer de framework ou de base de données, projets long-terme avec de nombreux développeurs.",{"type":30,"tag":38,"props":9427,"children":9428},{},[9429],{"type":36,"value":9430},"Dans la plupart des contextes (une API REST de taille moyenne, une application en phase de croissance), la mise en place complète génère plus de complexité qu'elle n'en résout. Les symptômes sont toujours les mêmes : des dizaines de fichiers pour chaque feature, des indirections qui rendent le debug cauchemardesque, des juniors qui passent 3 jours à comprendre où écrire un bout de logique.",{"type":30,"tag":38,"props":9432,"children":9433},{},[9434],{"type":36,"value":9435},"Le problème n'est pas la Clean Architecture. C'est l'application dogmatique de sa forme sans en comprendre l'essence.",{"type":30,"tag":38,"props":9437,"children":9438},{},[9439,9441,9445],{"type":36,"value":9440},"Robert C. Martin (Uncle Bob) a formalisé ces principes dans son livre éponyme de 2017. Ce qu'il dit, et que beaucoup oublient : les couches ne sont pas une prescription rigide. Ce sont des guides. L'essence, elle, tient en 3 règles. Et quand une équipe adopte cette architecture, documenter les décisions structurantes dans des ",{"type":30,"tag":142,"props":9442,"children":9443},{"href":144},[9444],{"type":36,"value":7632},{"type":36,"value":9446}," évite de refaire les mêmes débats à chaque nouveau développeur.",{"type":30,"tag":67,"props":9448,"children":9449},{},[],{"type":30,"tag":71,"props":9451,"children":9453},{"id":9452},"règle-1-la-règle-de-dépendance-les-flèches-pointent-vers-lintérieur",[9454],{"type":36,"value":9455},"Règle 1 : La règle de dépendance : les flèches pointent vers l'intérieur",{"type":30,"tag":38,"props":9457,"children":9458},{},[9459],{"type":36,"value":9460},"C'est la règle fondamentale. Dans un système bien architecturé, les modules de haut niveau (métier, règles business) ne doivent jamais dépendre des modules de bas niveau (frameworks, base de données, HTTP).",{"type":30,"tag":38,"props":9462,"children":9463},{},[9464,9466,9471,9473,9478],{"type":36,"value":9465},"La dépendance va dans une seule direction : ",{"type":30,"tag":44,"props":9467,"children":9468},{},[9469],{"type":36,"value":9470},"de l'extérieur vers l'intérieur",{"type":36,"value":9472},". Les couches externes dépendent des couches internes. Jamais l'inverse. C'est exactement ce que formule le ",{"type":30,"tag":142,"props":9474,"children":9475},{"href":817},[9476],{"type":36,"value":9477},"Dependency Inversion Principle",{"type":36,"value":9479},", et l'appliquer concrètement est le premier pas vers une codebase testable et maintenable.",{"type":30,"tag":38,"props":9481,"children":9482},{},[9483],{"type":30,"tag":44,"props":9484,"children":9485},{},[9486],{"type":36,"value":9487},"Ce que cela signifie concrètement :",{"type":30,"tag":620,"props":9489,"children":9490},{},[9491,9496,9501],{"type":30,"tag":624,"props":9492,"children":9493},{},[9494],{"type":36,"value":9495},"Votre logique métier ne doit pas importer Spring, Express, Django, ou aucun framework",{"type":30,"tag":624,"props":9497,"children":9498},{},[9499],{"type":36,"value":9500},"Votre logique métier ne doit pas importer de driver de base de données ou d'ORM",{"type":30,"tag":624,"props":9502,"children":9503},{},[9504],{"type":36,"value":9505},"Votre logique métier ne doit pas connaître le format HTTP (statut codes, headers)",{"type":30,"tag":185,"props":9507,"children":9509},{"className":4504,"code":9508,"language":4503,"meta":8,"style":8},"// Mauvais : la logique métier dépend de Spring\n@Service\npublic class OrderService {\n    @Autowired\n    private OrderRepository repository; // dépendance sur Spring Data\n\n    public void processOrder(Long orderId) {\n        Order order = repository.findById(orderId)\n            .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); // dépendance sur Spring HTTP\n        // logique métier...\n    }\n}\n\n// Mieux : la logique métier ne connaît pas Spring\npublic class ProcessOrderUseCase {\n    private final OrderRepository repository; // interface, pas Spring Data\n\n    public void execute(OrderId orderId) {\n        Order order = repository.findById(orderId)\n            .orElseThrow(() -> new OrderNotFoundException(orderId)); // exception métier\n        // logique métier pure...\n    }\n}\n",[9510],{"type":30,"tag":98,"props":9511,"children":9512},{"__ignoreMap":8},[9513,9521,9532,9551,9563,9587,9594,9630,9669,9725,9733,9740,9747,9754,9762,9782,9810,9817,9854,9893,9937,9945,9952],{"type":30,"tag":439,"props":9514,"children":9515},{"class":441,"line":442},[9516],{"type":30,"tag":439,"props":9517,"children":9518},{"style":446},[9519],{"type":36,"value":9520},"// Mauvais : la logique métier dépend de Spring\n",{"type":30,"tag":439,"props":9522,"children":9523},{"class":441,"line":452},[9524,9528],{"type":30,"tag":439,"props":9525,"children":9526},{"style":4665},[9527],{"type":36,"value":4668},{"type":30,"tag":439,"props":9529,"children":9530},{"style":4671},[9531],{"type":36,"value":4674},{"type":30,"tag":439,"props":9533,"children":9534},{"class":441,"line":478},[9535,9539,9543,9547],{"type":30,"tag":439,"props":9536,"children":9537},{"style":456},[9538],{"type":36,"value":4524},{"type":30,"tag":439,"props":9540,"children":9541},{"style":456},[9542],{"type":36,"value":4686},{"type":30,"tag":439,"props":9544,"children":9545},{"style":916},[9546],{"type":36,"value":919},{"type":30,"tag":439,"props":9548,"children":9549},{"style":922},[9550],{"type":36,"value":3022},{"type":30,"tag":439,"props":9552,"children":9553},{"class":441,"line":508},[9554,9558],{"type":30,"tag":439,"props":9555,"children":9556},{"style":4665},[9557],{"type":36,"value":5300},{"type":30,"tag":439,"props":9559,"children":9560},{"style":4671},[9561],{"type":36,"value":9562},"Autowired\n",{"type":30,"tag":439,"props":9564,"children":9565},{"class":441,"line":517},[9566,9570,9574,9578,9582],{"type":30,"tag":439,"props":9567,"children":9568},{"style":456},[9569],{"type":36,"value":4710},{"type":30,"tag":439,"props":9571,"children":9572},{"style":4558},[9573],{"type":36,"value":1449},{"type":30,"tag":439,"props":9575,"children":9576},{"style":462},[9577],{"type":36,"value":1714},{"type":30,"tag":439,"props":9579,"children":9580},{"style":922},[9581],{"type":36,"value":8056},{"type":30,"tag":439,"props":9583,"children":9584},{"style":446},[9585],{"type":36,"value":9586}," // dépendance sur Spring Data\n",{"type":30,"tag":439,"props":9588,"children":9589},{"class":441,"line":526},[9590],{"type":30,"tag":439,"props":9591,"children":9592},{"emptyLinePlaceholder":13},[9593],{"type":36,"value":514},{"type":30,"tag":439,"props":9595,"children":9596},{"class":441,"line":547},[9597,9601,9605,9609,9613,9618,9622,9626],{"type":30,"tag":439,"props":9598,"children":9599},{"style":456},[9600],{"type":36,"value":4767},{"type":30,"tag":439,"props":9602,"children":9603},{"style":456},[9604],{"type":36,"value":4894},{"type":30,"tag":439,"props":9606,"children":9607},{"style":1105},[9608],{"type":36,"value":8242},{"type":30,"tag":439,"props":9610,"children":9611},{"style":922},[9612],{"type":36,"value":944},{"type":30,"tag":439,"props":9614,"children":9615},{"style":4558},[9616],{"type":36,"value":9617},"Long",{"type":30,"tag":439,"props":9619,"children":9620},{"style":992},[9621],{"type":36,"value":3677},{"type":30,"tag":439,"props":9623,"children":9624},{"style":922},[9625],{"type":36,"value":1030},{"type":30,"tag":439,"props":9627,"children":9628},{"style":922},[9629],{"type":36,"value":3022},{"type":30,"tag":439,"props":9631,"children":9632},{"class":441,"line":1097},[9633,9637,9641,9645,9649,9653,9657,9661,9665],{"type":30,"tag":439,"props":9634,"children":9635},{"style":4558},[9636],{"type":36,"value":4927},{"type":30,"tag":439,"props":9638,"children":9639},{"style":462},[9640],{"type":36,"value":4932},{"type":30,"tag":439,"props":9642,"children":9643},{"style":487},[9644],{"type":36,"value":979},{"type":30,"tag":439,"props":9646,"children":9647},{"style":462},[9648],{"type":36,"value":1714},{"type":30,"tag":439,"props":9650,"children":9651},{"style":922},[9652],{"type":36,"value":969},{"type":30,"tag":439,"props":9654,"children":9655},{"style":1105},[9656],{"type":36,"value":3826},{"type":30,"tag":439,"props":9658,"children":9659},{"style":922},[9660],{"type":36,"value":944},{"type":30,"tag":439,"props":9662,"children":9663},{"style":462},[9664],{"type":36,"value":3099},{"type":30,"tag":439,"props":9666,"children":9667},{"style":922},[9668],{"type":36,"value":1180},{"type":30,"tag":439,"props":9670,"children":9671},{"class":441,"line":1133},[9672,9676,9680,9684,9688,9692,9697,9701,9706,9710,9715,9720],{"type":30,"tag":439,"props":9673,"children":9674},{"style":922},[9675],{"type":36,"value":4968},{"type":30,"tag":439,"props":9677,"children":9678},{"style":1105},[9679],{"type":36,"value":4973},{"type":30,"tag":439,"props":9681,"children":9682},{"style":922},[9683],{"type":36,"value":4978},{"type":30,"tag":439,"props":9685,"children":9686},{"style":456},[9687],{"type":36,"value":1519},{"type":30,"tag":439,"props":9689,"children":9690},{"style":456},[9691],{"type":36,"value":3627},{"type":30,"tag":439,"props":9693,"children":9694},{"style":1105},[9695],{"type":36,"value":9696}," ResponseStatusException",{"type":30,"tag":439,"props":9698,"children":9699},{"style":922},[9700],{"type":36,"value":944},{"type":30,"tag":439,"props":9702,"children":9703},{"style":462},[9704],{"type":36,"value":9705},"HttpStatus",{"type":30,"tag":439,"props":9707,"children":9708},{"style":922},[9709],{"type":36,"value":969},{"type":30,"tag":439,"props":9711,"children":9712},{"style":462},[9713],{"type":36,"value":9714},"NOT_FOUND",{"type":30,"tag":439,"props":9716,"children":9717},{"style":922},[9718],{"type":36,"value":9719},"));",{"type":30,"tag":439,"props":9721,"children":9722},{"style":446},[9723],{"type":36,"value":9724}," // dépendance sur Spring HTTP\n",{"type":30,"tag":439,"props":9726,"children":9727},{"class":441,"line":1183},[9728],{"type":30,"tag":439,"props":9729,"children":9730},{"style":446},[9731],{"type":36,"value":9732},"        // logique métier...\n",{"type":30,"tag":439,"props":9734,"children":9735},{"class":441,"line":1250},[9736],{"type":30,"tag":439,"props":9737,"children":9738},{"style":922},[9739],{"type":36,"value":3843},{"type":30,"tag":439,"props":9741,"children":9742},{"class":441,"line":1655},[9743],{"type":30,"tag":439,"props":9744,"children":9745},{"style":922},[9746],{"type":36,"value":3156},{"type":30,"tag":439,"props":9748,"children":9749},{"class":441,"line":1663},[9750],{"type":30,"tag":439,"props":9751,"children":9752},{"emptyLinePlaceholder":13},[9753],{"type":36,"value":514},{"type":30,"tag":439,"props":9755,"children":9756},{"class":441,"line":1672},[9757],{"type":30,"tag":439,"props":9758,"children":9759},{"style":446},[9760],{"type":36,"value":9761},"// Mieux : la logique métier ne connaît pas Spring\n",{"type":30,"tag":439,"props":9763,"children":9764},{"class":441,"line":1688},[9765,9769,9773,9778],{"type":30,"tag":439,"props":9766,"children":9767},{"style":456},[9768],{"type":36,"value":4524},{"type":30,"tag":439,"props":9770,"children":9771},{"style":456},[9772],{"type":36,"value":4686},{"type":30,"tag":439,"props":9774,"children":9775},{"style":916},[9776],{"type":36,"value":9777}," ProcessOrderUseCase",{"type":30,"tag":439,"props":9779,"children":9780},{"style":922},[9781],{"type":36,"value":3022},{"type":30,"tag":439,"props":9783,"children":9784},{"class":441,"line":1747},[9785,9789,9793,9797,9801,9805],{"type":30,"tag":439,"props":9786,"children":9787},{"style":456},[9788],{"type":36,"value":4710},{"type":30,"tag":439,"props":9790,"children":9791},{"style":456},[9792],{"type":36,"value":4715},{"type":30,"tag":439,"props":9794,"children":9795},{"style":4558},[9796],{"type":36,"value":1449},{"type":30,"tag":439,"props":9798,"children":9799},{"style":462},[9800],{"type":36,"value":1714},{"type":30,"tag":439,"props":9802,"children":9803},{"style":922},[9804],{"type":36,"value":8056},{"type":30,"tag":439,"props":9806,"children":9807},{"style":446},[9808],{"type":36,"value":9809}," // interface, pas Spring Data\n",{"type":30,"tag":439,"props":9811,"children":9812},{"class":441,"line":1773},[9813],{"type":30,"tag":439,"props":9814,"children":9815},{"emptyLinePlaceholder":13},[9816],{"type":36,"value":514},{"type":30,"tag":439,"props":9818,"children":9819},{"class":441,"line":1799},[9820,9824,9828,9833,9837,9842,9846,9850],{"type":30,"tag":439,"props":9821,"children":9822},{"style":456},[9823],{"type":36,"value":4767},{"type":30,"tag":439,"props":9825,"children":9826},{"style":456},[9827],{"type":36,"value":4894},{"type":30,"tag":439,"props":9829,"children":9830},{"style":1105},[9831],{"type":36,"value":9832}," execute",{"type":30,"tag":439,"props":9834,"children":9835},{"style":922},[9836],{"type":36,"value":944},{"type":30,"tag":439,"props":9838,"children":9839},{"style":4558},[9840],{"type":36,"value":9841},"OrderId",{"type":30,"tag":439,"props":9843,"children":9844},{"style":992},[9845],{"type":36,"value":3677},{"type":30,"tag":439,"props":9847,"children":9848},{"style":922},[9849],{"type":36,"value":1030},{"type":30,"tag":439,"props":9851,"children":9852},{"style":922},[9853],{"type":36,"value":3022},{"type":30,"tag":439,"props":9855,"children":9856},{"class":441,"line":1807},[9857,9861,9865,9869,9873,9877,9881,9885,9889],{"type":30,"tag":439,"props":9858,"children":9859},{"style":4558},[9860],{"type":36,"value":4927},{"type":30,"tag":439,"props":9862,"children":9863},{"style":462},[9864],{"type":36,"value":4932},{"type":30,"tag":439,"props":9866,"children":9867},{"style":487},[9868],{"type":36,"value":979},{"type":30,"tag":439,"props":9870,"children":9871},{"style":462},[9872],{"type":36,"value":1714},{"type":30,"tag":439,"props":9874,"children":9875},{"style":922},[9876],{"type":36,"value":969},{"type":30,"tag":439,"props":9878,"children":9879},{"style":1105},[9880],{"type":36,"value":3826},{"type":30,"tag":439,"props":9882,"children":9883},{"style":922},[9884],{"type":36,"value":944},{"type":30,"tag":439,"props":9886,"children":9887},{"style":462},[9888],{"type":36,"value":3099},{"type":30,"tag":439,"props":9890,"children":9891},{"style":922},[9892],{"type":36,"value":1180},{"type":30,"tag":439,"props":9894,"children":9895},{"class":441,"line":1859},[9896,9900,9904,9908,9912,9916,9920,9924,9928,9932],{"type":30,"tag":439,"props":9897,"children":9898},{"style":922},[9899],{"type":36,"value":4968},{"type":30,"tag":439,"props":9901,"children":9902},{"style":1105},[9903],{"type":36,"value":4973},{"type":30,"tag":439,"props":9905,"children":9906},{"style":922},[9907],{"type":36,"value":4978},{"type":30,"tag":439,"props":9909,"children":9910},{"style":456},[9911],{"type":36,"value":1519},{"type":30,"tag":439,"props":9913,"children":9914},{"style":456},[9915],{"type":36,"value":3627},{"type":30,"tag":439,"props":9917,"children":9918},{"style":1105},[9919],{"type":36,"value":4991},{"type":30,"tag":439,"props":9921,"children":9922},{"style":922},[9923],{"type":36,"value":944},{"type":30,"tag":439,"props":9925,"children":9926},{"style":462},[9927],{"type":36,"value":3099},{"type":30,"tag":439,"props":9929,"children":9930},{"style":922},[9931],{"type":36,"value":9719},{"type":30,"tag":439,"props":9933,"children":9934},{"style":446},[9935],{"type":36,"value":9936}," // exception métier\n",{"type":30,"tag":439,"props":9938,"children":9939},{"class":441,"line":1904},[9940],{"type":30,"tag":439,"props":9941,"children":9942},{"style":446},[9943],{"type":36,"value":9944},"        // logique métier pure...\n",{"type":30,"tag":439,"props":9946,"children":9947},{"class":441,"line":1965},[9948],{"type":30,"tag":439,"props":9949,"children":9950},{"style":922},[9951],{"type":36,"value":3843},{"type":30,"tag":439,"props":9953,"children":9954},{"class":441,"line":1977},[9955],{"type":30,"tag":439,"props":9956,"children":9957},{"style":922},[9958],{"type":36,"value":3156},{"type":30,"tag":38,"props":9960,"children":9961},{},[9962,9967,9969,9973],{"type":30,"tag":44,"props":9963,"children":9964},{},[9965],{"type":36,"value":9966},"Pourquoi c'est important :",{"type":36,"value":9968}," si votre logique métier est indépendante des frameworks, vous pouvez la tester sans démarrer de serveur HTTP et sans base de données. Vos tests métier s'exécutent en millisecondes, ce qui résout précisément le problème des ",{"type":30,"tag":142,"props":9970,"children":9971},{"href":5516},[9972],{"type":36,"value":5519},{"type":36,"value":9974}," où chaque test démarre une vraie infrastructure. Et si vous changez de framework un jour, la logique métier n'est pas touchée.",{"type":30,"tag":67,"props":9976,"children":9977},{},[],{"type":30,"tag":71,"props":9979,"children":9981},{"id":9980},"règle-2-les-entités-métier-ne-connaissent-pas-linfrastructure",[9982],{"type":36,"value":9983},"Règle 2 : Les entités métier ne connaissent pas l'infrastructure",{"type":30,"tag":38,"props":9985,"children":9986},{},[9987],{"type":36,"value":9988},"Vos objets métier (Order, User, Product, Contract) doivent pouvoir exister et être testés sans base de données, sans framework de persistance, sans format de sérialisation.",{"type":30,"tag":38,"props":9990,"children":9991},{},[9992],{"type":30,"tag":44,"props":9993,"children":9994},{},[9995],{"type":36,"value":9487},{"type":30,"tag":620,"props":9997,"children":9998},{},[9999,10019,10032],{"type":30,"tag":624,"props":10000,"children":10001},{},[10002,10004,10010,10011,10017],{"type":36,"value":10003},"Pas d'annotations JPA (",{"type":30,"tag":98,"props":10005,"children":10007},{"className":10006},[],[10008],{"type":36,"value":10009},"@Entity",{"type":36,"value":1360},{"type":30,"tag":98,"props":10012,"children":10014},{"className":10013},[],[10015],{"type":36,"value":10016},"@Column",{"type":36,"value":10018},") sur vos classes métier",{"type":30,"tag":624,"props":10020,"children":10021},{},[10022,10024,10030],{"type":36,"value":10023},"Pas de décorateurs de sérialisation (",{"type":30,"tag":98,"props":10025,"children":10027},{"className":10026},[],[10028],{"type":36,"value":10029},"@JsonProperty",{"type":36,"value":10031},") sur vos domaines objects",{"type":30,"tag":624,"props":10033,"children":10034},{},[10035],{"type":36,"value":10036},"Pas d'héritage de classes framework dans vos entités métier",{"type":30,"tag":185,"props":10038,"children":10040},{"className":2993,"code":10039,"language":2992,"meta":8,"style":8},"// Entité métier polluée par l'infrastructure\n@Entity()\n@Table({ name: 'orders' })\nexport class Order {\n    @PrimaryGeneratedColumn()\n    @ApiProperty()  // annotation Swagger\n    id: number;\n\n    @Column()\n    @IsNotEmpty()   // annotation validation HTTP\n    customerId: number;\n}\n\n// Entité métier pure\nexport class Order {\n    constructor(\n        public readonly id: OrderId,\n        public readonly customerId: CustomerId,\n        private status: OrderStatus\n    ) {}\n\n    confirm(): void {\n        if (this.status !== OrderStatus.PENDING) {\n            throw new OrderAlreadyConfirmedException(this.id);\n        }\n        this.status = OrderStatus.CONFIRMED;\n    }\n}\n// La persistance est gérée par une entité séparée dans la couche infrastructure\n",[10041],{"type":30,"tag":98,"props":10042,"children":10043},{"__ignoreMap":8},[10044,10052,10069,10111,10131,10147,10168,10189,10196,10212,10233,10253,10260,10267,10275,10294,10305,10334,10363,10383,10394,10401,10425,10478,10515,10522,10558,10565,10572],{"type":30,"tag":439,"props":10045,"children":10046},{"class":441,"line":442},[10047],{"type":30,"tag":439,"props":10048,"children":10049},{"style":446},[10050],{"type":36,"value":10051},"// Entité métier polluée par l'infrastructure\n",{"type":30,"tag":439,"props":10053,"children":10054},{"class":441,"line":452},[10055,10060,10065],{"type":30,"tag":439,"props":10056,"children":10058},{"style":10057},"--shiki-default:#8CAAEE;--shiki-default-font-style:italic;--shiki-dark:#E1E4E8;--shiki-dark-font-style:inherit",[10059],{"type":36,"value":4668},{"type":30,"tag":439,"props":10061,"children":10062},{"style":1105},[10063],{"type":36,"value":10064},"Entity",{"type":30,"tag":439,"props":10066,"children":10067},{"style":4665},[10068],{"type":36,"value":2753},{"type":30,"tag":439,"props":10070,"children":10071},{"class":441,"line":478},[10072,10076,10081,10085,10089,10094,10098,10103,10107],{"type":30,"tag":439,"props":10073,"children":10074},{"style":10057},[10075],{"type":36,"value":4668},{"type":30,"tag":439,"props":10077,"children":10078},{"style":1105},[10079],{"type":36,"value":10080},"Table",{"type":30,"tag":439,"props":10082,"children":10083},{"style":4665},[10084],{"type":36,"value":944},{"type":30,"tag":439,"props":10086,"children":10087},{"style":922},[10088],{"type":36,"value":2297},{"type":30,"tag":439,"props":10090,"children":10091},{"style":4665},[10092],{"type":36,"value":10093}," name",{"type":30,"tag":439,"props":10095,"children":10096},{"style":3480},[10097],{"type":36,"value":1504},{"type":30,"tag":439,"props":10099,"children":10100},{"style":1002},[10101],{"type":36,"value":10102}," 'orders'",{"type":30,"tag":439,"props":10104,"children":10105},{"style":922},[10106],{"type":36,"value":4111},{"type":30,"tag":439,"props":10108,"children":10109},{"style":4665},[10110],{"type":36,"value":1180},{"type":30,"tag":439,"props":10112,"children":10113},{"class":441,"line":508},[10114,10119,10123,10127],{"type":30,"tag":439,"props":10115,"children":10116},{"style":456},[10117],{"type":36,"value":10118},"export",{"type":30,"tag":439,"props":10120,"children":10121},{"style":456},[10122],{"type":36,"value":4686},{"type":30,"tag":439,"props":10124,"children":10125},{"style":916},[10126],{"type":36,"value":8938},{"type":30,"tag":439,"props":10128,"children":10129},{"style":922},[10130],{"type":36,"value":3022},{"type":30,"tag":439,"props":10132,"children":10133},{"class":441,"line":517},[10134,10138,10143],{"type":30,"tag":439,"props":10135,"children":10136},{"style":10057},[10137],{"type":36,"value":5300},{"type":30,"tag":439,"props":10139,"children":10140},{"style":1105},[10141],{"type":36,"value":10142},"PrimaryGeneratedColumn",{"type":30,"tag":439,"props":10144,"children":10145},{"style":4665},[10146],{"type":36,"value":2753},{"type":30,"tag":439,"props":10148,"children":10149},{"class":441,"line":526},[10150,10154,10159,10163],{"type":30,"tag":439,"props":10151,"children":10152},{"style":10057},[10153],{"type":36,"value":5300},{"type":30,"tag":439,"props":10155,"children":10156},{"style":1105},[10157],{"type":36,"value":10158},"ApiProperty",{"type":30,"tag":439,"props":10160,"children":10161},{"style":4665},[10162],{"type":36,"value":4003},{"type":30,"tag":439,"props":10164,"children":10165},{"style":446},[10166],{"type":36,"value":10167},"  // annotation Swagger\n",{"type":30,"tag":439,"props":10169,"children":10170},{"class":441,"line":547},[10171,10177,10181,10185],{"type":30,"tag":439,"props":10172,"children":10174},{"style":10173},"--shiki-default:#C6D0F5;--shiki-dark:#FFAB70",[10175],{"type":36,"value":10176},"    id",{"type":30,"tag":439,"props":10178,"children":10179},{"style":487},[10180],{"type":36,"value":1504},{"type":30,"tag":439,"props":10182,"children":10183},{"style":1638},[10184],{"type":36,"value":3205},{"type":30,"tag":439,"props":10186,"children":10187},{"style":922},[10188],{"type":36,"value":3082},{"type":30,"tag":439,"props":10190,"children":10191},{"class":441,"line":1097},[10192],{"type":30,"tag":439,"props":10193,"children":10194},{"emptyLinePlaceholder":13},[10195],{"type":36,"value":514},{"type":30,"tag":439,"props":10197,"children":10198},{"class":441,"line":1133},[10199,10203,10208],{"type":30,"tag":439,"props":10200,"children":10201},{"style":10057},[10202],{"type":36,"value":5300},{"type":30,"tag":439,"props":10204,"children":10205},{"style":1105},[10206],{"type":36,"value":10207},"Column",{"type":30,"tag":439,"props":10209,"children":10210},{"style":4665},[10211],{"type":36,"value":2753},{"type":30,"tag":439,"props":10213,"children":10214},{"class":441,"line":1183},[10215,10219,10224,10228],{"type":30,"tag":439,"props":10216,"children":10217},{"style":10057},[10218],{"type":36,"value":5300},{"type":30,"tag":439,"props":10220,"children":10221},{"style":1105},[10222],{"type":36,"value":10223},"IsNotEmpty",{"type":30,"tag":439,"props":10225,"children":10226},{"style":4665},[10227],{"type":36,"value":4003},{"type":30,"tag":439,"props":10229,"children":10230},{"style":446},[10231],{"type":36,"value":10232},"   // annotation validation HTTP\n",{"type":30,"tag":439,"props":10234,"children":10235},{"class":441,"line":1250},[10236,10241,10245,10249],{"type":30,"tag":439,"props":10237,"children":10238},{"style":10173},[10239],{"type":36,"value":10240},"    customerId",{"type":30,"tag":439,"props":10242,"children":10243},{"style":487},[10244],{"type":36,"value":1504},{"type":30,"tag":439,"props":10246,"children":10247},{"style":1638},[10248],{"type":36,"value":3205},{"type":30,"tag":439,"props":10250,"children":10251},{"style":922},[10252],{"type":36,"value":3082},{"type":30,"tag":439,"props":10254,"children":10255},{"class":441,"line":1655},[10256],{"type":30,"tag":439,"props":10257,"children":10258},{"style":922},[10259],{"type":36,"value":3156},{"type":30,"tag":439,"props":10261,"children":10262},{"class":441,"line":1663},[10263],{"type":30,"tag":439,"props":10264,"children":10265},{"emptyLinePlaceholder":13},[10266],{"type":36,"value":514},{"type":30,"tag":439,"props":10268,"children":10269},{"class":441,"line":1672},[10270],{"type":30,"tag":439,"props":10271,"children":10272},{"style":446},[10273],{"type":36,"value":10274},"// Entité métier pure\n",{"type":30,"tag":439,"props":10276,"children":10277},{"class":441,"line":1688},[10278,10282,10286,10290],{"type":30,"tag":439,"props":10279,"children":10280},{"style":456},[10281],{"type":36,"value":10118},{"type":30,"tag":439,"props":10283,"children":10284},{"style":456},[10285],{"type":36,"value":4686},{"type":30,"tag":439,"props":10287,"children":10288},{"style":916},[10289],{"type":36,"value":8938},{"type":30,"tag":439,"props":10291,"children":10292},{"style":922},[10293],{"type":36,"value":3022},{"type":30,"tag":439,"props":10295,"children":10296},{"class":441,"line":1747},[10297,10301],{"type":30,"tag":439,"props":10298,"children":10299},{"style":456},[10300],{"type":36,"value":3313},{"type":30,"tag":439,"props":10302,"children":10303},{"style":922},[10304],{"type":36,"value":3318},{"type":30,"tag":439,"props":10306,"children":10307},{"class":441,"line":1773},[10308,10313,10317,10321,10325,10330],{"type":30,"tag":439,"props":10309,"children":10310},{"style":456},[10311],{"type":36,"value":10312},"        public",{"type":30,"tag":439,"props":10314,"children":10315},{"style":456},[10316],{"type":36,"value":3331},{"type":30,"tag":439,"props":10318,"children":10319},{"style":992},[10320],{"type":36,"value":4079},{"type":30,"tag":439,"props":10322,"children":10323},{"style":487},[10324],{"type":36,"value":1504},{"type":30,"tag":439,"props":10326,"children":10327},{"style":916},[10328],{"type":36,"value":10329}," OrderId",{"type":30,"tag":439,"props":10331,"children":10332},{"style":922},[10333],{"type":36,"value":3348},{"type":30,"tag":439,"props":10335,"children":10336},{"class":441,"line":1799},[10337,10341,10345,10350,10354,10359],{"type":30,"tag":439,"props":10338,"children":10339},{"style":456},[10340],{"type":36,"value":10312},{"type":30,"tag":439,"props":10342,"children":10343},{"style":456},[10344],{"type":36,"value":3331},{"type":30,"tag":439,"props":10346,"children":10347},{"style":992},[10348],{"type":36,"value":10349}," customerId",{"type":30,"tag":439,"props":10351,"children":10352},{"style":487},[10353],{"type":36,"value":1504},{"type":30,"tag":439,"props":10355,"children":10356},{"style":916},[10357],{"type":36,"value":10358}," CustomerId",{"type":30,"tag":439,"props":10360,"children":10361},{"style":922},[10362],{"type":36,"value":3348},{"type":30,"tag":439,"props":10364,"children":10365},{"class":441,"line":1807},[10366,10370,10374,10378],{"type":30,"tag":439,"props":10367,"children":10368},{"style":456},[10369],{"type":36,"value":3326},{"type":30,"tag":439,"props":10371,"children":10372},{"style":992},[10373],{"type":36,"value":4097},{"type":30,"tag":439,"props":10375,"children":10376},{"style":487},[10377],{"type":36,"value":1504},{"type":30,"tag":439,"props":10379,"children":10380},{"style":916},[10381],{"type":36,"value":10382}," OrderStatus\n",{"type":30,"tag":439,"props":10384,"children":10385},{"class":441,"line":1859},[10386,10390],{"type":30,"tag":439,"props":10387,"children":10388},{"style":922},[10389],{"type":36,"value":3381},{"type":30,"tag":439,"props":10391,"children":10392},{"style":922},[10393],{"type":36,"value":2564},{"type":30,"tag":439,"props":10395,"children":10396},{"class":441,"line":1904},[10397],{"type":30,"tag":439,"props":10398,"children":10399},{"emptyLinePlaceholder":13},[10400],{"type":36,"value":514},{"type":30,"tag":439,"props":10402,"children":10403},{"class":441,"line":1965},[10404,10409,10413,10417,10421],{"type":30,"tag":439,"props":10405,"children":10406},{"style":1105},[10407],{"type":36,"value":10408},"    confirm",{"type":30,"tag":439,"props":10410,"children":10411},{"style":922},[10412],{"type":36,"value":4003},{"type":30,"tag":439,"props":10414,"children":10415},{"style":487},[10416],{"type":36,"value":1504},{"type":30,"tag":439,"props":10418,"children":10419},{"style":1638},[10420],{"type":36,"value":4894},{"type":30,"tag":439,"props":10422,"children":10423},{"style":922},[10424],{"type":36,"value":3022},{"type":30,"tag":439,"props":10426,"children":10427},{"class":441,"line":1977},[10428,10432,10436,10441,10445,10450,10455,10460,10464,10469,10474],{"type":30,"tag":439,"props":10429,"children":10430},{"style":456},[10431],{"type":36,"value":3584},{"type":30,"tag":439,"props":10433,"children":10434},{"style":462},[10435],{"type":36,"value":3589},{"type":30,"tag":439,"props":10437,"children":10438},{"style":3474},[10439],{"type":36,"value":10440},"this",{"type":30,"tag":439,"props":10442,"children":10443},{"style":3480},[10444],{"type":36,"value":969},{"type":30,"tag":439,"props":10446,"children":10447},{"style":462},[10448],{"type":36,"value":10449},"status ",{"type":30,"tag":439,"props":10451,"children":10452},{"style":487},[10453],{"type":36,"value":10454},"!==",{"type":30,"tag":439,"props":10456,"children":10457},{"style":462},[10458],{"type":36,"value":10459}," OrderStatus",{"type":30,"tag":439,"props":10461,"children":10462},{"style":3480},[10463],{"type":36,"value":969},{"type":30,"tag":439,"props":10465,"children":10466},{"style":1022},[10467],{"type":36,"value":10468},"PENDING",{"type":30,"tag":439,"props":10470,"children":10471},{"style":462},[10472],{"type":36,"value":10473},") ",{"type":30,"tag":439,"props":10475,"children":10476},{"style":922},[10477],{"type":36,"value":3613},{"type":30,"tag":439,"props":10479,"children":10480},{"class":441,"line":1985},[10481,10485,10489,10494,10498,10502,10506,10511],{"type":30,"tag":439,"props":10482,"children":10483},{"style":456},[10484],{"type":36,"value":3621},{"type":30,"tag":439,"props":10486,"children":10487},{"style":3624},[10488],{"type":36,"value":3627},{"type":30,"tag":439,"props":10490,"children":10491},{"style":1105},[10492],{"type":36,"value":10493}," OrderAlreadyConfirmedException",{"type":30,"tag":439,"props":10495,"children":10496},{"style":462},[10497],{"type":36,"value":944},{"type":30,"tag":439,"props":10499,"children":10500},{"style":3474},[10501],{"type":36,"value":10440},{"type":30,"tag":439,"props":10503,"children":10504},{"style":3480},[10505],{"type":36,"value":969},{"type":30,"tag":439,"props":10507,"children":10508},{"style":462},[10509],{"type":36,"value":10510},"id)",{"type":30,"tag":439,"props":10512,"children":10513},{"style":922},[10514],{"type":36,"value":3082},{"type":30,"tag":439,"props":10516,"children":10517},{"class":441,"line":1994},[10518],{"type":30,"tag":439,"props":10519,"children":10520},{"style":922},[10521],{"type":36,"value":3658},{"type":30,"tag":439,"props":10523,"children":10524},{"class":441,"line":2019},[10525,10529,10533,10537,10541,10545,10549,10554],{"type":30,"tag":439,"props":10526,"children":10527},{"style":3474},[10528],{"type":36,"value":4819},{"type":30,"tag":439,"props":10530,"children":10531},{"style":3480},[10532],{"type":36,"value":969},{"type":30,"tag":439,"props":10534,"children":10535},{"style":462},[10536],{"type":36,"value":10449},{"type":30,"tag":439,"props":10538,"children":10539},{"style":487},[10540],{"type":36,"value":979},{"type":30,"tag":439,"props":10542,"children":10543},{"style":462},[10544],{"type":36,"value":10459},{"type":30,"tag":439,"props":10546,"children":10547},{"style":3480},[10548],{"type":36,"value":969},{"type":30,"tag":439,"props":10550,"children":10551},{"style":1022},[10552],{"type":36,"value":10553},"CONFIRMED",{"type":30,"tag":439,"props":10555,"children":10556},{"style":922},[10557],{"type":36,"value":3082},{"type":30,"tag":439,"props":10559,"children":10560},{"class":441,"line":2071},[10561],{"type":30,"tag":439,"props":10562,"children":10563},{"style":922},[10564],{"type":36,"value":3843},{"type":30,"tag":439,"props":10566,"children":10567},{"class":441,"line":2121},[10568],{"type":30,"tag":439,"props":10569,"children":10570},{"style":922},[10571],{"type":36,"value":3156},{"type":30,"tag":439,"props":10573,"children":10574},{"class":441,"line":2129},[10575],{"type":30,"tag":439,"props":10576,"children":10577},{"style":446},[10578],{"type":36,"value":10579},"// La persistance est gérée par une entité séparée dans la couche infrastructure\n",{"type":30,"tag":38,"props":10581,"children":10582},{},[10583,10588],{"type":30,"tag":44,"props":10584,"children":10585},{},[10586],{"type":36,"value":10587},"L'erreur courante :",{"type":36,"value":10589}," mettre les annotations de persistance directement sur les objets métier par pragmatisme. Ça fonctionne pour les petites applications. Sur une application qui grandit, ça crée un couplage fort entre la représentation métier et le schéma de base de données, deux choses qui évoluent à des rythmes différents.",{"type":30,"tag":225,"props":10591,"children":10593},{"cta":227,"href":228,"title":10592,"type":230},"Votre architecture produit de la complexité sans apporter les bénéfices attendus ?",[10594],{"type":30,"tag":38,"props":10595,"children":10596},{},[10597],{"type":36,"value":10598},"Un audit architectural de votre codebase identifie les couplages problématiques, les opportunités de simplification, et la roadmap pour une architecture qui supporte votre croissance sans vous ralentir. En 30 minutes, on peut établir un premier diagnostic et définir les priorités.",{"type":30,"tag":67,"props":10600,"children":10601},{},[],{"type":30,"tag":71,"props":10603,"children":10605},{"id":10604},"règle-3-les-use-cases-orchestrent-sans-dépendre-du-delivery-mechanism",[10606],{"type":36,"value":10607},"Règle 3 : Les use cases orchestrent sans dépendre du delivery mechanism",{"type":30,"tag":38,"props":10609,"children":10610},{},[10611],{"type":36,"value":10612},"Un use case implémente un cas d'utilisation métier. Il orchestre des entités, appelle des repositories, et retourne un résultat.",{"type":30,"tag":38,"props":10614,"children":10615},{},[10616,10621],{"type":30,"tag":44,"props":10617,"children":10618},{},[10619],{"type":36,"value":10620},"Ce qu'il ne doit pas faire :",{"type":36,"value":10622}," connaître comment son résultat va être utilisé : HTTP response, message queue, CLI output, test assertion. Le use case retourne un objet métier. La couche externe décide de comment le sérialiser.",{"type":30,"tag":38,"props":10624,"children":10625},{},[10626],{"type":30,"tag":44,"props":10627,"children":10628},{},[10629],{"type":36,"value":9487},{"type":30,"tag":620,"props":10631,"children":10632},{},[10633,10646,10658],{"type":30,"tag":624,"props":10634,"children":10635},{},[10636,10638,10644],{"type":36,"value":10637},"Un use case ne retourne pas de ",{"type":30,"tag":98,"props":10639,"children":10641},{"className":10640},[],[10642],{"type":36,"value":10643},"ResponseEntity\u003C>",{"type":36,"value":10645}," ou d'objet HTTP",{"type":30,"tag":624,"props":10647,"children":10648},{},[10649,10651,10657],{"type":36,"value":10650},"Un use case ne lève pas d'exceptions HTTP (",{"type":30,"tag":98,"props":10652,"children":10654},{"className":10653},[],[10655],{"type":36,"value":10656},"HttpStatus.NOT_FOUND",{"type":36,"value":1030},{"type":30,"tag":624,"props":10659,"children":10660},{},[10661],{"type":36,"value":10662},"Un use case peut être appelé depuis un controller HTTP, un consumer de message queue, ou un test sans modification",{"type":30,"tag":185,"props":10664,"children":10666},{"className":886,"code":10665,"language":885,"meta":8,"style":8},"# Use case indépendant du delivery mechanism\nclass CreateUserUseCase:\n    def __init__(self, user_repository: UserRepository, email_service: EmailService):\n        self.user_repository = user_repository\n        self.email_service = email_service\n\n    def execute(self, command: CreateUserCommand) -> User:\n        if self.user_repository.exists_by_email(command.email):\n            raise EmailAlreadyExistsError(command.email)  # exception métier\n\n        user = User.create(command.email, command.name)\n        self.user_repository.save(user)\n        self.email_service.send_welcome(user)\n        return user  # retourne un objet métier, pas un objet HTTP\n\n# Le controller HTTP gère la sérialisation\nclass UserController:\n    def create_user(self, request: CreateUserRequest) -> Response:\n        try:\n            command = CreateUserCommand(request.email, request.name)\n            user = self.create_user_use_case.execute(command)\n            return Response(UserDTO.from_domain(user), status=201)\n        except EmailAlreadyExistsError:\n            return Response({\"error\": \"Email already exists\"}, status=409)\n",[10667],{"type":30,"tag":98,"props":10668,"children":10669},{"__ignoreMap":8},[10670,10678,10694,10753,10778,10803,10810,10864,10914,10952,10959,11021,11057,11094,11111,11118,11126,11142,11197,11208,11260,11305,11364,11379],{"type":30,"tag":439,"props":10671,"children":10672},{"class":441,"line":442},[10673],{"type":30,"tag":439,"props":10674,"children":10675},{"style":446},[10676],{"type":36,"value":10677},"# Use case indépendant du delivery mechanism\n",{"type":30,"tag":439,"props":10679,"children":10680},{"class":441,"line":452},[10681,10685,10690],{"type":30,"tag":439,"props":10682,"children":10683},{"style":456},[10684],{"type":36,"value":913},{"type":30,"tag":439,"props":10686,"children":10687},{"style":916},[10688],{"type":36,"value":10689}," CreateUserUseCase",{"type":30,"tag":439,"props":10691,"children":10692},{"style":922},[10693],{"type":36,"value":925},{"type":30,"tag":439,"props":10695,"children":10696},{"class":441,"line":478},[10697,10701,10705,10709,10713,10717,10722,10726,10731,10735,10740,10744,10749],{"type":30,"tag":439,"props":10698,"children":10699},{"style":456},[10700],{"type":36,"value":933},{"type":30,"tag":439,"props":10702,"children":10703},{"style":936},[10704],{"type":36,"value":939},{"type":30,"tag":439,"props":10706,"children":10707},{"style":922},[10708],{"type":36,"value":944},{"type":30,"tag":439,"props":10710,"children":10711},{"style":947},[10712],{"type":36,"value":950},{"type":30,"tag":439,"props":10714,"children":10715},{"style":922},[10716],{"type":36,"value":1010},{"type":30,"tag":439,"props":10718,"children":10719},{"style":1123},[10720],{"type":36,"value":10721}," user_repository",{"type":30,"tag":439,"props":10723,"children":10724},{"style":922},[10725],{"type":36,"value":1504},{"type":30,"tag":439,"props":10727,"children":10728},{"style":1721},[10729],{"type":36,"value":10730}," UserRepository",{"type":30,"tag":439,"props":10732,"children":10733},{"style":922},[10734],{"type":36,"value":1010},{"type":30,"tag":439,"props":10736,"children":10737},{"style":1123},[10738],{"type":36,"value":10739}," email_service",{"type":30,"tag":439,"props":10741,"children":10742},{"style":922},[10743],{"type":36,"value":1504},{"type":30,"tag":439,"props":10745,"children":10746},{"style":1721},[10747],{"type":36,"value":10748}," EmailService",{"type":30,"tag":439,"props":10750,"children":10751},{"style":922},[10752],{"type":36,"value":955},{"type":30,"tag":439,"props":10754,"children":10755},{"class":441,"line":508},[10756,10760,10764,10769,10773],{"type":30,"tag":439,"props":10757,"children":10758},{"style":961},[10759],{"type":36,"value":964},{"type":30,"tag":439,"props":10761,"children":10762},{"style":922},[10763],{"type":36,"value":969},{"type":30,"tag":439,"props":10765,"children":10766},{"style":462},[10767],{"type":36,"value":10768},"user_repository ",{"type":30,"tag":439,"props":10770,"children":10771},{"style":487},[10772],{"type":36,"value":979},{"type":30,"tag":439,"props":10774,"children":10775},{"style":462},[10776],{"type":36,"value":10777}," user_repository\n",{"type":30,"tag":439,"props":10779,"children":10780},{"class":441,"line":517},[10781,10785,10789,10794,10798],{"type":30,"tag":439,"props":10782,"children":10783},{"style":961},[10784],{"type":36,"value":964},{"type":30,"tag":439,"props":10786,"children":10787},{"style":922},[10788],{"type":36,"value":969},{"type":30,"tag":439,"props":10790,"children":10791},{"style":462},[10792],{"type":36,"value":10793},"email_service ",{"type":30,"tag":439,"props":10795,"children":10796},{"style":487},[10797],{"type":36,"value":979},{"type":30,"tag":439,"props":10799,"children":10800},{"style":462},[10801],{"type":36,"value":10802}," email_service\n",{"type":30,"tag":439,"props":10804,"children":10805},{"class":441,"line":526},[10806],{"type":30,"tag":439,"props":10807,"children":10808},{"emptyLinePlaceholder":13},[10809],{"type":36,"value":514},{"type":30,"tag":439,"props":10811,"children":10812},{"class":441,"line":547},[10813,10817,10821,10825,10829,10833,10838,10842,10847,10851,10855,10860],{"type":30,"tag":439,"props":10814,"children":10815},{"style":456},[10816],{"type":36,"value":933},{"type":30,"tag":439,"props":10818,"children":10819},{"style":1105},[10820],{"type":36,"value":9832},{"type":30,"tag":439,"props":10822,"children":10823},{"style":922},[10824],{"type":36,"value":944},{"type":30,"tag":439,"props":10826,"children":10827},{"style":947},[10828],{"type":36,"value":950},{"type":30,"tag":439,"props":10830,"children":10831},{"style":922},[10832],{"type":36,"value":1010},{"type":30,"tag":439,"props":10834,"children":10835},{"style":1123},[10836],{"type":36,"value":10837}," command",{"type":30,"tag":439,"props":10839,"children":10840},{"style":922},[10841],{"type":36,"value":1504},{"type":30,"tag":439,"props":10843,"children":10844},{"style":1721},[10845],{"type":36,"value":10846}," CreateUserCommand",{"type":30,"tag":439,"props":10848,"children":10849},{"style":922},[10850],{"type":36,"value":1030},{"type":30,"tag":439,"props":10852,"children":10853},{"style":922},[10854],{"type":36,"value":1519},{"type":30,"tag":439,"props":10856,"children":10857},{"style":462},[10858],{"type":36,"value":10859}," User",{"type":30,"tag":439,"props":10861,"children":10862},{"style":922},[10863],{"type":36,"value":925},{"type":30,"tag":439,"props":10865,"children":10866},{"class":441,"line":1097},[10867,10871,10875,10879,10884,10888,10893,10897,10902,10906,10910],{"type":30,"tag":439,"props":10868,"children":10869},{"style":456},[10870],{"type":36,"value":3584},{"type":30,"tag":439,"props":10872,"children":10873},{"style":961},[10874],{"type":36,"value":1148},{"type":30,"tag":439,"props":10876,"children":10877},{"style":922},[10878],{"type":36,"value":969},{"type":30,"tag":439,"props":10880,"children":10881},{"style":462},[10882],{"type":36,"value":10883},"user_repository",{"type":30,"tag":439,"props":10885,"children":10886},{"style":922},[10887],{"type":36,"value":969},{"type":30,"tag":439,"props":10889,"children":10890},{"style":982},[10891],{"type":36,"value":10892},"exists_by_email",{"type":30,"tag":439,"props":10894,"children":10895},{"style":922},[10896],{"type":36,"value":944},{"type":30,"tag":439,"props":10898,"children":10899},{"style":462},[10900],{"type":36,"value":10901},"command",{"type":30,"tag":439,"props":10903,"children":10904},{"style":922},[10905],{"type":36,"value":969},{"type":30,"tag":439,"props":10907,"children":10908},{"style":462},[10909],{"type":36,"value":1197},{"type":30,"tag":439,"props":10911,"children":10912},{"style":922},[10913],{"type":36,"value":955},{"type":30,"tag":439,"props":10915,"children":10916},{"class":441,"line":1133},[10917,10922,10927,10931,10935,10939,10943,10947],{"type":30,"tag":439,"props":10918,"children":10919},{"style":456},[10920],{"type":36,"value":10921},"            raise",{"type":30,"tag":439,"props":10923,"children":10924},{"style":982},[10925],{"type":36,"value":10926}," EmailAlreadyExistsError",{"type":30,"tag":439,"props":10928,"children":10929},{"style":922},[10930],{"type":36,"value":944},{"type":30,"tag":439,"props":10932,"children":10933},{"style":462},[10934],{"type":36,"value":10901},{"type":30,"tag":439,"props":10936,"children":10937},{"style":922},[10938],{"type":36,"value":969},{"type":30,"tag":439,"props":10940,"children":10941},{"style":462},[10942],{"type":36,"value":1197},{"type":30,"tag":439,"props":10944,"children":10945},{"style":922},[10946],{"type":36,"value":1030},{"type":30,"tag":439,"props":10948,"children":10949},{"style":446},[10950],{"type":36,"value":10951},"  # exception métier\n",{"type":30,"tag":439,"props":10953,"children":10954},{"class":441,"line":1183},[10955],{"type":30,"tag":439,"props":10956,"children":10957},{"emptyLinePlaceholder":13},[10958],{"type":36,"value":514},{"type":30,"tag":439,"props":10960,"children":10961},{"class":441,"line":1250},[10962,10967,10971,10975,10979,10984,10988,10992,10996,11000,11004,11008,11012,11017],{"type":30,"tag":439,"props":10963,"children":10964},{"style":462},[10965],{"type":36,"value":10966},"        user ",{"type":30,"tag":439,"props":10968,"children":10969},{"style":487},[10970],{"type":36,"value":979},{"type":30,"tag":439,"props":10972,"children":10973},{"style":462},[10974],{"type":36,"value":10859},{"type":30,"tag":439,"props":10976,"children":10977},{"style":922},[10978],{"type":36,"value":969},{"type":30,"tag":439,"props":10980,"children":10981},{"style":982},[10982],{"type":36,"value":10983},"create",{"type":30,"tag":439,"props":10985,"children":10986},{"style":922},[10987],{"type":36,"value":944},{"type":30,"tag":439,"props":10989,"children":10990},{"style":462},[10991],{"type":36,"value":10901},{"type":30,"tag":439,"props":10993,"children":10994},{"style":922},[10995],{"type":36,"value":969},{"type":30,"tag":439,"props":10997,"children":10998},{"style":462},[10999],{"type":36,"value":1197},{"type":30,"tag":439,"props":11001,"children":11002},{"style":922},[11003],{"type":36,"value":1010},{"type":30,"tag":439,"props":11005,"children":11006},{"style":462},[11007],{"type":36,"value":10837},{"type":30,"tag":439,"props":11009,"children":11010},{"style":922},[11011],{"type":36,"value":969},{"type":30,"tag":439,"props":11013,"children":11014},{"style":462},[11015],{"type":36,"value":11016},"name",{"type":30,"tag":439,"props":11018,"children":11019},{"style":922},[11020],{"type":36,"value":1180},{"type":30,"tag":439,"props":11022,"children":11023},{"class":441,"line":1655},[11024,11028,11032,11036,11040,11044,11048,11053],{"type":30,"tag":439,"props":11025,"children":11026},{"style":961},[11027],{"type":36,"value":964},{"type":30,"tag":439,"props":11029,"children":11030},{"style":922},[11031],{"type":36,"value":969},{"type":30,"tag":439,"props":11033,"children":11034},{"style":462},[11035],{"type":36,"value":10883},{"type":30,"tag":439,"props":11037,"children":11038},{"style":922},[11039],{"type":36,"value":969},{"type":30,"tag":439,"props":11041,"children":11042},{"style":982},[11043],{"type":36,"value":1166},{"type":30,"tag":439,"props":11045,"children":11046},{"style":922},[11047],{"type":36,"value":944},{"type":30,"tag":439,"props":11049,"children":11050},{"style":462},[11051],{"type":36,"value":11052},"user",{"type":30,"tag":439,"props":11054,"children":11055},{"style":922},[11056],{"type":36,"value":1180},{"type":30,"tag":439,"props":11058,"children":11059},{"class":441,"line":1663},[11060,11064,11068,11073,11077,11082,11086,11090],{"type":30,"tag":439,"props":11061,"children":11062},{"style":961},[11063],{"type":36,"value":964},{"type":30,"tag":439,"props":11065,"children":11066},{"style":922},[11067],{"type":36,"value":969},{"type":30,"tag":439,"props":11069,"children":11070},{"style":462},[11071],{"type":36,"value":11072},"email_service",{"type":30,"tag":439,"props":11074,"children":11075},{"style":922},[11076],{"type":36,"value":969},{"type":30,"tag":439,"props":11078,"children":11079},{"style":982},[11080],{"type":36,"value":11081},"send_welcome",{"type":30,"tag":439,"props":11083,"children":11084},{"style":922},[11085],{"type":36,"value":944},{"type":30,"tag":439,"props":11087,"children":11088},{"style":462},[11089],{"type":36,"value":11052},{"type":30,"tag":439,"props":11091,"children":11092},{"style":922},[11093],{"type":36,"value":1180},{"type":30,"tag":439,"props":11095,"children":11096},{"class":441,"line":1672},[11097,11101,11106],{"type":30,"tag":439,"props":11098,"children":11099},{"style":456},[11100],{"type":36,"value":1256},{"type":30,"tag":439,"props":11102,"children":11103},{"style":462},[11104],{"type":36,"value":11105}," user  ",{"type":30,"tag":439,"props":11107,"children":11108},{"style":446},[11109],{"type":36,"value":11110},"# retourne un objet métier, pas un objet HTTP\n",{"type":30,"tag":439,"props":11112,"children":11113},{"class":441,"line":1688},[11114],{"type":30,"tag":439,"props":11115,"children":11116},{"emptyLinePlaceholder":13},[11117],{"type":36,"value":514},{"type":30,"tag":439,"props":11119,"children":11120},{"class":441,"line":1747},[11121],{"type":30,"tag":439,"props":11122,"children":11123},{"style":446},[11124],{"type":36,"value":11125},"# Le controller HTTP gère la sérialisation\n",{"type":30,"tag":439,"props":11127,"children":11128},{"class":441,"line":1773},[11129,11133,11138],{"type":30,"tag":439,"props":11130,"children":11131},{"style":456},[11132],{"type":36,"value":913},{"type":30,"tag":439,"props":11134,"children":11135},{"style":916},[11136],{"type":36,"value":11137}," UserController",{"type":30,"tag":439,"props":11139,"children":11140},{"style":922},[11141],{"type":36,"value":925},{"type":30,"tag":439,"props":11143,"children":11144},{"class":441,"line":1799},[11145,11149,11154,11158,11162,11166,11171,11175,11180,11184,11188,11193],{"type":30,"tag":439,"props":11146,"children":11147},{"style":456},[11148],{"type":36,"value":933},{"type":30,"tag":439,"props":11150,"children":11151},{"style":1105},[11152],{"type":36,"value":11153}," create_user",{"type":30,"tag":439,"props":11155,"children":11156},{"style":922},[11157],{"type":36,"value":944},{"type":30,"tag":439,"props":11159,"children":11160},{"style":947},[11161],{"type":36,"value":950},{"type":30,"tag":439,"props":11163,"children":11164},{"style":922},[11165],{"type":36,"value":1010},{"type":30,"tag":439,"props":11167,"children":11168},{"style":1123},[11169],{"type":36,"value":11170}," request",{"type":30,"tag":439,"props":11172,"children":11173},{"style":922},[11174],{"type":36,"value":1504},{"type":30,"tag":439,"props":11176,"children":11177},{"style":1721},[11178],{"type":36,"value":11179}," CreateUserRequest",{"type":30,"tag":439,"props":11181,"children":11182},{"style":922},[11183],{"type":36,"value":1030},{"type":30,"tag":439,"props":11185,"children":11186},{"style":922},[11187],{"type":36,"value":1519},{"type":30,"tag":439,"props":11189,"children":11190},{"style":462},[11191],{"type":36,"value":11192}," Response",{"type":30,"tag":439,"props":11194,"children":11195},{"style":922},[11196],{"type":36,"value":925},{"type":30,"tag":439,"props":11198,"children":11199},{"class":441,"line":1807},[11200,11204],{"type":30,"tag":439,"props":11201,"children":11202},{"style":456},[11203],{"type":36,"value":6170},{"type":30,"tag":439,"props":11205,"children":11206},{"style":922},[11207],{"type":36,"value":925},{"type":30,"tag":439,"props":11209,"children":11210},{"class":441,"line":1859},[11211,11216,11220,11224,11228,11232,11236,11240,11244,11248,11252,11256],{"type":30,"tag":439,"props":11212,"children":11213},{"style":462},[11214],{"type":36,"value":11215},"            command ",{"type":30,"tag":439,"props":11217,"children":11218},{"style":487},[11219],{"type":36,"value":979},{"type":30,"tag":439,"props":11221,"children":11222},{"style":982},[11223],{"type":36,"value":10846},{"type":30,"tag":439,"props":11225,"children":11226},{"style":922},[11227],{"type":36,"value":944},{"type":30,"tag":439,"props":11229,"children":11230},{"style":462},[11231],{"type":36,"value":6784},{"type":30,"tag":439,"props":11233,"children":11234},{"style":922},[11235],{"type":36,"value":969},{"type":30,"tag":439,"props":11237,"children":11238},{"style":462},[11239],{"type":36,"value":1197},{"type":30,"tag":439,"props":11241,"children":11242},{"style":922},[11243],{"type":36,"value":1010},{"type":30,"tag":439,"props":11245,"children":11246},{"style":462},[11247],{"type":36,"value":11170},{"type":30,"tag":439,"props":11249,"children":11250},{"style":922},[11251],{"type":36,"value":969},{"type":30,"tag":439,"props":11253,"children":11254},{"style":462},[11255],{"type":36,"value":11016},{"type":30,"tag":439,"props":11257,"children":11258},{"style":922},[11259],{"type":36,"value":1180},{"type":30,"tag":439,"props":11261,"children":11262},{"class":441,"line":1904},[11263,11268,11272,11276,11280,11285,11289,11293,11297,11301],{"type":30,"tag":439,"props":11264,"children":11265},{"style":462},[11266],{"type":36,"value":11267},"            user ",{"type":30,"tag":439,"props":11269,"children":11270},{"style":487},[11271],{"type":36,"value":979},{"type":30,"tag":439,"props":11273,"children":11274},{"style":961},[11275],{"type":36,"value":1148},{"type":30,"tag":439,"props":11277,"children":11278},{"style":922},[11279],{"type":36,"value":969},{"type":30,"tag":439,"props":11281,"children":11282},{"style":462},[11283],{"type":36,"value":11284},"create_user_use_case",{"type":30,"tag":439,"props":11286,"children":11287},{"style":922},[11288],{"type":36,"value":969},{"type":30,"tag":439,"props":11290,"children":11291},{"style":982},[11292],{"type":36,"value":2097},{"type":30,"tag":439,"props":11294,"children":11295},{"style":922},[11296],{"type":36,"value":944},{"type":30,"tag":439,"props":11298,"children":11299},{"style":462},[11300],{"type":36,"value":10901},{"type":30,"tag":439,"props":11302,"children":11303},{"style":922},[11304],{"type":36,"value":1180},{"type":30,"tag":439,"props":11306,"children":11307},{"class":441,"line":1965},[11308,11312,11316,11320,11325,11329,11334,11338,11342,11347,11351,11355,11360],{"type":30,"tag":439,"props":11309,"children":11310},{"style":456},[11311],{"type":36,"value":6182},{"type":30,"tag":439,"props":11313,"children":11314},{"style":982},[11315],{"type":36,"value":11192},{"type":30,"tag":439,"props":11317,"children":11318},{"style":922},[11319],{"type":36,"value":944},{"type":30,"tag":439,"props":11321,"children":11322},{"style":462},[11323],{"type":36,"value":11324},"UserDTO",{"type":30,"tag":439,"props":11326,"children":11327},{"style":922},[11328],{"type":36,"value":969},{"type":30,"tag":439,"props":11330,"children":11331},{"style":982},[11332],{"type":36,"value":11333},"from_domain",{"type":30,"tag":439,"props":11335,"children":11336},{"style":922},[11337],{"type":36,"value":944},{"type":30,"tag":439,"props":11339,"children":11340},{"style":462},[11341],{"type":36,"value":11052},{"type":30,"tag":439,"props":11343,"children":11344},{"style":922},[11345],{"type":36,"value":11346},"),",{"type":30,"tag":439,"props":11348,"children":11349},{"style":992},[11350],{"type":36,"value":4097},{"type":30,"tag":439,"props":11352,"children":11353},{"style":487},[11354],{"type":36,"value":979},{"type":30,"tag":439,"props":11356,"children":11357},{"style":1022},[11358],{"type":36,"value":11359},"201",{"type":30,"tag":439,"props":11361,"children":11362},{"style":922},[11363],{"type":36,"value":1180},{"type":30,"tag":439,"props":11365,"children":11366},{"class":441,"line":1977},[11367,11371,11375],{"type":30,"tag":439,"props":11368,"children":11369},{"style":456},[11370],{"type":36,"value":6199},{"type":30,"tag":439,"props":11372,"children":11373},{"style":462},[11374],{"type":36,"value":10926},{"type":30,"tag":439,"props":11376,"children":11377},{"style":922},[11378],{"type":36,"value":925},{"type":30,"tag":439,"props":11380,"children":11381},{"class":441,"line":1985},[11382,11386,11390,11394,11399,11403,11408,11413,11417,11421,11426],{"type":30,"tag":439,"props":11383,"children":11384},{"style":456},[11385],{"type":36,"value":6182},{"type":30,"tag":439,"props":11387,"children":11388},{"style":982},[11389],{"type":36,"value":11192},{"type":30,"tag":439,"props":11391,"children":11392},{"style":922},[11393],{"type":36,"value":2847},{"type":30,"tag":439,"props":11395,"children":11396},{"style":1002},[11397],{"type":36,"value":11398},"\"error\"",{"type":30,"tag":439,"props":11400,"children":11401},{"style":922},[11402],{"type":36,"value":1504},{"type":30,"tag":439,"props":11404,"children":11405},{"style":1002},[11406],{"type":36,"value":11407}," \"Email already exists\"",{"type":30,"tag":439,"props":11409,"children":11410},{"style":922},[11411],{"type":36,"value":11412},"},",{"type":30,"tag":439,"props":11414,"children":11415},{"style":992},[11416],{"type":36,"value":4097},{"type":30,"tag":439,"props":11418,"children":11419},{"style":487},[11420],{"type":36,"value":979},{"type":30,"tag":439,"props":11422,"children":11423},{"style":1022},[11424],{"type":36,"value":11425},"409",{"type":30,"tag":439,"props":11427,"children":11428},{"style":922},[11429],{"type":36,"value":1180},{"type":30,"tag":67,"props":11431,"children":11432},{},[],{"type":30,"tag":71,"props":11434,"children":11436},{"id":11435},"les-anti-patterns-courants-à-éviter",[11437],{"type":36,"value":11438},"Les anti-patterns courants à éviter",{"type":30,"tag":38,"props":11440,"children":11441},{},[11442,11447],{"type":30,"tag":44,"props":11443,"children":11444},{},[11445],{"type":36,"value":11446},"L'Anemic Domain Model :",{"type":36,"value":11448}," les entités ne contiennent que des getters/setters, et toute la logique est dans les services. Le domaine est vide de sens. La logique métier se retrouve dispersée dans tous les services et est difficile à tester isolément.",{"type":30,"tag":38,"props":11450,"children":11451},{},[11452,11457],{"type":30,"tag":44,"props":11453,"children":11454},{},[11455],{"type":36,"value":11456},"Le God Service :",{"type":36,"value":11458}," un service qui fait tout : valide, orchestre, persiste, formate. Difficile à tester, impossible à réutiliser, cauchemar à maintenir.",{"type":30,"tag":38,"props":11460,"children":11461},{},[11462,11467],{"type":30,"tag":44,"props":11463,"children":11464},{},[11465],{"type":36,"value":11466},"Le leak d'infrastructure :",{"type":36,"value":11468}," des dépendances sur des frameworks qui se propagent jusqu'aux entités métier. Le signe infaillible que la règle 2 est violée.",{"type":30,"tag":67,"props":11470,"children":11471},{},[],{"type":30,"tag":71,"props":11473,"children":11475},{"id":11474},"quand-appliquer-les-3-règles-quand-ne-pas-les-appliquer",[11476],{"type":36,"value":11477},"Quand appliquer les 3 règles, quand ne pas les appliquer",{"type":30,"tag":38,"props":11479,"children":11480},{},[11481,11486],{"type":30,"tag":44,"props":11482,"children":11483},{},[11484],{"type":36,"value":11485},"Appliquer dès que :",{"type":36,"value":11487}," la logique métier est non-triviale, l'application doit durer plus de 2 ans, ou plusieurs développeurs travaillent sur le même codebase.",{"type":30,"tag":38,"props":11489,"children":11490},{},[11491,11496],{"type":30,"tag":44,"props":11492,"children":11493},{},[11494],{"type":36,"value":11495},"Ne pas appliquer dans sa totalité :",{"type":36,"value":11497}," pour des scripts, des prototypes, des outils internes à courte durée de vie. La Clean Architecture a un coût d'entrée qui doit être justifié par la longévité et la complexité du projet.",{"type":30,"tag":67,"props":11499,"children":11500},{},[],{"type":30,"tag":71,"props":11502,"children":11504},{"id":11503},"faq-sur-la-clean-architecture",[11505],{"type":36,"value":11506},"FAQ sur la Clean Architecture",{"type":30,"tag":714,"props":11508,"children":11509},{},[11510,11515],{"type":30,"tag":718,"props":11511,"children":11512},{},[11513],{"type":36,"value":11514},"1. La Clean Architecture est-elle compatible avec les frameworks comme Spring ou NestJS ?",{"type":30,"tag":38,"props":11516,"children":11517},{},[11518],{"type":36,"value":11519},"Oui, mais avec discipline. Spring et NestJS sont excellents comme couches d'infrastructure (injection de dépendances, routing HTTP). La règle est de ne pas laisser leurs annotations et abstractions contaminer les couches métier. Utiliser la DI du framework pour injecter les dépendances dans les use cases, sans que les use cases aient connaissance du framework lui-même.",{"type":30,"tag":714,"props":11521,"children":11522},{},[11523,11528],{"type":30,"tag":718,"props":11524,"children":11525},{},[11526],{"type":36,"value":11527},"2. Faut-il refactoriser tout un codebase existant pour appliquer ces 3 règles ?",{"type":30,"tag":38,"props":11529,"children":11530},{},[11531],{"type":36,"value":11532},"Non. Appliquer les 3 règles progressivement, au fil des nouvelles features et des refactorings planifiés. Commencer par les modules les plus actifs, ceux sur lesquels l'équipe travaille le plus souvent. Sur les modules legacy stables, le coût du refactoring n'est pas justifié si le gain opérationnel est faible. C'est la stratégie que j'applique systématiquement dans mes missions.",{"type":30,"tag":714,"props":11534,"children":11535},{},[11536,11541],{"type":30,"tag":718,"props":11537,"children":11538},{},[11539],{"type":36,"value":11540},"3. La Clean Architecture ralentit-elle le développement au départ ?",{"type":30,"tag":38,"props":11542,"children":11543},{},[11544],{"type":36,"value":11545},"Légèrement, sur les 2 à 4 premières semaines. Le coût est l'apprentissage de la structuration correcte du code. Après cette période, le développement accélère parce que les tests sont rapides, les changements sont localisés, et les nouveaux développeurs comprennent la structure rapidement. Sur l'équipe dont je parlais en ouverture, le ralentissement initial a été compensé dès le premier mois.",{"type":30,"tag":714,"props":11547,"children":11548},{},[11549,11554],{"type":30,"tag":718,"props":11550,"children":11551},{},[11552],{"type":36,"value":11553},"4. Clean Architecture, Hexagonal Architecture, Onion Architecture : quelle différence ?",{"type":30,"tag":38,"props":11555,"children":11556},{},[11557],{"type":36,"value":11558},"Ce sont des variantes du même principe : séparer le domaine de l'infrastructure et diriger les dépendances vers le centre. Les différences sont principalement de vocabulaire et d'emphase. L'Hexagonal (ports et adapters) est souvent plus simple à expliquer. La Clean Architecture est plus prescriptive sur les couches. Choisissez le vocabulaire qui résonne avec votre équipe. Le principe est le même.",{"type":30,"tag":714,"props":11560,"children":11561},{},[11562,11567],{"type":30,"tag":718,"props":11563,"children":11564},{},[11565],{"type":36,"value":11566},"5. Comment justifier le temps d'investissement en Clean Architecture auprès du management ?",{"type":30,"tag":38,"props":11568,"children":11569},{},[11570,11572,11578],{"type":36,"value":11571},"Trois arguments concrets que j'utilise : (1) Les tests métier s'exécutent en secondes et non en minutes, réduisant le feedback loop et le temps de CI. (2) Les nouveaux développeurs atteignent l'autonomie 2 à 4 semaines plus tôt, ce qui représente une économie de 40 à 80 heures d'accompagnement par recrutement. (3) Les changements de framework ou de base de données, qui arrivent tous les 3 à 5 ans, ne nécessitent pas de réécriture de la logique métier. Pour un CTO qui prend ses fonctions, investir dans ces fondations architecturales fait partie des ",{"type":30,"tag":142,"props":11573,"children":11575},{"href":11574},"/fr/management/cto-premiere-annee-90-jours",[11576],{"type":36,"value":11577},"décisions structurantes à prendre dans les 90 premiers jours",{"type":36,"value":11579},", avant que la dette s'accumule et rende le changement trop coûteux.",{"type":30,"tag":67,"props":11581,"children":11582},{},[],{"type":30,"tag":225,"props":11584,"children":11585},{"cta":785,"href":786,"title":787,"type":788},[11586],{"type":30,"tag":38,"props":11587,"children":11588},{},[11589],{"type":36,"value":11590},"L'Engineering Maturity Self-Assessment couvre le domaine Architecture & Craft : évaluez votre niveau sur la séparation des couches, le couplage, et la testabilité de votre codebase. Score et recommandations en 10 minutes.",{"type":30,"tag":796,"props":11592,"children":11593},{},[11594],{"type":36,"value":800},{"title":8,"searchDepth":452,"depth":452,"links":11596},[11597,11598,11599,11600,11601,11602,11603],{"id":9417,"depth":452,"text":9420},{"id":9452,"depth":452,"text":9455},{"id":9980,"depth":452,"text":9983},{"id":10604,"depth":452,"text":10607},{"id":11435,"depth":452,"text":11438},{"id":11474,"depth":452,"text":11477},{"id":11503,"depth":452,"text":11506},"content:fr:architecture-craft:clean-architecture-3-regles.md","fr/architecture-craft/clean-architecture-3-regles.md","fr/architecture-craft/clean-architecture-3-regles",{"_path":144,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":11608,"description":11609,"id":517,"date":11610,"listed":13,"nocomments":7,"hidden":7,"categories":11611,"tags":11612,"--cover":11615,"readingTime":11616,"body":11620,"_type":810,"_id":12385,"_source":812,"_file":12386,"_stem":12387,"_extension":815},"Architecture décisionnelle : pourquoi les ADRs changent tout","Chaque équipe refait les mêmes débats architecturaux parce que personne ne documente les décisions passées. Les ADRs résolvent ça en 30 minutes de mise en place.","2026-01-14",[6],[11613,826,11614],"ADR","Documentation Technique","covers/articles/adr-architecture-decision.jpg",{"text":22,"minutes":11617,"time":11618,"words":11619},7.04,422400,1408,{"type":27,"children":11621,"toc":12376},[11622,11627,11632,11637,11642,11647,11650,11656,11668,11673,11678,11696,11701,11714,11717,11723,11728,11738,11748,11788,11798,11808,12022,12031,12034,12040,12045,12050,12068,12081,12084,12090,12095,12103,12108,12118,12128,12138,12151,12154,12160,12168,12173,12178,12181,12187,12260,12263,12269,12282,12302,12328,12341,12361,12364,12372],{"type":30,"tag":31,"props":11623,"children":11625},{"id":11624},"architecture-décisionnelle-pourquoi-les-adrs-changent-tout",[11626],{"type":36,"value":11608},{"type":30,"tag":38,"props":11628,"children":11629},{},[11630],{"type":36,"value":11631},"J'ai rejoint une équipe bancaire en mission où le même débat revenait toutes les deux semaines : \"Pourquoi on a choisi PostgreSQL plutôt que MongoDB ?\" La décision avait deux ans. Personne ne s'en souvenait. Et chaque fois, on reconstruisait le contexte de zéro, parfois en prenant une direction différente de la précédente.",{"type":30,"tag":38,"props":11633,"children":11634},{},[11635],{"type":36,"value":11636},"Ce n'était pas un manque d'intelligence. C'était un manque de mémoire organisationnelle.",{"type":30,"tag":38,"props":11638,"children":11639},{},[11640],{"type":36,"value":11641},"Les Architecture Decision Records (ADRs) sont la réponse à ce problème. Un document court (une à deux pages) qui répond à une seule question : \"Pourquoi avons-nous fait ce choix technique ?\" Le concept existe depuis les années 2000. La plupart des équipes ne l'utilisent toujours pas.",{"type":30,"tag":38,"props":11643,"children":11644},{},[11645],{"type":36,"value":11646},"Et ça leur coûte cher.",{"type":30,"tag":67,"props":11648,"children":11649},{},[],{"type":30,"tag":71,"props":11651,"children":11653},{"id":11652},"le-coût-réel-des-décisions-non-documentées",[11654],{"type":36,"value":11655},"Le coût réel des décisions non documentées",{"type":30,"tag":38,"props":11657,"children":11658},{},[11659,11661,11666],{"type":36,"value":11660},"Dans une équipe de 10 développeurs, si chaque développeur perd 2 heures par mois à reconstruire le contexte de décisions passées, ça représente ",{"type":30,"tag":44,"props":11662,"children":11663},{},[11664],{"type":36,"value":11665},"240 heures par an",{"type":36,"value":11667},", soit plus de 6 semaines-développeur perdues sur des débats déjà tranchés.",{"type":30,"tag":38,"props":11669,"children":11670},{},[11671],{"type":36,"value":11672},"Quand j'ai mené cet exercice chez un client dans le secteur assurance (60 développeurs, 8 équipes), les résultats étaient éloquents : 73% des développeurs n'avaient pas conscience des 3 dernières décisions architecturales majeures prises par leur propre équipe. Le coût en onboardings ratés, en refactorings inutiles, en duplications de solutions était supérieur à 400 heures par trimestre.",{"type":30,"tag":38,"props":11674,"children":11675},{},[11676],{"type":36,"value":11677},"J'ai demandé à chaque équipe de répondre par écrit à trois questions :",{"type":30,"tag":620,"props":11679,"children":11680},{},[11681,11686,11691],{"type":30,"tag":624,"props":11682,"children":11683},{},[11684],{"type":36,"value":11685},"\"Pourquoi utilisez-vous cette technologie plutôt que l'alternative ?\"",{"type":30,"tag":624,"props":11687,"children":11688},{},[11689],{"type":36,"value":11690},"\"Qui a pris la décision d'adopter ce pattern et quand ?\"",{"type":30,"tag":624,"props":11692,"children":11693},{},[11694],{"type":36,"value":11695},"\"Avez-vous des décisions en cours avec lesquelles vous n'êtes pas d'accord mais que vous n'avez jamais remises en question ?\"",{"type":30,"tag":38,"props":11697,"children":11698},{},[11699],{"type":36,"value":11700},"La diversité des réponses (et surtout les \"je ne sais pas\") a suffi à convaincre les équipes de changer leurs pratiques.",{"type":30,"tag":38,"props":11702,"children":11703},{},[11704,11706,11712],{"type":36,"value":11705},"Michael Nygard, dans son billet fondateur de 2011 sur les ADRs, posait la même exigence : capturer les décisions architecturalement significatives et leur contexte, pas seulement leur résultat. Ce qui importe, c'est de comprendre ",{"type":30,"tag":11707,"props":11708,"children":11709},"em",{},[11710],{"type":36,"value":11711},"pourquoi",{"type":36,"value":11713}," une décision a été prise, pas seulement laquelle.",{"type":30,"tag":67,"props":11715,"children":11716},{},[],{"type":30,"tag":71,"props":11718,"children":11720},{"id":11719},"le-template-adr-en-5-sections",[11721],{"type":36,"value":11722},"Le template ADR en 5 sections",{"type":30,"tag":38,"props":11724,"children":11725},{},[11726],{"type":36,"value":11727},"Un ADR bien rédigé répond à 5 questions dans l'ordre :",{"type":30,"tag":38,"props":11729,"children":11730},{},[11731,11736],{"type":30,"tag":44,"props":11732,"children":11733},{},[11734],{"type":36,"value":11735},"Contexte",{"type":36,"value":11737}," : quelle situation nécessite cette décision ? Quelles contraintes s'appliquent ? Qui est impliqué ?",{"type":30,"tag":38,"props":11739,"children":11740},{},[11741,11746],{"type":30,"tag":44,"props":11742,"children":11743},{},[11744],{"type":36,"value":11745},"Décision",{"type":36,"value":11747}," : quelle est la décision prise, formulée comme une assertion active (\"Nous utilisons PostgreSQL comme base de données principale\") plutôt que comme une question ouverte.",{"type":30,"tag":38,"props":11749,"children":11750},{},[11751,11756,11758,11764,11766,11772,11773,11779,11780,11786],{"type":30,"tag":44,"props":11752,"children":11753},{},[11754],{"type":36,"value":11755},"Statut",{"type":36,"value":11757}," : ",{"type":30,"tag":98,"props":11759,"children":11761},{"className":11760},[],[11762],{"type":36,"value":11763},"proposed",{"type":36,"value":11765}," | ",{"type":30,"tag":98,"props":11767,"children":11769},{"className":11768},[],[11770],{"type":36,"value":11771},"accepted",{"type":36,"value":11765},{"type":30,"tag":98,"props":11774,"children":11776},{"className":11775},[],[11777],{"type":36,"value":11778},"deprecated",{"type":36,"value":11765},{"type":30,"tag":98,"props":11781,"children":11783},{"className":11782},[],[11784],{"type":36,"value":11785},"superseded",{"type":36,"value":11787},". Le statut évolue dans le temps : une décision peut être remplacée par une nouvelle ADR.",{"type":30,"tag":38,"props":11789,"children":11790},{},[11791,11796],{"type":30,"tag":44,"props":11792,"children":11793},{},[11794],{"type":36,"value":11795},"Conséquences",{"type":36,"value":11797}," : implications positives et négatives. Qu'est-ce qui devient plus simple ? Qu'est-ce qui devient plus difficile ?",{"type":30,"tag":38,"props":11799,"children":11800},{},[11801,11806],{"type":30,"tag":44,"props":11802,"children":11803},{},[11804],{"type":36,"value":11805},"Alternatives considérées",{"type":36,"value":11807}," : quelles autres options ont été évaluées et pourquoi ont-elles été écartées ?",{"type":30,"tag":185,"props":11809,"children":11812},{"code":11810,"language":810,"meta":8,"className":11811,"style":8},"# ADR-0001 : Utilisation de PostgreSQL comme base de données principale\n\n## Contexte\nNotre service de gestion des commandes nécessite une base de données relationnelle\navec support des transactions ACID. L'équipe a de l'expérience avec PostgreSQL et MySQL.\nContrainte : déploiement sur AWS, budget infrastructure limité.\n\n## Décision\nNous utilisons PostgreSQL 15 comme base de données principale pour tous les services\nqui nécessitent une persistance relationnelle.\n\n## Statut\nAccepté (2026-01-14)\n\n## Conséquences\n- Positives : ACID garanti, support JSON natif, extensions riches (PostGIS si besoin)\n- Négatives : scaling vertical nécessaire avant sharding ; expertise spécifique requise\n- Impact sur l'équipe : formation nécessaire pour 3 développeurs juniors\n\n## Alternatives considérées\n- MySQL 8.0 : écarté car support JSON moins mature et licensing Oracle\n- MongoDB : écarté car nos données sont relationnelles\n- DynamoDB : écarté car coût à l'usage imprévisible et expertise absente\n","language-markdown shiki shiki-themes catppuccin-frappe github-dark",[11813],{"type":30,"tag":98,"props":11814,"children":11815},{"__ignoreMap":8},[11816,11825,11832,11841,11849,11857,11865,11872,11880,11888,11896,11903,11911,11919,11926,11934,11947,11959,11971,11978,11986,11998,12010],{"type":30,"tag":439,"props":11817,"children":11818},{"class":441,"line":442},[11819],{"type":30,"tag":439,"props":11820,"children":11822},{"style":11821},"--shiki-default:#E78284;--shiki-default-font-weight:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold",[11823],{"type":36,"value":11824},"# ADR-0001 : Utilisation de PostgreSQL comme base de données principale\n",{"type":30,"tag":439,"props":11826,"children":11827},{"class":441,"line":452},[11828],{"type":30,"tag":439,"props":11829,"children":11830},{"emptyLinePlaceholder":13},[11831],{"type":36,"value":514},{"type":30,"tag":439,"props":11833,"children":11834},{"class":441,"line":478},[11835],{"type":30,"tag":439,"props":11836,"children":11838},{"style":11837},"--shiki-default:#EF9F76;--shiki-default-font-weight:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-weight:bold",[11839],{"type":36,"value":11840},"## Contexte\n",{"type":30,"tag":439,"props":11842,"children":11843},{"class":441,"line":508},[11844],{"type":30,"tag":439,"props":11845,"children":11846},{"style":462},[11847],{"type":36,"value":11848},"Notre service de gestion des commandes nécessite une base de données relationnelle\n",{"type":30,"tag":439,"props":11850,"children":11851},{"class":441,"line":517},[11852],{"type":30,"tag":439,"props":11853,"children":11854},{"style":462},[11855],{"type":36,"value":11856},"avec support des transactions ACID. L'équipe a de l'expérience avec PostgreSQL et MySQL.\n",{"type":30,"tag":439,"props":11858,"children":11859},{"class":441,"line":526},[11860],{"type":30,"tag":439,"props":11861,"children":11862},{"style":462},[11863],{"type":36,"value":11864},"Contrainte : déploiement sur AWS, budget infrastructure limité.\n",{"type":30,"tag":439,"props":11866,"children":11867},{"class":441,"line":547},[11868],{"type":30,"tag":439,"props":11869,"children":11870},{"emptyLinePlaceholder":13},[11871],{"type":36,"value":514},{"type":30,"tag":439,"props":11873,"children":11874},{"class":441,"line":1097},[11875],{"type":30,"tag":439,"props":11876,"children":11877},{"style":11837},[11878],{"type":36,"value":11879},"## Décision\n",{"type":30,"tag":439,"props":11881,"children":11882},{"class":441,"line":1133},[11883],{"type":30,"tag":439,"props":11884,"children":11885},{"style":462},[11886],{"type":36,"value":11887},"Nous utilisons PostgreSQL 15 comme base de données principale pour tous les services\n",{"type":30,"tag":439,"props":11889,"children":11890},{"class":441,"line":1183},[11891],{"type":30,"tag":439,"props":11892,"children":11893},{"style":462},[11894],{"type":36,"value":11895},"qui nécessitent une persistance relationnelle.\n",{"type":30,"tag":439,"props":11897,"children":11898},{"class":441,"line":1250},[11899],{"type":30,"tag":439,"props":11900,"children":11901},{"emptyLinePlaceholder":13},[11902],{"type":36,"value":514},{"type":30,"tag":439,"props":11904,"children":11905},{"class":441,"line":1655},[11906],{"type":30,"tag":439,"props":11907,"children":11908},{"style":11837},[11909],{"type":36,"value":11910},"## Statut\n",{"type":30,"tag":439,"props":11912,"children":11913},{"class":441,"line":1663},[11914],{"type":30,"tag":439,"props":11915,"children":11916},{"style":462},[11917],{"type":36,"value":11918},"Accepté (2026-01-14)\n",{"type":30,"tag":439,"props":11920,"children":11921},{"class":441,"line":1672},[11922],{"type":30,"tag":439,"props":11923,"children":11924},{"emptyLinePlaceholder":13},[11925],{"type":36,"value":514},{"type":30,"tag":439,"props":11927,"children":11928},{"class":441,"line":1688},[11929],{"type":30,"tag":439,"props":11930,"children":11931},{"style":11837},[11932],{"type":36,"value":11933},"## Conséquences\n",{"type":30,"tag":439,"props":11935,"children":11936},{"class":441,"line":1747},[11937,11942],{"type":30,"tag":439,"props":11938,"children":11940},{"style":11939},"--shiki-default:#81C8BE;--shiki-dark:#FFAB70",[11941],{"type":36,"value":6245},{"type":30,"tag":439,"props":11943,"children":11944},{"style":462},[11945],{"type":36,"value":11946}," Positives : ACID garanti, support JSON natif, extensions riches (PostGIS si besoin)\n",{"type":30,"tag":439,"props":11948,"children":11949},{"class":441,"line":1773},[11950,11954],{"type":30,"tag":439,"props":11951,"children":11952},{"style":11939},[11953],{"type":36,"value":6245},{"type":30,"tag":439,"props":11955,"children":11956},{"style":462},[11957],{"type":36,"value":11958}," Négatives : scaling vertical nécessaire avant sharding ; expertise spécifique requise\n",{"type":30,"tag":439,"props":11960,"children":11961},{"class":441,"line":1799},[11962,11966],{"type":30,"tag":439,"props":11963,"children":11964},{"style":11939},[11965],{"type":36,"value":6245},{"type":30,"tag":439,"props":11967,"children":11968},{"style":462},[11969],{"type":36,"value":11970}," Impact sur l'équipe : formation nécessaire pour 3 développeurs juniors\n",{"type":30,"tag":439,"props":11972,"children":11973},{"class":441,"line":1807},[11974],{"type":30,"tag":439,"props":11975,"children":11976},{"emptyLinePlaceholder":13},[11977],{"type":36,"value":514},{"type":30,"tag":439,"props":11979,"children":11980},{"class":441,"line":1859},[11981],{"type":30,"tag":439,"props":11982,"children":11983},{"style":11837},[11984],{"type":36,"value":11985},"## Alternatives considérées\n",{"type":30,"tag":439,"props":11987,"children":11988},{"class":441,"line":1904},[11989,11993],{"type":30,"tag":439,"props":11990,"children":11991},{"style":11939},[11992],{"type":36,"value":6245},{"type":30,"tag":439,"props":11994,"children":11995},{"style":462},[11996],{"type":36,"value":11997}," MySQL 8.0 : écarté car support JSON moins mature et licensing Oracle\n",{"type":30,"tag":439,"props":11999,"children":12000},{"class":441,"line":1965},[12001,12005],{"type":30,"tag":439,"props":12002,"children":12003},{"style":11939},[12004],{"type":36,"value":6245},{"type":30,"tag":439,"props":12006,"children":12007},{"style":462},[12008],{"type":36,"value":12009}," MongoDB : écarté car nos données sont relationnelles\n",{"type":30,"tag":439,"props":12011,"children":12012},{"class":441,"line":1977},[12013,12017],{"type":30,"tag":439,"props":12014,"children":12015},{"style":11939},[12016],{"type":36,"value":6245},{"type":30,"tag":439,"props":12018,"children":12019},{"style":462},[12020],{"type":36,"value":12021}," DynamoDB : écarté car coût à l'usage imprévisible et expertise absente\n",{"type":30,"tag":225,"props":12023,"children":12025},{"cta":227,"href":228,"title":12024,"type":230},"Votre équipe refait les mêmes débats techniques sans jamais capitaliser sur les décisions passées ?",[12026],{"type":30,"tag":38,"props":12027,"children":12028},{},[12029],{"type":36,"value":12030},"Je vois ce pattern dans toutes les équipes que j'accompagne : des heures perdues à reconstituer le contexte de décisions qui auraient dû être documentées. Un appel de 30 minutes suffit à définir la stratégie d'adoption des ADRs adaptée à votre équipe et à identifier les 10 décisions passées à documenter en priorité.",{"type":30,"tag":67,"props":12032,"children":12033},{},[],{"type":30,"tag":71,"props":12035,"children":12037},{"id":12036},"les-3-premières-adrs-à-rédiger",[12038],{"type":36,"value":12039},"Les 3 premières ADRs à rédiger",{"type":30,"tag":38,"props":12041,"children":12042},{},[12043],{"type":36,"value":12044},"Ne commencez pas par les décisions les plus anciennes ni les plus complexes. Commencez par les 3 décisions qui génèrent le plus de questions récurrentes.",{"type":30,"tag":38,"props":12046,"children":12047},{},[12048],{"type":36,"value":12049},"Pour identifier lesquelles, je pose toujours ces questions :",{"type":30,"tag":620,"props":12051,"children":12052},{},[12053,12058,12063],{"type":30,"tag":624,"props":12054,"children":12055},{},[12056],{"type":36,"value":12057},"Quelle décision technique revient en débat à chaque onboarding d'un nouveau développeur ?",{"type":30,"tag":624,"props":12059,"children":12060},{},[12061],{"type":36,"value":12062},"Quelle décision architecturale génère des \"oui mais pourquoi ?\" récurrents en code review ?",{"type":30,"tag":624,"props":12064,"children":12065},{},[12066],{"type":36,"value":12067},"Quelle décision récente mérite d'être documentée pendant que le contexte est encore frais ?",{"type":30,"tag":38,"props":12069,"children":12070},{},[12071,12073,12079],{"type":36,"value":12072},"Pour les décisions prises sans documentation, je recommande d'écrire l'ADR avec ce qu'on sait, même imparfaitement. Un ADR incomplet vaut mieux que pas d'ADR. Le statut ",{"type":30,"tag":98,"props":12074,"children":12076},{"className":12075},[],[12077],{"type":36,"value":12078},"reconstructed",{"type":36,"value":12080}," signale que le document a été rédigé après-coup.",{"type":30,"tag":67,"props":12082,"children":12083},{},[],{"type":30,"tag":71,"props":12085,"children":12087},{"id":12086},"organisation-et-rituels-de-maintenance",[12088],{"type":36,"value":12089},"Organisation et rituels de maintenance",{"type":30,"tag":38,"props":12091,"children":12092},{},[12093],{"type":36,"value":12094},"Je recommande cette structure dans le dépôt Git :",{"type":30,"tag":185,"props":12096,"children":12098},{"code":12097},"docs/\n  architecture/\n    decisions/\n      0001-postgresql-base-de-donnees.md\n      0002-event-driven-architecture.md\n      0003-monorepo-structure.md\n    README.md  ← index des ADRs avec titre et statut\n",[12099],{"type":30,"tag":98,"props":12100,"children":12101},{"__ignoreMap":8},[12102],{"type":36,"value":12097},{"type":30,"tag":38,"props":12104,"children":12105},{},[12106],{"type":36,"value":12107},"Trois règles de maintenance que j'impose à toutes les équipes que j'accompagne :",{"type":30,"tag":38,"props":12109,"children":12110},{},[12111,12116],{"type":30,"tag":44,"props":12112,"children":12113},{},[12114],{"type":36,"value":12115},"Numérotation séquentielle.",{"type":36,"value":12117}," ADR-0001, ADR-0002... La chronologie fait partie de l'information.",{"type":30,"tag":38,"props":12119,"children":12120},{},[12121,12126],{"type":30,"tag":44,"props":12122,"children":12123},{},[12124],{"type":36,"value":12125},"Ne jamais modifier ni supprimer une ADR.",{"type":36,"value":12127}," Seulement la superseder avec une nouvelle. La trace historique a de la valeur : comprendre pourquoi une décision a changé est aussi utile que la décision actuelle.",{"type":30,"tag":38,"props":12129,"children":12130},{},[12131,12136],{"type":30,"tag":44,"props":12132,"children":12133},{},[12134],{"type":36,"value":12135},"Rituel de PR.",{"type":36,"value":12137}," À chaque pull request contenant un changement architectural significatif, vérifier si une ADR doit être créée ou mise à jour. Ce check peut être intégré dans le template de PR.",{"type":30,"tag":38,"props":12139,"children":12140},{},[12141,12143,12149],{"type":36,"value":12142},"L'outil ",{"type":30,"tag":98,"props":12144,"children":12146},{"className":12145},[],[12147],{"type":36,"value":12148},"adr-tools",{"type":36,"value":12150}," (CLI open source) génère et gère les ADRs depuis le terminal. Pour les équipes sur Confluence, l'intégration est possible, mais je garde toujours le dépôt Git comme source de vérité.",{"type":30,"tag":67,"props":12152,"children":12153},{},[],{"type":30,"tag":71,"props":12155,"children":12157},{"id":12156},"le-piège-de-la-rétro-documentation-exhaustive",[12158],{"type":36,"value":12159},"Le piège de la rétro-documentation exhaustive",{"type":30,"tag":9223,"props":12161,"children":12162},{},[12163],{"type":30,"tag":38,"props":12164,"children":12165},{},[12166],{"type":36,"value":12167},"Ne pas essayer de documenter toutes les décisions passées d'un coup. La rétro-documentation exhaustive est un projet qui ne se termine pas et qui décourage l'équipe avant qu'elle prenne le pli.",{"type":30,"tag":38,"props":12169,"children":12170},{},[12171],{"type":36,"value":12172},"Ma recommandation : documenter toutes les nouvelles décisions systématiquement à partir d'aujourd'hui, et rattraper les décisions passées au fil des besoins : lors des onboardings, des débats récurrents, ou des refactorings qui remettent en question des choix passés.",{"type":30,"tag":38,"props":12174,"children":12175},{},[12176],{"type":36,"value":12177},"En 3 mois à ce rythme, les équipes que j'accompagne ont entre 15 et 25 ADRs actives. C'est suffisant pour changer la culture.",{"type":30,"tag":67,"props":12179,"children":12180},{},[],{"type":30,"tag":71,"props":12182,"children":12184},{"id":12183},"ce-que-ça-change-concrètement",[12185],{"type":36,"value":12186},"Ce que ça change concrètement",{"type":30,"tag":252,"props":12188,"children":12189},{},[12190,12205],{"type":30,"tag":256,"props":12191,"children":12192},{},[12193],{"type":30,"tag":260,"props":12194,"children":12195},{},[12196,12201],{"type":30,"tag":264,"props":12197,"children":12198},{},[12199],{"type":36,"value":12200},"Action",{"type":30,"tag":264,"props":12202,"children":12203},{},[12204],{"type":36,"value":7753},{"type":30,"tag":280,"props":12206,"children":12207},{},[12208,12221,12234,12247],{"type":30,"tag":260,"props":12209,"children":12210},{},[12211,12216],{"type":30,"tag":287,"props":12212,"children":12213},{},[12214],{"type":36,"value":12215},"Calculer le coût des décisions non documentées",{"type":30,"tag":287,"props":12217,"children":12218},{},[12219],{"type":36,"value":12220},"Conviction de l'équipe",{"type":30,"tag":260,"props":12222,"children":12223},{},[12224,12229],{"type":30,"tag":287,"props":12225,"children":12226},{},[12227],{"type":36,"value":12228},"Adopter le template en 5 sections",{"type":30,"tag":287,"props":12230,"children":12231},{},[12232],{"type":36,"value":12233},"Format standardisé",{"type":30,"tag":260,"props":12235,"children":12236},{},[12237,12242],{"type":30,"tag":287,"props":12238,"children":12239},{},[12240],{"type":36,"value":12241},"Rédiger les 3 premières ADRs prioritaires",{"type":30,"tag":287,"props":12243,"children":12244},{},[12245],{"type":36,"value":12246},"Premières ADRs dans le dépôt",{"type":30,"tag":260,"props":12248,"children":12249},{},[12250,12255],{"type":30,"tag":287,"props":12251,"children":12252},{},[12253],{"type":36,"value":12254},"Setup le registre et les rituels de PR",{"type":30,"tag":287,"props":12256,"children":12257},{},[12258],{"type":36,"value":12259},"Processus pérenne",{"type":30,"tag":67,"props":12261,"children":12262},{},[],{"type":30,"tag":71,"props":12264,"children":12266},{"id":12265},"faq-sur-les-adrs",[12267],{"type":36,"value":12268},"FAQ sur les ADRs",{"type":30,"tag":714,"props":12270,"children":12271},{},[12272,12277],{"type":30,"tag":718,"props":12273,"children":12274},{},[12275],{"type":36,"value":12276},"1. Où stocker les ADRs : dans le dépôt Git ou dans Confluence/Notion ?",{"type":30,"tag":38,"props":12278,"children":12279},{},[12280],{"type":36,"value":12281},"Dans le dépôt Git, si possible. Les ADRs sont du code de documentation : elles doivent versionner avec le code qu'elles documentent. Une ADR dans Git est visible dans les PR, peut être liée aux commits concernés, et survit aux migrations d'outils. Si votre organisation exige Confluence pour la documentation, utilisez-le en complément, mais gardez le dépôt Git comme source de vérité. J'ai vu trop d'ADRs perdues lors de migrations Confluence.",{"type":30,"tag":714,"props":12283,"children":12284},{},[12285,12290],{"type":30,"tag":718,"props":12286,"children":12287},{},[12288],{"type":36,"value":12289},"2. Faut-il un ADR pour chaque décision technique ou seulement les grandes décisions d'architecture ?",{"type":30,"tag":38,"props":12291,"children":12292},{},[12293,12295,12300],{"type":36,"value":12294},"Seulement les décisions significatives : choix de technologie, choix d'architecture (microservices vs monolithe, synchrone vs asynchrone), patterns de design appliqués à l'ensemble du système, décisions de sécurité ou de conformité. Pas besoin d'ADR pour les décisions de code quotidiennes : quel nom de variable, quelle structure de fichier dans un composant. La règle empirique que j'utilise : si la décision fera débat dans 6 mois, elle mérite une ADR. Par exemple, le choix d'adopter un ",{"type":30,"tag":142,"props":12296,"children":12297},{"href":5},[12298],{"type":36,"value":12299},"pattern Database per Service dans une architecture microservices",{"type":36,"value":12301}," est typiquement le genre de décision qui doit être documentée dans une ADR.",{"type":30,"tag":714,"props":12303,"children":12304},{},[12305,12310],{"type":30,"tag":718,"props":12306,"children":12307},{},[12308],{"type":36,"value":12309},"3. Que faire quand une ADR ancienne devient obsolète ?",{"type":30,"tag":38,"props":12311,"children":12312},{},[12313,12315,12320,12322,12326],{"type":36,"value":12314},"Créer une nouvelle ADR qui supersede l'ancienne. L'ancienne garde son statut ",{"type":30,"tag":98,"props":12316,"children":12318},{"className":12317},[],[12319],{"type":36,"value":11785},{"type":36,"value":12321}," avec une référence vers la nouvelle. Jamais modifier l'ADR originale : la trace historique des décisions a de la valeur, même quand elles sont remplacées. Comprendre ",{"type":30,"tag":11707,"props":12323,"children":12324},{},[12325],{"type":36,"value":11711},{"type":36,"value":12327}," une décision a changé est souvent aussi utile que comprendre la décision actuelle.",{"type":30,"tag":714,"props":12329,"children":12330},{},[12331,12336],{"type":30,"tag":718,"props":12332,"children":12333},{},[12334],{"type":36,"value":12335},"4. Comment introduire les ADRs dans une équipe qui n'a jamais documenté ses décisions ?",{"type":30,"tag":38,"props":12337,"children":12338},{},[12339],{"type":36,"value":12340},"Je commence toujours par l'exemple. Je rédige les 3 premières ADRs moi-même, les présente en réunion d'équipe, et explique la valeur qu'elles auraient eue lors de situations passées concrètes, des situations que l'équipe reconnaît. Ensuite, j'invite les développeurs à rédiger les ADRs pour leurs prochaines décisions avec du support. Après 3 cycles, la pratique s'installe naturellement.",{"type":30,"tag":714,"props":12342,"children":12343},{},[12344,12349],{"type":30,"tag":718,"props":12345,"children":12346},{},[12347],{"type":36,"value":12348},"5. Est-ce que les ADRs ralentissent le processus de décision ?",{"type":30,"tag":38,"props":12350,"children":12351},{},[12352,12354,12359],{"type":36,"value":12353},"Non, et c'est le contrepoint que j'entends toujours. Rédiger une ADR prend 30 minutes. Refaire le même débat dans 6 mois prend 3 heures, mobilise 5 personnes, et produit parfois une décision différente de la précédente sans raison valable. Le retour sur investissement est immédiat après la première ADR utilisée pour clore un débat récurrent. C'est d'autant plus vrai pour un CTO en début de mandat : parmi les ",{"type":30,"tag":142,"props":12355,"children":12356},{"href":11574},[12357],{"type":36,"value":12358},"90 premiers jours d'un CTO",{"type":36,"value":12360},", poser les bases de la gouvernance architecturale (dont les ADRs) est l'une des décisions structurantes les plus durables.",{"type":30,"tag":67,"props":12362,"children":12363},{},[],{"type":30,"tag":225,"props":12365,"children":12366},{"cta":785,"href":786,"title":787,"type":788},[12367],{"type":30,"tag":38,"props":12368,"children":12369},{},[12370],{"type":36,"value":12371},"L'Engineering Maturity Self-Assessment couvre le domaine Gouvernance & Documentation : évaluez votre maturité sur les pratiques de décision architecturale, la documentation, et la cohérence du système. Score et plan d'action en 10 minutes.",{"type":30,"tag":796,"props":12373,"children":12374},{},[12375],{"type":36,"value":800},{"title":8,"searchDepth":452,"depth":452,"links":12377},[12378,12379,12380,12381,12382,12383,12384],{"id":11652,"depth":452,"text":11655},{"id":11719,"depth":452,"text":11722},{"id":12036,"depth":452,"text":12039},{"id":12086,"depth":452,"text":12089},{"id":12156,"depth":452,"text":12159},{"id":12183,"depth":452,"text":12186},{"id":12265,"depth":452,"text":12268},"content:fr:architecture-craft:adr-architecture-decision-record.md","fr/architecture-craft/adr-architecture-decision-record.md","fr/architecture-craft/adr-architecture-decision-record",1775679765178]