[{"data":1,"prerenderedAt":2791},["ShallowReactive",2],{"authors":3,"article-2022-09-01-application-events":331},[4,23,35,48,61,73,85,98,111,124,136,148,161,173,185,197,209,221,233,245,258,270,282,295,307,319],{"id":5,"title":6,"body":7,"description":11,"extension":14,"meta":15,"name":16,"navigation":17,"path":18,"readingTime":19,"seo":20,"stem":21,"__hash__":22},"authors\u002Fauthors\u002Falexandre-guillon.md","Software Engineer",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"md",{},"Alexandre Guillon",true,"\u002Fauthors\u002Falexandre-guillon",1,{"title":6,"description":11},"authors\u002Falexandre-guillon","4tf48mjyjFNqItOHaulICbrjeCyMag1o6801uHeTz98",{"id":24,"title":6,"body":25,"description":11,"extension":14,"meta":29,"name":30,"navigation":17,"path":31,"readingTime":19,"seo":32,"stem":33,"__hash__":34},"authors\u002Fauthors\u002Falexis-ablain.md",{"type":8,"value":26,"toc":27},[],{"title":11,"searchDepth":12,"depth":12,"links":28},[],{},"Alexis Ablain","\u002Fauthors\u002Falexis-ablain",{"title":6,"description":11},"authors\u002Falexis-ablain","_SIAtB7f-39e5t3GiJof81NP47s6MGo2n4gaHkTy1uQ",{"id":36,"title":37,"body":38,"description":11,"extension":14,"meta":42,"name":43,"navigation":17,"path":44,"readingTime":19,"seo":45,"stem":46,"__hash__":47},"authors\u002Fauthors\u002Faxel-shaita.md","Engineering Manager",{"type":8,"value":39,"toc":40},[],{"title":11,"searchDepth":12,"depth":12,"links":41},[],{},"Axel Shaïta","\u002Fauthors\u002Faxel-shaita",{"title":37,"description":11},"authors\u002Faxel-shaita","fK0argUhsBkWLjpTAhY13oYLVzQthcEYkCEdtHWmIgE",{"id":49,"title":50,"body":51,"description":11,"extension":14,"meta":55,"name":56,"navigation":17,"path":57,"readingTime":19,"seo":58,"stem":59,"__hash__":60},"authors\u002Fauthors\u002Fbaptiste-faure.md","Head of Talent Acquisition",{"type":8,"value":52,"toc":53},[],{"title":11,"searchDepth":12,"depth":12,"links":54},[],{},"Baptiste Faure","\u002Fauthors\u002Fbaptiste-faure",{"title":50,"description":11},"authors\u002Fbaptiste-faure","ELisToYtcgHmgdVWZkCclTPV6exZtfyXqhpx1jjbJHs",{"id":62,"title":6,"body":63,"description":11,"extension":14,"meta":67,"name":68,"navigation":17,"path":69,"readingTime":19,"seo":70,"stem":71,"__hash__":72},"authors\u002Fauthors\u002Fbenjamin-bouillot.md",{"type":8,"value":64,"toc":65},[],{"title":11,"searchDepth":12,"depth":12,"links":66},[],{},"Benjamin Bouillot","\u002Fauthors\u002Fbenjamin-bouillot",{"title":6,"description":11},"authors\u002Fbenjamin-bouillot","tbhCFZyfTt7ZM5b5YgqQ2nhgnSTl8BweaQQryc87fHo",{"id":74,"title":37,"body":75,"description":11,"extension":14,"meta":79,"name":80,"navigation":17,"path":81,"readingTime":19,"seo":82,"stem":83,"__hash__":84},"authors\u002Fauthors\u002Fcedric-nicoloso.md",{"type":8,"value":76,"toc":77},[],{"title":11,"searchDepth":12,"depth":12,"links":78},[],{},"Cédric Nicoloso","\u002Fauthors\u002Fcedric-nicoloso",{"title":37,"description":11},"authors\u002Fcedric-nicoloso","ibSoh4VZYiWYTuLOnZTedaAfcnvet1Q9H7ogW0LgorY",{"id":86,"title":87,"body":88,"description":11,"extension":14,"meta":92,"name":93,"navigation":17,"path":94,"readingTime":19,"seo":95,"stem":96,"__hash__":97},"authors\u002Fauthors\u002Fdavid-touzet.md","Staff Engineer",{"type":8,"value":89,"toc":90},[],{"title":11,"searchDepth":12,"depth":12,"links":91},[],{},"David Touzet","\u002Fauthors\u002Fdavid-touzet",{"title":87,"description":11},"authors\u002Fdavid-touzet","dHWwnQxb1Ubt-WwXWEODGEo9AFoq1cJUhfg3kdnYSBM",{"id":99,"title":100,"body":101,"description":11,"extension":14,"meta":105,"name":106,"navigation":17,"path":107,"readingTime":19,"seo":108,"stem":109,"__hash__":110},"authors\u002Fauthors\u002Feloise-chizat.md","Data Engineer",{"type":8,"value":102,"toc":103},[],{"title":11,"searchDepth":12,"depth":12,"links":104},[],{},"Eloïse Chizat","\u002Fauthors\u002Feloise-chizat",{"title":100,"description":11},"authors\u002Feloise-chizat","Utd72Vm9qT4hh2ZbFi6a2_nXw5Wb494Ed_HL1ra5yw8",{"id":112,"title":113,"body":114,"description":11,"extension":14,"meta":118,"name":119,"navigation":17,"path":120,"readingTime":19,"seo":121,"stem":122,"__hash__":123},"authors\u002Fauthors\u002Femmanuel-auclair.md","Staff engineer",{"type":8,"value":115,"toc":116},[],{"title":11,"searchDepth":12,"depth":12,"links":117},[],{},"Emmanuel Auclair","\u002Fauthors\u002Femmanuel-auclair",{"title":113,"description":11},"authors\u002Femmanuel-auclair","MtsA8THNLEn0dTtYEIQaGwDuf7MjQL55IOeei5gugEg",{"id":125,"title":6,"body":126,"description":11,"extension":14,"meta":130,"name":131,"navigation":17,"path":132,"readingTime":19,"seo":133,"stem":134,"__hash__":135},"authors\u002Fauthors\u002Fhoreb-parraud.md",{"type":8,"value":127,"toc":128},[],{"title":11,"searchDepth":12,"depth":12,"links":129},[],{},"Horeb Parraud","\u002Fauthors\u002Fhoreb-parraud",{"title":6,"description":11},"authors\u002Fhoreb-parraud","ajjsnUX4ohZI-ghMdbb92q_taWDkKXVZSLZXoAeLQtg",{"id":137,"title":37,"body":138,"description":11,"extension":14,"meta":142,"name":143,"navigation":17,"path":144,"readingTime":19,"seo":145,"stem":146,"__hash__":147},"authors\u002Fauthors\u002Fhugo-contreras.md",{"type":8,"value":139,"toc":140},[],{"title":11,"searchDepth":12,"depth":12,"links":141},[],{},"Hugo Contreras","\u002Fauthors\u002Fhugo-contreras",{"title":37,"description":11},"authors\u002Fhugo-contreras","2nc3VMu9ASq9Z6Pwx2-7-Ye991Pww4p-UEDBQFfjF-Q",{"id":149,"title":150,"body":151,"description":11,"extension":14,"meta":155,"name":156,"navigation":17,"path":157,"readingTime":19,"seo":158,"stem":159,"__hash__":160},"authors\u002Fauthors\u002Fjulien-tassin.md","Head of Engineering",{"type":8,"value":152,"toc":153},[],{"title":11,"searchDepth":12,"depth":12,"links":154},[],{},"Julien Tassin","\u002Fauthors\u002Fjulien-tassin",{"title":150,"description":11},"authors\u002Fjulien-tassin","iUIHI7SITje38Jh9X9uvYs4-VsHx4eCdt6hAlyLFG_o",{"id":162,"title":6,"body":163,"description":11,"extension":14,"meta":167,"name":168,"navigation":17,"path":169,"readingTime":19,"seo":170,"stem":171,"__hash__":172},"authors\u002Fauthors\u002Flaurent-renard.md",{"type":8,"value":164,"toc":165},[],{"title":11,"searchDepth":12,"depth":12,"links":166},[],{},"Laurent Renard","\u002Fauthors\u002Flaurent-renard",{"title":6,"description":11},"authors\u002Flaurent-renard","5BP7Ed-pt1SQHjh0UJ1XUrlLTcdlFaDoKBCP4deHq8A",{"id":174,"title":6,"body":175,"description":11,"extension":14,"meta":179,"name":180,"navigation":17,"path":181,"readingTime":19,"seo":182,"stem":183,"__hash__":184},"authors\u002Fauthors\u002Fleo-martin.md",{"type":8,"value":176,"toc":177},[],{"title":11,"searchDepth":12,"depth":12,"links":178},[],{},"Léo Martin","\u002Fauthors\u002Fleo-martin",{"title":6,"description":11},"authors\u002Fleo-martin","eYxCHkRgbGDV7shKdTA9s7Tu0zGV4yDGFoKR5MHQntY",{"id":186,"title":6,"body":187,"description":11,"extension":14,"meta":191,"name":192,"navigation":17,"path":193,"readingTime":19,"seo":194,"stem":195,"__hash__":196},"authors\u002Fauthors\u002Floic-bousquet.md",{"type":8,"value":188,"toc":189},[],{"title":11,"searchDepth":12,"depth":12,"links":190},[],{},"Loïc Bousquet","\u002Fauthors\u002Floic-bousquet",{"title":6,"description":11},"authors\u002Floic-bousquet","ko12qZwiGL8XNjAoy9oWypPkIjr29Pbq7vhdtgldqeQ",{"id":198,"title":6,"body":199,"description":11,"extension":14,"meta":203,"name":204,"navigation":17,"path":205,"readingTime":19,"seo":206,"stem":207,"__hash__":208},"authors\u002Fauthors\u002Floic-poullain.md",{"type":8,"value":200,"toc":201},[],{"title":11,"searchDepth":12,"depth":12,"links":202},[],{},"Loïc Poullain","\u002Fauthors\u002Floic-poullain",{"title":6,"description":11},"authors\u002Floic-poullain","oRIyJhFRTqxy5dLCYQ2OnYZ1DB-gLDUM-85vTSYuTF0",{"id":210,"title":100,"body":211,"description":11,"extension":14,"meta":215,"name":216,"navigation":17,"path":217,"readingTime":19,"seo":218,"stem":219,"__hash__":220},"authors\u002Fauthors\u002Fmaud-lelu.md",{"type":8,"value":212,"toc":213},[],{"title":11,"searchDepth":12,"depth":12,"links":214},[],{},"Maud Lélu","\u002Fauthors\u002Fmaud-lelu",{"title":100,"description":11},"authors\u002Fmaud-lelu","MMbsCKuE41OMHusrl12FIEsI-Trx7l8Nn_ANhvj2_y4",{"id":222,"title":37,"body":223,"description":11,"extension":14,"meta":227,"name":228,"navigation":17,"path":229,"readingTime":19,"seo":230,"stem":231,"__hash__":232},"authors\u002Fauthors\u002Fnicolas-poirier.md",{"type":8,"value":224,"toc":225},[],{"title":11,"searchDepth":12,"depth":12,"links":226},[],{},"Nicolas Poirier","\u002Fauthors\u002Fnicolas-poirier",{"title":37,"description":11},"authors\u002Fnicolas-poirier","dXrJkYo8az4SN_D23aYc3fQ7z8s1dR2a0lt1ogjAjJs",{"id":234,"title":37,"body":235,"description":11,"extension":14,"meta":239,"name":240,"navigation":17,"path":241,"readingTime":19,"seo":242,"stem":243,"__hash__":244},"authors\u002Fauthors\u002Fraphael-sauget.md",{"type":8,"value":236,"toc":237},[],{"title":11,"searchDepth":12,"depth":12,"links":238},[],{},"Raphaël Sauget","\u002Fauthors\u002Fraphael-sauget",{"title":37,"description":11},"authors\u002Fraphael-sauget","Uri9bcq0QDuxRA0PbBoNtu7p_5L3dALu4kzcXVW0xyM",{"id":246,"title":247,"body":248,"description":11,"extension":14,"meta":252,"name":253,"navigation":17,"path":254,"readingTime":19,"seo":255,"stem":256,"__hash__":257},"authors\u002Fauthors\u002Fromain-koenig.md","Co-funder & Head of innovation",{"type":8,"value":249,"toc":250},[],{"title":11,"searchDepth":12,"depth":12,"links":251},[],{},"Romain Koenig","\u002Fauthors\u002Fromain-koenig",{"title":247,"description":11},"authors\u002Fromain-koenig","uyS8--eG2_ezyqRABcJnMJmQKKuSArhPWd14aUvFeEw",{"id":259,"title":37,"body":260,"description":11,"extension":14,"meta":264,"name":265,"navigation":17,"path":266,"readingTime":19,"seo":267,"stem":268,"__hash__":269},"authors\u002Fauthors\u002Fromaric-juniet.md",{"type":8,"value":261,"toc":262},[],{"title":11,"searchDepth":12,"depth":12,"links":263},[],{},"Romaric Juniet","\u002Fauthors\u002Fromaric-juniet",{"title":37,"description":11},"authors\u002Fromaric-juniet","4Zb2artgT-eo-PHLXi3xi4d5t7s6PfhUxeSfXIikSUY",{"id":271,"title":6,"body":272,"description":11,"extension":14,"meta":276,"name":277,"navigation":17,"path":278,"readingTime":19,"seo":279,"stem":280,"__hash__":281},"authors\u002Fauthors\u002Fstanyslas-bres.md",{"type":8,"value":273,"toc":274},[],{"title":11,"searchDepth":12,"depth":12,"links":275},[],{},"Stanyslas Bres","\u002Fauthors\u002Fstanyslas-bres",{"title":6,"description":11},"authors\u002Fstanyslas-bres","Xa0SahETuiN4q1jrmR2ych3moAqcZ2LbU7vSfEt2RuU",{"id":283,"title":284,"body":285,"description":11,"extension":14,"meta":289,"name":290,"navigation":17,"path":291,"readingTime":19,"seo":292,"stem":293,"__hash__":294},"authors\u002Fauthors\u002Ftalent-acquisition.md","Talent Acquisition",{"type":8,"value":286,"toc":287},[],{"title":11,"searchDepth":12,"depth":12,"links":288},[],{},"Équipe Talent Acquisition","\u002Fauthors\u002Ftalent-acquisition",{"description":11},"authors\u002Ftalent-acquisition","doDfE76txftQ4wIiKjJoDmSpyzSKk0tzlgVAp6-opAY",{"id":296,"title":6,"body":297,"description":11,"extension":14,"meta":301,"name":302,"navigation":17,"path":303,"readingTime":19,"seo":304,"stem":305,"__hash__":306},"authors\u002Fauthors\u002Fvictor-borg.md",{"type":8,"value":298,"toc":299},[],{"title":11,"searchDepth":12,"depth":12,"links":300},[],{},"Victor Borg","\u002Fauthors\u002Fvictor-borg",{"title":6,"description":11},"authors\u002Fvictor-borg","-Za-JweoiP6hyclue_WkxMXdRUDTczPGlJf6AZckjUc",{"id":308,"title":6,"body":309,"description":11,"extension":14,"meta":313,"name":314,"navigation":17,"path":315,"readingTime":19,"seo":316,"stem":317,"__hash__":318},"authors\u002Fauthors\u002Fvirgil-roger.md",{"type":8,"value":310,"toc":311},[],{"title":11,"searchDepth":12,"depth":12,"links":312},[],{},"Virgil Roger","\u002Fauthors\u002Fvirgil-roger",{"title":6,"description":11},"authors\u002Fvirgil-roger","DfVFe5j0bCgXeEr381ZYOM5DP4m-pWb93J9-m_muKJ0",{"id":320,"title":6,"body":321,"description":11,"extension":14,"meta":325,"name":326,"navigation":17,"path":327,"readingTime":19,"seo":328,"stem":329,"__hash__":330},"authors\u002Fauthors\u002Fyukan-zhao.md",{"type":8,"value":322,"toc":323},[],{"title":11,"searchDepth":12,"depth":12,"links":324},[],{},"Yukan Zhao","\u002Fauthors\u002Fyukan-zhao",{"title":6,"description":11},"authors\u002Fyukan-zhao","LRPHugtAJnWHsmHxy9_SR5Zas_C5p-GR_uHEs1Fhk_E",{"id":332,"title":333,"author":334,"body":335,"date":2781,"description":2782,"extension":14,"lang":2783,"meta":2784,"navigation":17,"path":2785,"published":17,"readingTime":742,"seo":2786,"stem":2787,"tags":2788,"__hash__":2790},"articles\u002Farticles\u002F2022-09-01-application-events.md","Application Events","romain-koenig",{"type":8,"value":336,"toc":2767},[337,345,357,367,371,382,385,407,410,419,422,428,431,436,604,607,610,617,810,813,820,835,841,850,853,941,944,947,958,961,970,976,982,985,991,1197,1200,1298,1301,1304,1315,1324,1327,1333,1336,1343,1352,1493,1508,1522,1525,1599,1602,1668,1671,1674,1683,1689,1696,1699,1702,1708,1723,1726,1736,1747,1753,2376,2382,2387,2390,2396,2399,2572,2584,2590,2593,2740,2743,2746,2752,2755,2758,2763],[338,339,341],"h2",{"id":340},"introduction-des-événements-métiers-chez-indy",[342,343,344],"strong",{},"Introduction des événements métiers chez Indy",[346,347,348,349,353,354],"p",{},"Tag : ",[350,351,352],"code",{},"scalabilité",", ",[350,355,356],{},"architecture",[346,358,359,360,363,364],{},"Authors : ",[350,361,362],{},"Koenig R."," avec l’aide de la team ",[350,365,366],{},"Core",[338,368,370],{"id":369},"termes-employés","Termes employés",[346,372,373,374,381],{},"Qu’entendons-nous par événement métier chez ",[375,376,380],"a",{"href":377,"rel":378},"https:\u002F\u002Fwww.indy.fr\u002F",[379],"nofollow","Indy"," ? Dans le cadre d'Indy et\nde sa comptabilité je vais donner des exemples qui peuvent vous parler, histoire de bien illustrer\nla chose :",[346,383,384],{},"Nous aurons un événement métier lorsque par exemple un utilisateur...",[386,387,388,392,395,398,401,404],"ul",{},[389,390,391],"li",{},"termine son onboarding",[389,393,394],{},"s'abonne",[389,396,397],{},"ajoute son compte bancaire",[389,399,400],{},"clôture sa liasse fiscale",[389,402,403],{},"ajoute une transaction",[389,405,406],{},"change la catégorie d’une transaction",[346,408,409],{},"etc...",[346,411,412,413,418],{},"Lorsque ces événements arrivent, on veut pouvoir exécuter du code qui n’est pas relatif au cœur du\nproduit. J’entends par là, par exemple, que lorsqu’un utilisateur ajoute une transaction, on exécute\nbien le code métier qui ajoute une transaction dans l’application : C’est le produit. Mais on veut\naussi exécuter du code qui n’a rien à voir avec ce cœur de produit et ça peut être assez divers :\nEnvoyer un email ou une notification, mettre à jour un\n",[375,414,417],{"href":415,"rel":416},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCustomer_relationship_management",[379],"CRM",", envoyer une requête à un\nmicroservice.",[346,420,421],{},"Ce code lui est nécessaire au bon fonctionnement de l'entreprise, mais le produit et l’utilisateur,\neux, s’en fichent. Ainsi il serait dommage que l’utilisateur final soit pénalisé par du code destiné\naux équipes d’Indy. Que ce soit en termes d'erreur, bugs ou de délais à cause d'un élément exogène.",[338,423,425],{"id":424},"exemples-de-problèmes-avant",[342,426,427],{},"Exemples de problèmes “Avant”",[346,429,430],{},"Pour illustrer un peu plus loin les problèmes qui ont pu nous mener à cette réflexion, je propose de\nvoir des exemples sur lesquels nous nous sommes cassés un peu les dents :",[346,432,433],{},[342,434,435],{},"Exemples :",[437,438,442],"pre",{"className":439,"code":440,"language":441,"meta":11,"style":11},"language-ts shiki shiki-themes github-light github-dark","import serviceX from 'serviceX';\nimport serviceY from 'serviceY';\nimport CRM1 from 'CRM1';\n\nfunction finalizeOnboarding(...) {\n    const user = await saveUser(); \u002F\u002F do the interesting stuff for the client\n    await serviceX.initConfigurationServiceX();\n    await serviceY.initConfigurationServiceY();\n    \u002F\u002F [...]\n    await serviceX.updateCRM1(); \u002F\u002F That is our stuff\n    return user;\n    }\n","ts",[350,443,444,466,480,495,501,514,540,555,568,574,589,598],{"__ignoreMap":11},[445,446,448,452,456,459,463],"span",{"class":447,"line":19},"line",[445,449,451],{"class":450},"szBVR","import",[445,453,455],{"class":454},"sVt8B"," serviceX ",[445,457,458],{"class":450},"from",[445,460,462],{"class":461},"sZZnC"," 'serviceX'",[445,464,465],{"class":454},";\n",[445,467,468,470,473,475,478],{"class":447,"line":12},[445,469,451],{"class":450},[445,471,472],{"class":454}," serviceY ",[445,474,458],{"class":450},[445,476,477],{"class":461}," 'serviceY'",[445,479,465],{"class":454},[445,481,483,485,488,490,493],{"class":447,"line":482},3,[445,484,451],{"class":450},[445,486,487],{"class":454}," CRM1 ",[445,489,458],{"class":450},[445,491,492],{"class":461}," 'CRM1'",[445,494,465],{"class":454},[445,496,498],{"class":447,"line":497},4,[445,499,500],{"emptyLinePlaceholder":17},"\n",[445,502,504,507,511],{"class":447,"line":503},5,[445,505,506],{"class":450},"function",[445,508,510],{"class":509},"sScJk"," finalizeOnboarding",[445,512,513],{"class":454},"(...) {\n",[445,515,517,520,524,527,530,533,536],{"class":447,"line":516},6,[445,518,519],{"class":450},"    const",[445,521,523],{"class":522},"sj4cs"," user",[445,525,526],{"class":450}," =",[445,528,529],{"class":450}," await",[445,531,532],{"class":509}," saveUser",[445,534,535],{"class":454},"(); ",[445,537,539],{"class":538},"sJ8bj","\u002F\u002F do the interesting stuff for the client\n",[445,541,543,546,549,552],{"class":447,"line":542},7,[445,544,545],{"class":450},"    await",[445,547,548],{"class":454}," serviceX.",[445,550,551],{"class":509},"initConfigurationServiceX",[445,553,554],{"class":454},"();\n",[445,556,558,560,563,566],{"class":447,"line":557},8,[445,559,545],{"class":450},[445,561,562],{"class":454}," serviceY.",[445,564,565],{"class":509},"initConfigurationServiceY",[445,567,554],{"class":454},[445,569,571],{"class":447,"line":570},9,[445,572,573],{"class":538},"    \u002F\u002F [...]\n",[445,575,577,579,581,584,586],{"class":447,"line":576},10,[445,578,545],{"class":450},[445,580,548],{"class":454},[445,582,583],{"class":509},"updateCRM1",[445,585,535],{"class":454},[445,587,588],{"class":538},"\u002F\u002F That is our stuff\n",[445,590,592,595],{"class":447,"line":591},11,[445,593,594],{"class":450},"    return",[445,596,597],{"class":454}," user;\n",[445,599,601],{"class":447,"line":600},12,[445,602,603],{"class":454},"    }\n",[346,605,606],{},"Les trois dernières fonctions sont indépendantes, mais elles doivent être exécutées lors de la\nfinalisation de l'onboarding.",[346,608,609],{},"Le premier problème est qu'une erreur dans une de ces fonctions peut entraîner la non exécution des\nsuivantes, qui pourtant ne sont pas liées (elles n'attendent pas le retour des précédentes) et\nlaisser le code et la data dans un état incertain. Dans le pire des cas : On renvoie même une erreur\nà l’utilisateur qui est bloqué dans son onboarding.",[346,611,612,613,616],{},"Alors on peut répondre à ce problème en ajoutant des ",[350,614,615],{},"try catch"," et c'est ce que nous avons fait :",[437,618,620],{"className":439,"code":619,"language":441,"meta":11,"style":11},"import serviceX from 'serviceX';\nimport serviceY from 'serviceY';\nimport CRM1 from 'CRM1';\n\nfunction finalizeOnboarding(...) {\n    const businessResult = await myBusinessFunction();\n    try {\n        await initConfigurationServiceX();\n    } catch (error) {\n        \u002F\u002F do something... maybe\n    }\n    try {\n        await initConfigurationServiceY();\n    } catch (error) {\n        \u002F\u002F do something... maybe\n    }\n    try {\n        await updateCRM1();\n    } catch (error) {\n        \u002F\u002F do something... maybe it depends of the CRM\n    }\n    return businessResult;\n}\n",[350,621,622,634,646,658,662,670,686,694,704,715,720,724,730,740,749,754,759,766,776,785,791,796,804],{"__ignoreMap":11},[445,623,624,626,628,630,632],{"class":447,"line":19},[445,625,451],{"class":450},[445,627,455],{"class":454},[445,629,458],{"class":450},[445,631,462],{"class":461},[445,633,465],{"class":454},[445,635,636,638,640,642,644],{"class":447,"line":12},[445,637,451],{"class":450},[445,639,472],{"class":454},[445,641,458],{"class":450},[445,643,477],{"class":461},[445,645,465],{"class":454},[445,647,648,650,652,654,656],{"class":447,"line":482},[445,649,451],{"class":450},[445,651,487],{"class":454},[445,653,458],{"class":450},[445,655,492],{"class":461},[445,657,465],{"class":454},[445,659,660],{"class":447,"line":497},[445,661,500],{"emptyLinePlaceholder":17},[445,663,664,666,668],{"class":447,"line":503},[445,665,506],{"class":450},[445,667,510],{"class":509},[445,669,513],{"class":454},[445,671,672,674,677,679,681,684],{"class":447,"line":516},[445,673,519],{"class":450},[445,675,676],{"class":522}," businessResult",[445,678,526],{"class":450},[445,680,529],{"class":450},[445,682,683],{"class":509}," myBusinessFunction",[445,685,554],{"class":454},[445,687,688,691],{"class":447,"line":542},[445,689,690],{"class":450},"    try",[445,692,693],{"class":454}," {\n",[445,695,696,699,702],{"class":447,"line":557},[445,697,698],{"class":450},"        await",[445,700,701],{"class":509}," initConfigurationServiceX",[445,703,554],{"class":454},[445,705,706,709,712],{"class":447,"line":570},[445,707,708],{"class":454},"    } ",[445,710,711],{"class":450},"catch",[445,713,714],{"class":454}," (error) {\n",[445,716,717],{"class":447,"line":576},[445,718,719],{"class":538},"        \u002F\u002F do something... maybe\n",[445,721,722],{"class":447,"line":591},[445,723,603],{"class":454},[445,725,726,728],{"class":447,"line":600},[445,727,690],{"class":450},[445,729,693],{"class":454},[445,731,733,735,738],{"class":447,"line":732},13,[445,734,698],{"class":450},[445,736,737],{"class":509}," initConfigurationServiceY",[445,739,554],{"class":454},[445,741,743,745,747],{"class":447,"line":742},14,[445,744,708],{"class":454},[445,746,711],{"class":450},[445,748,714],{"class":454},[445,750,752],{"class":447,"line":751},15,[445,753,719],{"class":538},[445,755,757],{"class":447,"line":756},16,[445,758,603],{"class":454},[445,760,762,764],{"class":447,"line":761},17,[445,763,690],{"class":450},[445,765,693],{"class":454},[445,767,769,771,774],{"class":447,"line":768},18,[445,770,698],{"class":450},[445,772,773],{"class":509}," updateCRM1",[445,775,554],{"class":454},[445,777,779,781,783],{"class":447,"line":778},19,[445,780,708],{"class":454},[445,782,711],{"class":450},[445,784,714],{"class":454},[445,786,788],{"class":447,"line":787},20,[445,789,790],{"class":538},"        \u002F\u002F do something... maybe it depends of the CRM\n",[445,792,794],{"class":447,"line":793},21,[445,795,603],{"class":454},[445,797,799,801],{"class":447,"line":798},22,[445,800,594],{"class":450},[445,802,803],{"class":454}," businessResult;\n",[445,805,807],{"class":447,"line":806},23,[445,808,809],{"class":454},"}\n",[346,811,812],{},"C'était un compromis simple et rapide, qui nous a permis de continuer un certains temps.",[346,814,815,816,819],{},"Ici nous n'avons plus le problème des erreurs mais nous avons encore des fonctions qui sont liées de\nmanière synchrone. Typiquement si une fonction prend beaucoup de temps, elle va différer l'exécution\ndes suivantes, ainsi que l'exécution de la fonction ",[350,817,818],{},"finalizeOnboarding"," de manière générale.",[346,821,822,823,826,827,830,831,834],{},"On pourrait éventuellement répondre à ce point en les wrappant aussi d'un ",[350,824,825],{},"setImmediate",". Ce que\nnous verrons dans la suite. (aussi possible de faire sans ",[350,828,829],{},"await"," avec un simple ",[350,832,833],{},".catch",")",[346,836,837,838,834],{},"En monitoring, cette fonction nous a montré qu'elle pouvait s'exécuter en 0.2s comme parfois 4s et\nmême plus rarement 40s, en fonction du temps de réponses des API externes... Car souvent nos outils\nsont des SaaS externes ! Ce qui bien sûr peut entraîner un timeout pour le client. Ces événements\ndevenant plus fréquents, cette implémentation est de moins en moins acceptable. (problème\n:",[350,839,840],{},"performances",[346,842,843,844,353,847,834],{},"Un autre problème est la charge mentale. Un autre développeur, s’il ne connaît pas l'historique, ou\ns'il l'a oublié, peut assumer que ces fonctions ne sont pas indépendantes et que l'ordre doit être\nrespecté : Par exemple parce que chaque fonction va muter l'état en base de données et que les\nautres s'attendent à ce nouvel état (ce qui est une mauvaise pratique, mais comment savoir quand on\nne connaît pas le code par cœur ?). Sans une inspection de chaque fonction c'est difficile de\nsavoir, la confiance s'en trouve réduite, et ça freine le développeur dans sa refonte éventuelle,\ndans son ajout de feature etc... ( problèmes: ",[350,845,846],{},"perte de confiance",[350,848,849],{},"pauvre maintenabilité",[346,851,852],{},"Je continue dans ma liste des problèmes : A un autre endroit de l'application nous avions des\nfonctions à exécuter lors d'une modification sur un modèle, c’était fait via un hook :",[437,854,856],{"className":439,"code":855,"language":441,"meta":11,"style":11},"import updateCRM from \".\u002Fcrm.service.js\";\n\nasync function postHook(user) {\n  try {\n    await updateCRM(user);\n  } catch (err) {\n    logError(err);\n  }\n}\n",[350,857,858,872,876,897,904,914,924,932,937],{"__ignoreMap":11},[445,859,860,862,865,867,870],{"class":447,"line":19},[445,861,451],{"class":450},[445,863,864],{"class":454}," updateCRM ",[445,866,458],{"class":450},[445,868,869],{"class":461}," \".\u002Fcrm.service.js\"",[445,871,465],{"class":454},[445,873,874],{"class":447,"line":12},[445,875,500],{"emptyLinePlaceholder":17},[445,877,878,881,884,887,890,894],{"class":447,"line":482},[445,879,880],{"class":450},"async",[445,882,883],{"class":450}," function",[445,885,886],{"class":509}," postHook",[445,888,889],{"class":454},"(",[445,891,893],{"class":892},"s4XuR","user",[445,895,896],{"class":454},") {\n",[445,898,899,902],{"class":447,"line":497},[445,900,901],{"class":450},"  try",[445,903,693],{"class":454},[445,905,906,908,911],{"class":447,"line":503},[445,907,545],{"class":450},[445,909,910],{"class":509}," updateCRM",[445,912,913],{"class":454},"(user);\n",[445,915,916,919,921],{"class":447,"line":516},[445,917,918],{"class":454},"  } ",[445,920,711],{"class":450},[445,922,923],{"class":454}," (err) {\n",[445,925,926,929],{"class":447,"line":542},[445,927,928],{"class":509},"    logError",[445,930,931],{"class":454},"(err);\n",[445,933,934],{"class":447,"line":557},[445,935,936],{"class":454},"  }\n",[445,938,939],{"class":447,"line":570},[445,940,809],{"class":454},[346,942,943],{},"Cette fonction étant dépendante du service de CRM en question qui est un service de haut niveau et\nqui importe d'autres modules.",[346,945,946],{},"Ce qui a pour effet de coupler notre modèle de données à d'autres modèles et services d'autres\nmodules, CRM1 dépendant de tous.",[346,948,949,953,954],{},[950,951],"img",{"alt":11,"src":952},"\u002Fimages\u002FCapture-d%C3%A9cran-de-2022-08-12-15-49-11.png"," ",[955,956,957],"em",{},"Dépendances entre les modules : Intercom\nétant un CRM",[346,959,960],{},"Ce qui entraîne des problèmes d'imports, des dépendances circulaires, des difficultés à comprendre\net étendre le système.",[346,962,963,964,353,967,834],{},"(problèmes: ",[350,965,966],{},"couplages des services",[350,968,969],{},"violation d'architecture",[338,971,973],{"id":972},"solution",[342,974,975],{},"Solution",[346,977,978,979],{},"On veut pouvoir : ",[342,980,981],{},"Exécuter du code sans bloquer la requête initiale",[346,983,984],{},"Bien sûr le code métier qui doit être fait par la route et nécessaire à la réponse faite au client\nsera bloquant, mais le code indépendant qui doit être exécuté ne doit plus l'être pour la requête\ninitiale, comme la mise à jour d'un CRM par exemple.",[346,986,987,988,990],{},"Alors pour répondre au problème du non bloquant nous pouvons utiliser ",[350,989,825],{},":",[437,992,994],{"className":439,"code":993,"language":441,"meta":11,"style":11},"import serviceX from '.\u002FserviceX';\nimport serviceY from '.\u002FserviceY';\nimport CRM1 from '.\u002FCRM1';\n\nfunction finalizeOnboarding(...) {\n    const businessResult = await myBusinessFunction();\n    setImmediate(() =>\n        try {\n            await serviceX.initConfigurationServiceX();\n        } catch (error) {\n            \u002F\u002F do something... maybe it depends of the CRM\n        }\n    ));\n    setImmediate(() =>\n        try {\n            await serviceY.initConfigurationServiceY();\n        } catch (error) {\n            \u002F\u002F do something... maybe\n        }\n    ));\n    setImmediate(() =>\n        try {\n            await serviceX.updateCRM1();\n        } catch (error) {\n            \u002F\u002F do something... maybe\n        }\n    ));\n    return businessResult;\n}\n",[350,995,996,1009,1022,1035,1039,1047,1061,1072,1077,1082,1091,1096,1101,1106,1114,1118,1123,1131,1136,1140,1144,1152,1156,1161,1170,1175,1180,1185,1192],{"__ignoreMap":11},[445,997,998,1000,1002,1004,1007],{"class":447,"line":19},[445,999,451],{"class":450},[445,1001,455],{"class":454},[445,1003,458],{"class":450},[445,1005,1006],{"class":461}," '.\u002FserviceX'",[445,1008,465],{"class":454},[445,1010,1011,1013,1015,1017,1020],{"class":447,"line":12},[445,1012,451],{"class":450},[445,1014,472],{"class":454},[445,1016,458],{"class":450},[445,1018,1019],{"class":461}," '.\u002FserviceY'",[445,1021,465],{"class":454},[445,1023,1024,1026,1028,1030,1033],{"class":447,"line":482},[445,1025,451],{"class":450},[445,1027,487],{"class":454},[445,1029,458],{"class":450},[445,1031,1032],{"class":461}," '.\u002FCRM1'",[445,1034,465],{"class":454},[445,1036,1037],{"class":447,"line":497},[445,1038,500],{"emptyLinePlaceholder":17},[445,1040,1041,1043,1045],{"class":447,"line":503},[445,1042,506],{"class":450},[445,1044,510],{"class":509},[445,1046,513],{"class":454},[445,1048,1049,1051,1053,1055,1057,1059],{"class":447,"line":516},[445,1050,519],{"class":450},[445,1052,676],{"class":522},[445,1054,526],{"class":450},[445,1056,529],{"class":450},[445,1058,683],{"class":509},[445,1060,554],{"class":454},[445,1062,1063,1066,1069],{"class":447,"line":542},[445,1064,1065],{"class":509},"    setImmediate",[445,1067,1068],{"class":454},"(() ",[445,1070,1071],{"class":450},"=>\n",[445,1073,1074],{"class":447,"line":557},[445,1075,1076],{"class":454},"        try {\n",[445,1078,1079],{"class":447,"line":570},[445,1080,1081],{"class":454},"            await serviceX.initConfigurationServiceX();\n",[445,1083,1084,1087,1089],{"class":447,"line":576},[445,1085,1086],{"class":454},"        } ",[445,1088,711],{"class":509},[445,1090,714],{"class":454},[445,1092,1093],{"class":447,"line":591},[445,1094,1095],{"class":538},"            \u002F\u002F do something... maybe it depends of the CRM\n",[445,1097,1098],{"class":447,"line":600},[445,1099,1100],{"class":454},"        }\n",[445,1102,1103],{"class":447,"line":732},[445,1104,1105],{"class":454},"    ));\n",[445,1107,1108,1110,1112],{"class":447,"line":742},[445,1109,1065],{"class":509},[445,1111,1068],{"class":454},[445,1113,1071],{"class":450},[445,1115,1116],{"class":447,"line":751},[445,1117,1076],{"class":454},[445,1119,1120],{"class":447,"line":756},[445,1121,1122],{"class":454},"            await serviceY.initConfigurationServiceY();\n",[445,1124,1125,1127,1129],{"class":447,"line":761},[445,1126,1086],{"class":454},[445,1128,711],{"class":509},[445,1130,714],{"class":454},[445,1132,1133],{"class":447,"line":768},[445,1134,1135],{"class":538},"            \u002F\u002F do something... maybe\n",[445,1137,1138],{"class":447,"line":778},[445,1139,1100],{"class":454},[445,1141,1142],{"class":447,"line":787},[445,1143,1105],{"class":454},[445,1145,1146,1148,1150],{"class":447,"line":793},[445,1147,1065],{"class":509},[445,1149,1068],{"class":454},[445,1151,1071],{"class":450},[445,1153,1154],{"class":447,"line":798},[445,1155,1076],{"class":454},[445,1157,1158],{"class":447,"line":806},[445,1159,1160],{"class":454},"            await serviceX.updateCRM1();\n",[445,1162,1164,1166,1168],{"class":447,"line":1163},24,[445,1165,1086],{"class":454},[445,1167,711],{"class":509},[445,1169,714],{"class":454},[445,1171,1173],{"class":447,"line":1172},25,[445,1174,1135],{"class":538},[445,1176,1178],{"class":447,"line":1177},26,[445,1179,1100],{"class":454},[445,1181,1183],{"class":447,"line":1182},27,[445,1184,1105],{"class":454},[445,1186,1188,1190],{"class":447,"line":1187},28,[445,1189,594],{"class":450},[445,1191,803],{"class":454},[445,1193,1195],{"class":447,"line":1194},29,[445,1196,809],{"class":454},[346,1198,1199],{},"et",[437,1201,1203],{"className":439,"code":1202,"language":441,"meta":11,"style":11},"import updateIntercom from 'intercom.service.js';\n\nasync function postHook(user) {\n    setImmediate(() =>\n        try {\n            await updateIntercom(user);\n        } catch(err) {\n            LogError(err)\n        }\n    ));\n}\n",[350,1204,1205,1219,1223,1237,1245,1249,1264,1273,1286,1290,1294],{"__ignoreMap":11},[445,1206,1207,1209,1212,1214,1217],{"class":447,"line":19},[445,1208,451],{"class":450},[445,1210,1211],{"class":454}," updateIntercom ",[445,1213,458],{"class":450},[445,1215,1216],{"class":461}," 'intercom.service.js'",[445,1218,465],{"class":454},[445,1220,1221],{"class":447,"line":12},[445,1222,500],{"emptyLinePlaceholder":17},[445,1224,1225,1227,1229,1231,1233,1235],{"class":447,"line":482},[445,1226,880],{"class":450},[445,1228,883],{"class":450},[445,1230,886],{"class":509},[445,1232,889],{"class":454},[445,1234,893],{"class":892},[445,1236,896],{"class":454},[445,1238,1239,1241,1243],{"class":447,"line":497},[445,1240,1065],{"class":509},[445,1242,1068],{"class":454},[445,1244,1071],{"class":450},[445,1246,1247],{"class":447,"line":503},[445,1248,1076],{"class":454},[445,1250,1251,1254,1257,1259,1261],{"class":447,"line":516},[445,1252,1253],{"class":454},"            await ",[445,1255,1256],{"class":509},"updateIntercom",[445,1258,889],{"class":454},[445,1260,893],{"class":892},[445,1262,1263],{"class":454},");\n",[445,1265,1266,1268,1270],{"class":447,"line":542},[445,1267,1086],{"class":454},[445,1269,711],{"class":509},[445,1271,1272],{"class":454},"(err) {\n",[445,1274,1275,1278,1280,1283],{"class":447,"line":557},[445,1276,1277],{"class":509},"            LogError",[445,1279,889],{"class":454},[445,1281,1282],{"class":892},"err",[445,1284,1285],{"class":454},")\n",[445,1287,1288],{"class":447,"line":570},[445,1289,1100],{"class":454},[445,1291,1292],{"class":447,"line":576},[445,1293,1105],{"class":454},[445,1295,1296],{"class":447,"line":591},[445,1297,809],{"class":454},[346,1299,1300],{},"Cela peut résoudre le premier point (Toujours est-il que la lecture de la fonction ne s'améliore\npas).",[346,1302,1303],{},"Mais nous avons toujours le problème des dépendances. Pour ça, on va vouloir :",[346,1305,1306,1307],{},"⇒ ",[342,1308,1309,1310,834],{},"Casser la dépendance et découpler notre code applicatif du code qui doit réagir.\n(",[375,1311,1314],{"href":1312,"rel":1313},"https:\u002F\u002Fmartinfowler.com\u002Fbliki\u002FInversionOfControl.html",[379],"Inversion of control",[346,1316,1317,1318,1323],{},"Par exemple, on ne veut plus que le model user dépende du service Intercom qui est un CRM. Cela\nparait évident mais je me permets d'enfoncer le clou avec le OCP\n(",[375,1319,1322],{"href":1320,"rel":1321},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FOpen%E2%80%93closed_principle",[379],"Open Closed Principle",") de SOLID\ncomme quoi le code doit être ouvert à l'extension et fermé à la modification.",[346,1325,1326],{},"Typiquement si demain on ajoute un nouveau CRM, ou un nouveau module métier je ne veux pas avoir à\nmodifier le code de User pour y insérer : \"Importer le code du nouveau module, appeler la bonne\nfonction pour l'action désirée comme mettre à jour le CRM ou initialiser le module\".",[346,1328,1329,1332],{},[342,1330,1331],{},"Mais"," on doit pouvoir ajouter un nouveau service, qui aura une fonction qu'il faut appeler, au\nbon moment, qui ne bloquera pas la fonction métier originelle, qui pourra être monitorée par\nailleurs pour ceux que ça regarde sans polluer ceux qui s'occupent du métier. Le tout étant dans un\nfichier\u002Fdossier\u002Frepo différent qui peut être la responsabilité d'une autre équipe.",[346,1334,1335],{},"Pour avoir l'inversion of control il nous faut le pattern Observer.",[346,1337,1338],{},[375,1339,1342],{"href":1340,"rel":1341},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObserver_pattern",[379],"Observer Pattern",[346,1344,1345,1346,1351],{},"(voir ",[375,1347,1350],{"href":1348,"rel":1349},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FObserver_pattern#JavaScript",[379],"section JavaScript"," pour la suite)",[437,1353,1355],{"className":439,"code":1354,"language":441,"meta":11,"style":11},"class Subject {\n  observers = [];\n  attach(observer) {\n    this.observers.push(observer);\n  }\n  notify() {\n    this.observers.forEach((observer) => {\n      observer.update();\n    });\n  }\n}\n\nclass Observer {\n  update() {\n    \u002F\u002F Do what you have to do\n  }\n}\n",[350,1356,1357,1367,1377,1389,1403,1407,1415,1437,1447,1452,1456,1460,1464,1473,1480,1485,1489],{"__ignoreMap":11},[445,1358,1359,1362,1365],{"class":447,"line":19},[445,1360,1361],{"class":450},"class",[445,1363,1364],{"class":509}," Subject",[445,1366,693],{"class":454},[445,1368,1369,1372,1374],{"class":447,"line":12},[445,1370,1371],{"class":892},"  observers",[445,1373,526],{"class":450},[445,1375,1376],{"class":454}," [];\n",[445,1378,1379,1382,1384,1387],{"class":447,"line":482},[445,1380,1381],{"class":509},"  attach",[445,1383,889],{"class":454},[445,1385,1386],{"class":892},"observer",[445,1388,896],{"class":454},[445,1390,1391,1394,1397,1400],{"class":447,"line":497},[445,1392,1393],{"class":522},"    this",[445,1395,1396],{"class":454},".observers.",[445,1398,1399],{"class":509},"push",[445,1401,1402],{"class":454},"(observer);\n",[445,1404,1405],{"class":447,"line":503},[445,1406,936],{"class":454},[445,1408,1409,1412],{"class":447,"line":516},[445,1410,1411],{"class":509},"  notify",[445,1413,1414],{"class":454},"() {\n",[445,1416,1417,1419,1421,1424,1427,1429,1432,1435],{"class":447,"line":542},[445,1418,1393],{"class":522},[445,1420,1396],{"class":454},[445,1422,1423],{"class":509},"forEach",[445,1425,1426],{"class":454},"((",[445,1428,1386],{"class":892},[445,1430,1431],{"class":454},") ",[445,1433,1434],{"class":450},"=>",[445,1436,693],{"class":454},[445,1438,1439,1442,1445],{"class":447,"line":557},[445,1440,1441],{"class":454},"      observer.",[445,1443,1444],{"class":509},"update",[445,1446,554],{"class":454},[445,1448,1449],{"class":447,"line":570},[445,1450,1451],{"class":454},"    });\n",[445,1453,1454],{"class":447,"line":576},[445,1455,936],{"class":454},[445,1457,1458],{"class":447,"line":591},[445,1459,809],{"class":454},[445,1461,1462],{"class":447,"line":600},[445,1463,500],{"emptyLinePlaceholder":17},[445,1465,1466,1468,1471],{"class":447,"line":732},[445,1467,1361],{"class":450},[445,1469,1470],{"class":509}," Observer",[445,1472,693],{"class":454},[445,1474,1475,1478],{"class":447,"line":742},[445,1476,1477],{"class":509},"  update",[445,1479,1414],{"class":454},[445,1481,1482],{"class":447,"line":751},[445,1483,1484],{"class":538},"    \u002F\u002F Do what you have to do\n",[445,1486,1487],{"class":447,"line":756},[445,1488,936],{"class":454},[445,1490,1491],{"class":447,"line":761},[445,1492,809],{"class":454},[346,1494,1495,1496,1499,1500,1503,1504,1507],{},"Par exemple, car c'est plus clair avec des exemples, nous aurons ici ",[350,1497,1498],{},"User"," qui serait un ",[350,1501,1502],{},"subject","\net les différents modules et\u002Fou CRM des ",[350,1505,1506],{},"observers",".",[346,1509,1510,1511,1513,1514,1517,1518,1521],{},"Comme Intercom observe le user et réagit à ses changements d'état, nous avons l'",[350,1512,1386],{}," qui\nimporte le ",[350,1515,1516],{},"Subjet"," pour avoir accès à la fonction ",[350,1519,1520],{},"attach"," et s'attache.",[346,1523,1524],{},"Le rôle du sujet est juste de signaler son changement d'état via notify et la fonction update de\nl'Observer sera appelé :",[437,1526,1528],{"className":439,"code":1527,"language":441,"meta":11,"style":11},"\u002F\u002F intercom.service.js\nimport User from \".\u002Fuser\";\n\nclass intercom {\n  async update() {\n    \u002F\u002F Build data and call API\n  }\n}\n\nUser.attach(intercom);\n",[350,1529,1530,1535,1549,1553,1562,1572,1577,1581,1585,1589],{"__ignoreMap":11},[445,1531,1532],{"class":447,"line":19},[445,1533,1534],{"class":538},"\u002F\u002F intercom.service.js\n",[445,1536,1537,1539,1542,1544,1547],{"class":447,"line":12},[445,1538,451],{"class":450},[445,1540,1541],{"class":454}," User ",[445,1543,458],{"class":450},[445,1545,1546],{"class":461}," \".\u002Fuser\"",[445,1548,465],{"class":454},[445,1550,1551],{"class":447,"line":482},[445,1552,500],{"emptyLinePlaceholder":17},[445,1554,1555,1557,1560],{"class":447,"line":497},[445,1556,1361],{"class":450},[445,1558,1559],{"class":509}," intercom",[445,1561,693],{"class":454},[445,1563,1564,1567,1570],{"class":447,"line":503},[445,1565,1566],{"class":450},"  async",[445,1568,1569],{"class":509}," update",[445,1571,1414],{"class":454},[445,1573,1574],{"class":447,"line":516},[445,1575,1576],{"class":538},"    \u002F\u002F Build data and call API\n",[445,1578,1579],{"class":447,"line":542},[445,1580,936],{"class":454},[445,1582,1583],{"class":447,"line":557},[445,1584,809],{"class":454},[445,1586,1587],{"class":447,"line":570},[445,1588,500],{"emptyLinePlaceholder":17},[445,1590,1591,1594,1596],{"class":447,"line":576},[445,1592,1593],{"class":454},"User.",[445,1595,1520],{"class":509},[445,1597,1598],{"class":454},"(intercom);\n",[346,1600,1601],{},"Et dans User nous avons désormais :",[437,1603,1605],{"className":439,"code":1604,"language":441,"meta":11,"style":11},"async function postHook(user) {\n    setImmediate(() =>\n        try {\n            this.notify();\n        } catch(err) {\n            LogError(err)\n        }\n    ));\n}\n",[350,1606,1607,1621,1629,1633,1638,1646,1656,1660,1664],{"__ignoreMap":11},[445,1608,1609,1611,1613,1615,1617,1619],{"class":447,"line":19},[445,1610,880],{"class":450},[445,1612,883],{"class":450},[445,1614,886],{"class":509},[445,1616,889],{"class":454},[445,1618,893],{"class":892},[445,1620,896],{"class":454},[445,1622,1623,1625,1627],{"class":447,"line":12},[445,1624,1065],{"class":509},[445,1626,1068],{"class":454},[445,1628,1071],{"class":450},[445,1630,1631],{"class":447,"line":482},[445,1632,1076],{"class":454},[445,1634,1635],{"class":447,"line":497},[445,1636,1637],{"class":454},"            this.notify();\n",[445,1639,1640,1642,1644],{"class":447,"line":503},[445,1641,1086],{"class":454},[445,1643,711],{"class":509},[445,1645,1272],{"class":454},[445,1647,1648,1650,1652,1654],{"class":447,"line":516},[445,1649,1277],{"class":509},[445,1651,889],{"class":454},[445,1653,1282],{"class":892},[445,1655,1285],{"class":454},[445,1657,1658],{"class":447,"line":542},[445,1659,1100],{"class":454},[445,1661,1662],{"class":447,"line":557},[445,1663,1105],{"class":454},[445,1665,1666],{"class":447,"line":570},[445,1667,809],{"class":454},[346,1669,1670],{},"L'import a disparu du fichier User ! 🎉 Nous avons donc un fichier haut niveau qui importe un\nfichier plus bas niveau (User ici) tout est rentré dans l’ordre.",[346,1672,1673],{},"Bon dans cette implémentation naïve, tout est synchrone, la gestion des erreurs n'est pas faite, on\nveut pouvoir avoir des noms d'événements différents pour un même sujet... Il reste du chemin à\nfaire.",[346,1675,1676,1677,1679,1680,1507],{},"Heureusement la plupart des langages offrent de manière native des ",[350,1678,1361],{}," pour implémenter ce\npattern et en Node nous avons ",[350,1681,1682],{},"EventEmitter",[346,1684,1685],{},[375,1686,1687],{"href":1687,"rel":1688},"https:\u002F\u002Fnodejs.org\u002Fapi\u002Fevents.html",[379],[1690,1691,1693],"h3",{"id":1692},"pourquoi-tant-de-focus-sur-ce-découplage",[342,1694,1695],{},"Pourquoi tant de focus sur ce découplage ?",[346,1697,1698],{},"En tant qu'architecte chez Indy, un de mes principaux driver aujourd'hui est de gérer une base de\ncode modifiable par une équipe de plus de 30 développeurs, et de poser les fondations pour une\néquipe de 60 développeurs.",[346,1700,1701],{},"D'expérience, les problèmes de communications priment sur les problèmes techniques pour lesquels on\ntrouve toujours une solution. C'est pourquoi découpler le code, et par extension les teams qui en\nsont responsables est un objectif très haut dans ma roadmap.",[346,1703,1704,1705,1707],{},"Typiquement, sur le code vu plus haut, la gestion du modèle ",[350,1706,893],{}," pourra être l'object d'une squad\nA, pendant que la gestion du CRM1 pourra être la responsabilité d'une squad B etc...",[346,1709,1710,1711,1716,1717,1722],{},"Le code pouvant être protégé par le système de code owners (de\n",[375,1712,1715],{"href":1713,"rel":1714},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Frepositories\u002Fmanaging-your-repositorys-settings-and-features\u002Fcustomizing-your-repository\u002Fabout-code-owners",[379],"Github","\nou ",[375,1718,1721],{"href":1719,"rel":1720},"https:\u002F\u002Fdocs.gitlab.com\u002Fee\u002Fuser\u002Fproject\u002Fcode_owners.html",[379],"GitLab","), et les squads pouvant avoir\ndes priorités différentes, je veux que chacune puisse avancer au maximum sans attendre le retour\nd'une autre.",[346,1724,1725],{},"C'est pourquoi ce découplage est si important à mes yeux.",[346,1727,1728,1731,1732,1735],{},[342,1729,1730],{},"Avec ce pattern, on peut avoir un code métier complètement découplé du code non-métier destiné à\nl’entreprise."," De plus on peut avoir autant de ",[955,1733,1734],{},"listeners"," que l’on veut, ces derniers étant\ndécouplés entre eux aussi.",[346,1737,1738],{},[955,1739,1740,1741,1746],{},"L'enjeu ici, c'est définir les interfaces entre les squads et comment je les sépare. Ici je réponds\nau fait que ce sont les Services externes qui importent User et qui réagissent dessus. Le code entre\nles squads est soumis aux\n",[375,1742,1745],{"href":1743,"rel":1744},"https:\u002F\u002Fdocs.aws.amazon.com\u002Fprescriptive-guidance\u002Flatest\u002Farchitectural-decision-records\u002Fadr-process.html",[379],"ADRs","\n(ensemble de règles et conventions que nous avons entre développeurs chez Indy) ce qui ne sera pas\nforcément le cas du code soumis à une seule squad.",[338,1748,1750],{"id":1749},"implémentation-moins-naïve",[342,1751,1752],{},"Implémentation moins naïve",[437,1754,1756],{"className":439,"code":1755,"language":441,"meta":11,"style":11},"import EventEmitter from \"events\";\nimport _ from \"lodash\";\nimport { createLogger, newEventContext } from \"..\u002F..\u002Flogger.js\";\nimport config from \"..\u002F..\u002Fconfig\";\nimport {\n  getCheckAlreadyRegisteredListener,\n  isEventListenerEnabled,\n} from \".\u002FapplicationEvents.model\";\n\nconst logger = createLogger({ namespace: \"application-events\" });\nconst eventsEnabledConfig = config.private.applicationEvents.events;\nconst listenersEnabledConfig = config.private.applicationEvents.listeners;\nconst eventListenersEnabledConfig = config.private.applicationEvents.eventListenerPairs;\n\nexport function createApplicationEvents() {\n  const eventEmitter = new EventEmitter();\n  eventEmitter.setMaxListeners(10);\n  eventEmitter.on(\"error\", (err) => {\n    logger.error({ err }, \"[ApplicationEvents] Internal event emitter error\");\n  });\n  \u002F\u002F Memoize to prevent log spam\n  const logEventWithoutListener = _.memoize(({ eventName }) => {\n    logger.info(\n      { eventName },\n      \"[ApplicationEvents] An event has been emited but no listeners are registered for this event\",\n    );\n  });\n\n  return {\n    emit(eventName, payload) {\n      \u002F\u002F Don’t trigger the event if it is disabled\n      if (!isEventListenerEnabled({ name: eventName, config: eventsEnabledConfig })) return;\n      const listenerCount = eventEmitter.listenerCount(eventName);\n      if (listenerCount === 0) {\n        logEventWithoutListener({ eventName });\n      }\n      return eventEmitter.emit(eventName, payload);\n    },\n    on(eventName, listenerName, cb) {\n      const eventListenerKey = `${eventName}-${listenerName}`;\n      \u002F\u002F Don’t register the event if the listener is disabled\n      if (!isEventListenerEnabled({ name: listenerName, config: listenersEnabledConfig })) return;\n      \u002F\u002F Don’t register the event if the pair event-listenner is disabled\n      if (!isEventListenerEnabled({ name: eventListenerKey, config: eventListenersEnabledConfig }))\n        return;\n      checkAlreadyRegisteredListener({ eventListenerKey });\n      eventEmitter.on(eventName, async (payload) => {\n        setImmediate(() =>\n          newEventContext({ eventName: eventListenerKey }, () => cb(payload, eventName)),\n        );\n      });\n    },\n  };\n}\n",[350,1757,1758,1772,1786,1800,1814,1820,1825,1830,1842,1846,1868,1880,1892,1904,1908,1920,1938,1953,1976,1992,1997,2002,2030,2040,2045,2053,2058,2062,2066,2073,2090,2096,2119,2139,2155,2164,2170,2184,2190,2212,2237,2243,2261,2267,2281,2289,2298,2321,2331,2348,2354,2360,2365,2371],{"__ignoreMap":11},[445,1759,1760,1762,1765,1767,1770],{"class":447,"line":19},[445,1761,451],{"class":450},[445,1763,1764],{"class":454}," EventEmitter ",[445,1766,458],{"class":450},[445,1768,1769],{"class":461}," \"events\"",[445,1771,465],{"class":454},[445,1773,1774,1776,1779,1781,1784],{"class":447,"line":12},[445,1775,451],{"class":450},[445,1777,1778],{"class":454}," _ ",[445,1780,458],{"class":450},[445,1782,1783],{"class":461}," \"lodash\"",[445,1785,465],{"class":454},[445,1787,1788,1790,1793,1795,1798],{"class":447,"line":482},[445,1789,451],{"class":450},[445,1791,1792],{"class":454}," { createLogger, newEventContext } ",[445,1794,458],{"class":450},[445,1796,1797],{"class":461}," \"..\u002F..\u002Flogger.js\"",[445,1799,465],{"class":454},[445,1801,1802,1804,1807,1809,1812],{"class":447,"line":497},[445,1803,451],{"class":450},[445,1805,1806],{"class":454}," config ",[445,1808,458],{"class":450},[445,1810,1811],{"class":461}," \"..\u002F..\u002Fconfig\"",[445,1813,465],{"class":454},[445,1815,1816,1818],{"class":447,"line":503},[445,1817,451],{"class":450},[445,1819,693],{"class":454},[445,1821,1822],{"class":447,"line":516},[445,1823,1824],{"class":454},"  getCheckAlreadyRegisteredListener,\n",[445,1826,1827],{"class":447,"line":542},[445,1828,1829],{"class":454},"  isEventListenerEnabled,\n",[445,1831,1832,1835,1837,1840],{"class":447,"line":557},[445,1833,1834],{"class":454},"} ",[445,1836,458],{"class":450},[445,1838,1839],{"class":461}," \".\u002FapplicationEvents.model\"",[445,1841,465],{"class":454},[445,1843,1844],{"class":447,"line":570},[445,1845,500],{"emptyLinePlaceholder":17},[445,1847,1848,1851,1854,1856,1859,1862,1865],{"class":447,"line":576},[445,1849,1850],{"class":450},"const",[445,1852,1853],{"class":522}," logger",[445,1855,526],{"class":450},[445,1857,1858],{"class":509}," createLogger",[445,1860,1861],{"class":454},"({ namespace: ",[445,1863,1864],{"class":461},"\"application-events\"",[445,1866,1867],{"class":454}," });\n",[445,1869,1870,1872,1875,1877],{"class":447,"line":591},[445,1871,1850],{"class":450},[445,1873,1874],{"class":522}," eventsEnabledConfig",[445,1876,526],{"class":450},[445,1878,1879],{"class":454}," config.private.applicationEvents.events;\n",[445,1881,1882,1884,1887,1889],{"class":447,"line":600},[445,1883,1850],{"class":450},[445,1885,1886],{"class":522}," listenersEnabledConfig",[445,1888,526],{"class":450},[445,1890,1891],{"class":454}," config.private.applicationEvents.listeners;\n",[445,1893,1894,1896,1899,1901],{"class":447,"line":732},[445,1895,1850],{"class":450},[445,1897,1898],{"class":522}," eventListenersEnabledConfig",[445,1900,526],{"class":450},[445,1902,1903],{"class":454}," config.private.applicationEvents.eventListenerPairs;\n",[445,1905,1906],{"class":447,"line":742},[445,1907,500],{"emptyLinePlaceholder":17},[445,1909,1910,1913,1915,1918],{"class":447,"line":751},[445,1911,1912],{"class":450},"export",[445,1914,883],{"class":450},[445,1916,1917],{"class":509}," createApplicationEvents",[445,1919,1414],{"class":454},[445,1921,1922,1925,1928,1930,1933,1936],{"class":447,"line":756},[445,1923,1924],{"class":450},"  const",[445,1926,1927],{"class":522}," eventEmitter",[445,1929,526],{"class":450},[445,1931,1932],{"class":450}," new",[445,1934,1935],{"class":509}," EventEmitter",[445,1937,554],{"class":454},[445,1939,1940,1943,1946,1948,1951],{"class":447,"line":761},[445,1941,1942],{"class":454},"  eventEmitter.",[445,1944,1945],{"class":509},"setMaxListeners",[445,1947,889],{"class":454},[445,1949,1950],{"class":522},"10",[445,1952,1263],{"class":454},[445,1954,1955,1957,1960,1962,1965,1968,1970,1972,1974],{"class":447,"line":768},[445,1956,1942],{"class":454},[445,1958,1959],{"class":509},"on",[445,1961,889],{"class":454},[445,1963,1964],{"class":461},"\"error\"",[445,1966,1967],{"class":454},", (",[445,1969,1282],{"class":892},[445,1971,1431],{"class":454},[445,1973,1434],{"class":450},[445,1975,693],{"class":454},[445,1977,1978,1981,1984,1987,1990],{"class":447,"line":778},[445,1979,1980],{"class":454},"    logger.",[445,1982,1983],{"class":509},"error",[445,1985,1986],{"class":454},"({ err }, ",[445,1988,1989],{"class":461},"\"[ApplicationEvents] Internal event emitter error\"",[445,1991,1263],{"class":454},[445,1993,1994],{"class":447,"line":787},[445,1995,1996],{"class":454},"  });\n",[445,1998,1999],{"class":447,"line":793},[445,2000,2001],{"class":538},"  \u002F\u002F Memoize to prevent log spam\n",[445,2003,2004,2006,2009,2011,2014,2017,2020,2023,2026,2028],{"class":447,"line":798},[445,2005,1924],{"class":450},[445,2007,2008],{"class":522}," logEventWithoutListener",[445,2010,526],{"class":450},[445,2012,2013],{"class":454}," _.",[445,2015,2016],{"class":509},"memoize",[445,2018,2019],{"class":454},"(({ ",[445,2021,2022],{"class":892},"eventName",[445,2024,2025],{"class":454}," }) ",[445,2027,1434],{"class":450},[445,2029,693],{"class":454},[445,2031,2032,2034,2037],{"class":447,"line":806},[445,2033,1980],{"class":454},[445,2035,2036],{"class":509},"info",[445,2038,2039],{"class":454},"(\n",[445,2041,2042],{"class":447,"line":1163},[445,2043,2044],{"class":454},"      { eventName },\n",[445,2046,2047,2050],{"class":447,"line":1172},[445,2048,2049],{"class":461},"      \"[ApplicationEvents] An event has been emited but no listeners are registered for this event\"",[445,2051,2052],{"class":454},",\n",[445,2054,2055],{"class":447,"line":1177},[445,2056,2057],{"class":454},"    );\n",[445,2059,2060],{"class":447,"line":1182},[445,2061,1996],{"class":454},[445,2063,2064],{"class":447,"line":1187},[445,2065,500],{"emptyLinePlaceholder":17},[445,2067,2068,2071],{"class":447,"line":1194},[445,2069,2070],{"class":450},"  return",[445,2072,693],{"class":454},[445,2074,2076,2079,2081,2083,2085,2088],{"class":447,"line":2075},30,[445,2077,2078],{"class":509},"    emit",[445,2080,889],{"class":454},[445,2082,2022],{"class":892},[445,2084,353],{"class":454},[445,2086,2087],{"class":892},"payload",[445,2089,896],{"class":454},[445,2091,2093],{"class":447,"line":2092},31,[445,2094,2095],{"class":538},"      \u002F\u002F Don’t trigger the event if it is disabled\n",[445,2097,2099,2102,2105,2108,2111,2114,2117],{"class":447,"line":2098},32,[445,2100,2101],{"class":450},"      if",[445,2103,2104],{"class":454}," (",[445,2106,2107],{"class":450},"!",[445,2109,2110],{"class":509},"isEventListenerEnabled",[445,2112,2113],{"class":454},"({ name: eventName, config: eventsEnabledConfig })) ",[445,2115,2116],{"class":450},"return",[445,2118,465],{"class":454},[445,2120,2122,2125,2128,2130,2133,2136],{"class":447,"line":2121},33,[445,2123,2124],{"class":450},"      const",[445,2126,2127],{"class":522}," listenerCount",[445,2129,526],{"class":450},[445,2131,2132],{"class":454}," eventEmitter.",[445,2134,2135],{"class":509},"listenerCount",[445,2137,2138],{"class":454},"(eventName);\n",[445,2140,2142,2144,2147,2150,2153],{"class":447,"line":2141},34,[445,2143,2101],{"class":450},[445,2145,2146],{"class":454}," (listenerCount ",[445,2148,2149],{"class":450},"===",[445,2151,2152],{"class":522}," 0",[445,2154,896],{"class":454},[445,2156,2158,2161],{"class":447,"line":2157},35,[445,2159,2160],{"class":509},"        logEventWithoutListener",[445,2162,2163],{"class":454},"({ eventName });\n",[445,2165,2167],{"class":447,"line":2166},36,[445,2168,2169],{"class":454},"      }\n",[445,2171,2173,2176,2178,2181],{"class":447,"line":2172},37,[445,2174,2175],{"class":450},"      return",[445,2177,2132],{"class":454},[445,2179,2180],{"class":509},"emit",[445,2182,2183],{"class":454},"(eventName, payload);\n",[445,2185,2187],{"class":447,"line":2186},38,[445,2188,2189],{"class":454},"    },\n",[445,2191,2193,2196,2198,2200,2202,2205,2207,2210],{"class":447,"line":2192},39,[445,2194,2195],{"class":509},"    on",[445,2197,889],{"class":454},[445,2199,2022],{"class":892},[445,2201,353],{"class":454},[445,2203,2204],{"class":892},"listenerName",[445,2206,353],{"class":454},[445,2208,2209],{"class":892},"cb",[445,2211,896],{"class":454},[445,2213,2215,2217,2220,2222,2225,2227,2230,2232,2235],{"class":447,"line":2214},40,[445,2216,2124],{"class":450},[445,2218,2219],{"class":522}," eventListenerKey",[445,2221,526],{"class":450},[445,2223,2224],{"class":461}," `${",[445,2226,2022],{"class":454},[445,2228,2229],{"class":461},"}-${",[445,2231,2204],{"class":454},[445,2233,2234],{"class":461},"}`",[445,2236,465],{"class":454},[445,2238,2240],{"class":447,"line":2239},41,[445,2241,2242],{"class":538},"      \u002F\u002F Don’t register the event if the listener is disabled\n",[445,2244,2246,2248,2250,2252,2254,2257,2259],{"class":447,"line":2245},42,[445,2247,2101],{"class":450},[445,2249,2104],{"class":454},[445,2251,2107],{"class":450},[445,2253,2110],{"class":509},[445,2255,2256],{"class":454},"({ name: listenerName, config: listenersEnabledConfig })) ",[445,2258,2116],{"class":450},[445,2260,465],{"class":454},[445,2262,2264],{"class":447,"line":2263},43,[445,2265,2266],{"class":538},"      \u002F\u002F Don’t register the event if the pair event-listenner is disabled\n",[445,2268,2270,2272,2274,2276,2278],{"class":447,"line":2269},44,[445,2271,2101],{"class":450},[445,2273,2104],{"class":454},[445,2275,2107],{"class":450},[445,2277,2110],{"class":509},[445,2279,2280],{"class":454},"({ name: eventListenerKey, config: eventListenersEnabledConfig }))\n",[445,2282,2284,2287],{"class":447,"line":2283},45,[445,2285,2286],{"class":450},"        return",[445,2288,465],{"class":454},[445,2290,2292,2295],{"class":447,"line":2291},46,[445,2293,2294],{"class":509},"      checkAlreadyRegisteredListener",[445,2296,2297],{"class":454},"({ eventListenerKey });\n",[445,2299,2301,2304,2306,2309,2311,2313,2315,2317,2319],{"class":447,"line":2300},47,[445,2302,2303],{"class":454},"      eventEmitter.",[445,2305,1959],{"class":509},[445,2307,2308],{"class":454},"(eventName, ",[445,2310,880],{"class":450},[445,2312,2104],{"class":454},[445,2314,2087],{"class":892},[445,2316,1431],{"class":454},[445,2318,1434],{"class":450},[445,2320,693],{"class":454},[445,2322,2324,2327,2329],{"class":447,"line":2323},48,[445,2325,2326],{"class":509},"        setImmediate",[445,2328,1068],{"class":454},[445,2330,1071],{"class":450},[445,2332,2334,2337,2340,2342,2345],{"class":447,"line":2333},49,[445,2335,2336],{"class":509},"          newEventContext",[445,2338,2339],{"class":454},"({ eventName: eventListenerKey }, () ",[445,2341,1434],{"class":450},[445,2343,2344],{"class":509}," cb",[445,2346,2347],{"class":454},"(payload, eventName)),\n",[445,2349,2351],{"class":447,"line":2350},50,[445,2352,2353],{"class":454},"        );\n",[445,2355,2357],{"class":447,"line":2356},51,[445,2358,2359],{"class":454},"      });\n",[445,2361,2363],{"class":447,"line":2362},52,[445,2364,2189],{"class":454},[445,2366,2368],{"class":447,"line":2367},53,[445,2369,2370],{"class":454},"  };\n",[445,2372,2374],{"class":447,"line":2373},54,[445,2375,809],{"class":454},[338,2377,2379],{"id":2378},"quel-type-de-payload-dans-les-événements-métiers",[342,2380,2381],{},"Quel type de payload dans les événements métiers ?",[346,2383,2384],{},[342,2385,2386],{},"⇒ Payload minimal",[346,2388,2389],{},"Lorsqu'on émet un événement, on peut passer plus ou moins de data à cet évènement pour les listeners\nderrières.",[1690,2391,2393],{"id":2392},"payload-léger",[342,2394,2395],{},"Payload léger",[346,2397,2398],{},"Il s'agit de passer le minimum syndical d'informations pour que les listeners puissent fonctionner,\nce qui a pour but d'alléger au maximum la fonction métier, typiquement on passe des arguments non\nobjets, type String ou Number comme un ID :",[437,2400,2402],{"className":439,"code":2401,"language":441,"meta":11,"style":11},"async function updateUser({ userId, newFields }) {\n    await UserRepository.udpate({ userId, newFields }});\n    User.emit('updateUser', userId);\n}\n\n\u002F\u002F Other file :\nUser.on('updateUser', (userId) => {\n    const user = UserRepository.findOne({ userId });\n    await updateCRM1(user);\n})\n\n\u002F\u002F Other file :\nUser.on('updateUser', (userId) => {\n    const user = UserRepository.findOne({ userId });\n    await updateCRM2(user);\n})\n",[350,2403,2404,2427,2440,2455,2459,2463,2468,2488,2504,2512,2517,2521,2525,2545,2559,2568],{"__ignoreMap":11},[445,2405,2406,2408,2410,2413,2416,2419,2421,2424],{"class":447,"line":19},[445,2407,880],{"class":450},[445,2409,883],{"class":450},[445,2411,2412],{"class":509}," updateUser",[445,2414,2415],{"class":454},"({ ",[445,2417,2418],{"class":892},"userId",[445,2420,353],{"class":454},[445,2422,2423],{"class":892},"newFields",[445,2425,2426],{"class":454}," }) {\n",[445,2428,2429,2431,2434,2437],{"class":447,"line":12},[445,2430,545],{"class":450},[445,2432,2433],{"class":454}," UserRepository.",[445,2435,2436],{"class":509},"udpate",[445,2438,2439],{"class":454},"({ userId, newFields }});\n",[445,2441,2442,2445,2447,2449,2452],{"class":447,"line":482},[445,2443,2444],{"class":454},"    User.",[445,2446,2180],{"class":509},[445,2448,889],{"class":454},[445,2450,2451],{"class":461},"'updateUser'",[445,2453,2454],{"class":454},", userId);\n",[445,2456,2457],{"class":447,"line":497},[445,2458,809],{"class":454},[445,2460,2461],{"class":447,"line":503},[445,2462,500],{"emptyLinePlaceholder":17},[445,2464,2465],{"class":447,"line":516},[445,2466,2467],{"class":538},"\u002F\u002F Other file :\n",[445,2469,2470,2472,2474,2476,2478,2480,2482,2484,2486],{"class":447,"line":542},[445,2471,1593],{"class":454},[445,2473,1959],{"class":509},[445,2475,889],{"class":454},[445,2477,2451],{"class":461},[445,2479,1967],{"class":454},[445,2481,2418],{"class":892},[445,2483,1431],{"class":454},[445,2485,1434],{"class":450},[445,2487,693],{"class":454},[445,2489,2490,2492,2494,2496,2498,2501],{"class":447,"line":557},[445,2491,519],{"class":450},[445,2493,523],{"class":522},[445,2495,526],{"class":450},[445,2497,2433],{"class":454},[445,2499,2500],{"class":509},"findOne",[445,2502,2503],{"class":454},"({ userId });\n",[445,2505,2506,2508,2510],{"class":447,"line":570},[445,2507,545],{"class":450},[445,2509,773],{"class":509},[445,2511,913],{"class":454},[445,2513,2514],{"class":447,"line":576},[445,2515,2516],{"class":454},"})\n",[445,2518,2519],{"class":447,"line":591},[445,2520,500],{"emptyLinePlaceholder":17},[445,2522,2523],{"class":447,"line":600},[445,2524,2467],{"class":538},[445,2526,2527,2529,2531,2533,2535,2537,2539,2541,2543],{"class":447,"line":732},[445,2528,1593],{"class":454},[445,2530,1959],{"class":509},[445,2532,889],{"class":454},[445,2534,2451],{"class":461},[445,2536,1967],{"class":454},[445,2538,2418],{"class":892},[445,2540,1431],{"class":454},[445,2542,1434],{"class":450},[445,2544,693],{"class":454},[445,2546,2547,2549,2551,2553,2555,2557],{"class":447,"line":742},[445,2548,519],{"class":450},[445,2550,523],{"class":522},[445,2552,526],{"class":450},[445,2554,2433],{"class":454},[445,2556,2500],{"class":509},[445,2558,2503],{"class":454},[445,2560,2561,2563,2566],{"class":447,"line":751},[445,2562,545],{"class":450},[445,2564,2565],{"class":509}," updateCRM2",[445,2567,913],{"class":454},[445,2569,2570],{"class":447,"line":756},[445,2571,2516],{"class":454},[346,2573,2574,2575,2104,2580,2583],{},"On pourra essayer de ",[375,2576,2579],{"href":2577,"rel":2578},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FDon%27t_repeat_yourself",[379],"DRY",[955,2581,2582],{},"Don’t repeat\nyourself",") par la suite, mais sans compromettre le driver numéro 1 de découplage.",[1690,2585,2587],{"id":2586},"payload-lourd",[342,2588,2589],{},"Payload lourd",[346,2591,2592],{},"Une autre approche est de préparer le payload pour les listeners, ce qui peut éviter de recopier du\ncode, d'alléger les appels à la base de données :",[437,2594,2596],{"className":439,"code":2595,"language":441,"meta":11,"style":11},"async function updateUser({ userId, newFields }) {\n    await userRepository.udpate({ userId, newFields }});\n    const user = userRepository.findOne({ userId });\n    userEvents.emit('updateUser', user);\n}\n\n\u002F\u002F Other file :\nuserEvents.on('updateUser', (user) => {\n    await updateCRM1(user);\n})\n\n\u002F\u002F Other file :\nuserEvents.on('updateUser', (user) => {\n    await updateCRM2(user);\n})\n",[350,2597,2598,2616,2627,2641,2655,2659,2663,2667,2688,2696,2700,2704,2708,2728,2736],{"__ignoreMap":11},[445,2599,2600,2602,2604,2606,2608,2610,2612,2614],{"class":447,"line":19},[445,2601,880],{"class":450},[445,2603,883],{"class":450},[445,2605,2412],{"class":509},[445,2607,2415],{"class":454},[445,2609,2418],{"class":892},[445,2611,353],{"class":454},[445,2613,2423],{"class":892},[445,2615,2426],{"class":454},[445,2617,2618,2620,2623,2625],{"class":447,"line":12},[445,2619,545],{"class":450},[445,2621,2622],{"class":454}," userRepository.",[445,2624,2436],{"class":509},[445,2626,2439],{"class":454},[445,2628,2629,2631,2633,2635,2637,2639],{"class":447,"line":482},[445,2630,519],{"class":450},[445,2632,523],{"class":522},[445,2634,526],{"class":450},[445,2636,2622],{"class":454},[445,2638,2500],{"class":509},[445,2640,2503],{"class":454},[445,2642,2643,2646,2648,2650,2652],{"class":447,"line":497},[445,2644,2645],{"class":454},"    userEvents.",[445,2647,2180],{"class":509},[445,2649,889],{"class":454},[445,2651,2451],{"class":461},[445,2653,2654],{"class":454},", user);\n",[445,2656,2657],{"class":447,"line":503},[445,2658,809],{"class":454},[445,2660,2661],{"class":447,"line":516},[445,2662,500],{"emptyLinePlaceholder":17},[445,2664,2665],{"class":447,"line":542},[445,2666,2467],{"class":538},[445,2668,2669,2672,2674,2676,2678,2680,2682,2684,2686],{"class":447,"line":557},[445,2670,2671],{"class":454},"userEvents.",[445,2673,1959],{"class":509},[445,2675,889],{"class":454},[445,2677,2451],{"class":461},[445,2679,1967],{"class":454},[445,2681,893],{"class":892},[445,2683,1431],{"class":454},[445,2685,1434],{"class":450},[445,2687,693],{"class":454},[445,2689,2690,2692,2694],{"class":447,"line":570},[445,2691,545],{"class":450},[445,2693,773],{"class":509},[445,2695,913],{"class":454},[445,2697,2698],{"class":447,"line":576},[445,2699,2516],{"class":454},[445,2701,2702],{"class":447,"line":591},[445,2703,500],{"emptyLinePlaceholder":17},[445,2705,2706],{"class":447,"line":600},[445,2707,2467],{"class":538},[445,2709,2710,2712,2714,2716,2718,2720,2722,2724,2726],{"class":447,"line":732},[445,2711,2671],{"class":454},[445,2713,1959],{"class":509},[445,2715,889],{"class":454},[445,2717,2451],{"class":461},[445,2719,1967],{"class":454},[445,2721,893],{"class":892},[445,2723,1431],{"class":454},[445,2725,1434],{"class":450},[445,2727,693],{"class":454},[445,2729,2730,2732,2734],{"class":447,"line":742},[445,2731,545],{"class":450},[445,2733,2565],{"class":509},[445,2735,913],{"class":454},[445,2737,2738],{"class":447,"line":751},[445,2739,2516],{"class":454},[346,2741,2742],{},"Ici on introduit donc une fonction non nécessaire au métier dans la première fonction updateUser, le\ncontrat entre l'évènement et ces listeners est plus fort. Donc la première team doit avoir une\nmeilleure connaissance des listeners potentiellement gérés par d'autres personnes.",[346,2744,2745],{},"Le compromis ici est une interface et une contrainte plus forte entre les teams composées d'humains\npour une optimisation du code.",[338,2747,2749],{"id":2748},"conclusion",[342,2750,2751],{},"Conclusion",[346,2753,2754],{},"Il se trouve que la plupart du temps l'optimisation de code de la solution 2 est négligeable comparé\nau coût de communication humain, et c'est pourquoi ce n'est pas notre priorité et que cette solution\nn'est retenue que dans les cas prouvés comme étant problématiques.",[346,2756,2757],{},"Les deux peuvent fonctionner, mais étant donné que notre driver architectural est le découplage.\nNous allons retenir la solution 1 du payload léger, quitte à gérer les côtés négatifs dans un second\ntemps.",[346,2759,2760],{},[342,2761,2762],{},"On utilise un pattern Observer basé sur le EventEmitter de Node pour découpler le code et les\nteams aux maximum. En ce qui concerne les évènements, le payload sera minimal et les listeners\nseront aussi découpés dans des fichiers différents.",[2764,2765,2766],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":11,"searchDepth":12,"depth":12,"links":2768},[2769,2770,2771,2772,2775,2776,2780],{"id":340,"depth":12,"text":344},{"id":369,"depth":12,"text":370},{"id":424,"depth":12,"text":427},{"id":972,"depth":12,"text":975,"children":2773},[2774],{"id":1692,"depth":482,"text":1695},{"id":1749,"depth":12,"text":1752},{"id":2378,"depth":12,"text":2381,"children":2777},[2778,2779],{"id":2392,"depth":482,"text":2395},{"id":2586,"depth":482,"text":2589},{"id":2748,"depth":12,"text":2751},"2022-09-01","Qu’entendons-nous par événement métier chez Indy ? ","fr",{},"\u002Farticles\u002F2022-09-01-application-events",{"title":333,"description":2782},"articles\u002F2022-09-01-application-events",[2789],"Tech","EKCFqe9k1y4Wxx6V7sbRgbX2TZBoToNO_Dwgl_AR1Wg",1778159243192]