<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Julien Leicher | Artisan développeur durable</title><description>Les derniers articles du blog</description><link>https://julien.leicher.me/</link><item><title>Premier mois en tant que Staff Engineer !</title><link>https://julien.leicher.me/writes/first-month-as-staff-engineer/</link><guid isPermaLink="true">https://julien.leicher.me/writes/first-month-as-staff-engineer/</guid><description>Déjà un mois que j’ai pris mon nouveau poste en tant que Staff Engineer chez Attineos. Petit retour sur cette nouvelle aventure.</description><pubDate>Mon, 24 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Après avoir décidé de &lt;a href=&quot;https://julien.leicher.me/writes/nouveau-depart&quot;&gt;mettre fin&lt;/a&gt; à mon aventure entrepreneuriale en ce début d’année, j’ai donc rejoins la société &lt;a href=&quot;https://www.attineos.com&quot;&gt;Attineos&lt;/a&gt; le 24 Février dernier en tant que &lt;strong&gt;Staff Engineer&lt;/strong&gt; (je vous explique en quoi ça consiste juste après).&lt;/p&gt;
&lt;p&gt;Pourquoi repartir dans une société de services alors que je cherchais plutôt un client final me direz-vous ? Et bien tout simplement parce que &lt;strong&gt;le poste qui m’a été proposé&lt;/strong&gt; me collait comme un gant !&lt;/p&gt;
&lt;p&gt;Cerise sur le gâteau, je retrouve d’anciens collègues que j’apprécie énormément et avec qui je suis ravi de pouvoir travailler de nouveau.&lt;/p&gt;
&lt;h2 id=&quot;cest-quoi-ça-staff-engineer&quot;&gt;C’est quoi ça Staff Engineer ?&lt;/h2&gt;
&lt;p&gt;C’est compliqué ! La fiche de poste qu’on m’a présenté était très vaste et contenait pas mal de choses, allant de l’expertise technique à l’avant-vente.&lt;/p&gt;
&lt;p&gt;Au final, en lisant &lt;a href=&quot;https://touilleur-express.fr/2022/07/17/devenir-staff-engineer/&quot;&gt;des articles&lt;/a&gt;, en regardant &lt;a href=&quot;https://www.youtube.com/watch?v=LgvJ3cuWBYQ&quot;&gt;des vidéos&lt;/a&gt; et en comparant le tout avec ce que je fais au quotidien, je m’aperçois que le &lt;strong&gt;rôle est vraiment dépendant de l’entreprise&lt;/strong&gt; dans lequel il est pratiqué.&lt;/p&gt;
&lt;p&gt;Sur mon premier mois par exemple, aucun jour ne s’est ressemblé. J’ai entre autre :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;contribué au Wiki interne&lt;/li&gt;
&lt;li&gt;participé à de l’avant vente pour apporter des solutions techniques&lt;/li&gt;
&lt;li&gt;pratiqué de la revue de code pour proposer des pistes d’améliorations et partager des bonnes pratiques&lt;/li&gt;
&lt;li&gt;échangé avec des collègues sur certains points techniques précis&lt;/li&gt;
&lt;li&gt;réalisé un &lt;abbr title=&quot;Proof Of Concept&quot;&gt;POC&lt;/abbr&gt; autour de l’accessibilité et de l’éco-conception&lt;/li&gt;
&lt;li&gt;fais de la veille&lt;/li&gt;
&lt;li&gt;participé à pas mal de réunions sur tout un tas de sujets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le moins que l’on puisse dire, c’est que ce fut un premier mois bien chargé ! Et j’ai &lt;strong&gt;adoré&lt;/strong&gt; ! Les semaines qui suivent s’annoncent d’ailleurs tout aussi enrichissantes.&lt;/p&gt;
&lt;p&gt;Au final, il s’agit réellement d’un poste à la fois &lt;strong&gt;transverse et technique&lt;/strong&gt; où le planning se construit au gré des besoins de l’entreprise.&lt;/p&gt;
&lt;h2 id=&quot;à-bicyclette&quot;&gt;À bicyclette…&lt;/h2&gt;
&lt;p&gt;L’entreprise étant sur &lt;strong&gt;Rouen&lt;/strong&gt;, c’était l’occasion pour moi de réfléchir à un mode de transport en accord avec mes convictions, exit la voiture donc et bonjour les &lt;strong&gt;transports en commun&lt;/strong&gt; !&lt;/p&gt;
&lt;p&gt;Sur ce point, j’ai doublement de la chance. D’une part, une gare à 10 minutes de chez moi a ré-ouvert il y a quelques années maintenant et d’autre part, &lt;strong&gt;Attineos&lt;/strong&gt; rembourse 100% des transports en commun.&lt;/p&gt;
&lt;p&gt;Pour le moment, le reste du trajet se fait en &lt;strong&gt;métro/bus&lt;/strong&gt; mais bientôt …&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://julien.leicher.me/_astro/brompton-gare.BHC4i41X_1cJRLw.webp&quot; alt=&quot;Photographie d&amp;#x27;un Brompton C-Line rose replié sur le quai de la gare&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2048&quot; height=&quot;1536&quot;&gt;&lt;/div&gt;
&lt;p&gt;Ce sera en &lt;strong&gt;vélo&lt;/strong&gt; ! J’ai eu l’occasion de tester un Brompton grâce au magasin &lt;a href=&quot;https://rouen.cyclable.com/&quot;&gt;Cyclable&lt;/a&gt; le temps d’une journée et en suis tombé amoureux. Du coup… J’ai &lt;a href=&quot;https://mamot.fr/deck/@julien_leicher/114074265517596303&quot;&gt;commandé le mien&lt;/a&gt; le soir même !&lt;/p&gt;
&lt;h2 id=&quot;pour-finir&quot;&gt;Pour finir&lt;/h2&gt;
&lt;p&gt;En conclusion, je suis vraiment &lt;strong&gt;satisfait&lt;/strong&gt; ! À la fois vis à vis du &lt;strong&gt;poste&lt;/strong&gt; mais aussi de l’&lt;strong&gt;entreprise&lt;/strong&gt; dans laquelle règne une bienveillance qui donne envie de tout donner !&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>Ma veille en 2025</title><link>https://julien.leicher.me/writes/ma-veille-2025/</link><guid isPermaLink="true">https://julien.leicher.me/writes/ma-veille-2025/</guid><description>Ma veille depuis la dernière fois a quelque peu évolué, en particulier au niveau des outils mis en place. Petit point sur ma démarche actuelle.</description><pubDate>Sat, 08 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;mes-canaux-dinformations&quot;&gt;Mes canaux d’informations&lt;/h2&gt;
&lt;p&gt;Plusieurs canaux me permettent de rester informé sur les sujets qui me tiennent à cœur. Cette partie-là n’a pas particulièrement bougé depuis mon dernier article :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Des &lt;strong&gt;flux RSS&lt;/strong&gt; finement choisis&lt;/li&gt;
&lt;li&gt;Des &lt;strong&gt;chaînes Youtube&lt;/strong&gt; spécifiques&lt;/li&gt;
&lt;li&gt;Des &lt;strong&gt;podcasts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mastodon&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Des &lt;strong&gt;bouquins&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Je repasse de temps en temps sur mes flux RSS pour être sûr d’avoir toujours du contenu pertinent et d’ajuster les flux suivis.&lt;/p&gt;
&lt;p&gt;J’ai abandonné &lt;strong&gt;Reddit&lt;/strong&gt;, beaucoup trop de choses à suivre, trop de sujets clivants ou &lt;em&gt;hype&lt;/em&gt; qui me faisait perdre du temps. Au final, j’ai plutôt tendance à chercher des conférences de qualité sur Youtube : &lt;a href=&quot;https://www.youtube.com/@DevoxxFRvideos&quot;&gt;Devoxx France&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/@ddd_eu&quot;&gt;DDD Europe&lt;/a&gt; ou &lt;a href=&quot;https://www.youtube.com/@NDC&quot;&gt;NDC Conferences&lt;/a&gt; par exemple. Ça fait beaucoup de vidéos, je n’ai évidemment pas le temps de tout regarder mais il y a vraiment des sujets passionnants, bien présentés et avec plus de recul que sur Reddit.&lt;/p&gt;
&lt;h2 id=&quot;mes-outils&quot;&gt;Mes outils&lt;/h2&gt;
&lt;p&gt;Je n’en avais pas parlé dans mon précédent article mais je me suis pas mal outillé au fil des années.&lt;/p&gt;
&lt;p&gt;Fervent défenseur d’auto-hébergement, une grosse partie de mes outils est hébergée sur un &lt;a href=&quot;https://www.hetzner.com/cloud/&quot;&gt;VPS Hetzner CX22&lt;/a&gt; sur lequel j’ai installé un &lt;a href=&quot;https://yunohost.org/&quot;&gt;YunoHost&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aujourd’hui j’utilise :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freshrss.org/&quot;&gt;FreshRSS&lt;/a&gt; pour les flux RSS, &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.seazon.feedme&amp;#x26;hl=fr&quot;&gt;FeedMe&lt;/a&gt; sur Android connecté à mon instance.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wallabag.org/&quot;&gt;Wallabag&lt;/a&gt; pour mettre de côté les articles / vidéos que je souhaite lire plus tard depuis n’importe quel appareil (remplace petit à petit &lt;a href=&quot;https://github.com/shaarli/Shaarli&quot;&gt;Shaarli&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Youtube ou &lt;a href=&quot;https://newpipe.net/&quot;&gt;NewPipe&lt;/a&gt; sur Android.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; comme base de connaissances. Il n’est pas très fourni pour le moment mais ça s’étoffe. Je ne paie pas d’abonnement pour la synchro entre mes appareils mais synchronise via le plugin &lt;a href=&quot;https://github.com/Vinzent03/obsidian-git&quot;&gt;Git&lt;/a&gt; sur un &lt;a href=&quot;https://about.gitea.com/&quot;&gt;Gitea&lt;/a&gt; hébergé sur Yunohost aussi.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apps.ankiweb.net/&quot;&gt;Anki&lt;/a&gt; pour me créer des “fiches” et pratiquer l’apprentissage par répétition (pour combler mon âge grandissant 🙂), et du coup j’ai &lt;a href=&quot;https://github.com/YuukanOO/obsidian-flashcards&quot;&gt;développé un petit plugin&lt;/a&gt; pour que certaines notes dans mon Obsidian puissent être envoyées sur mon compte Anki.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;la-pratique&quot;&gt;La pratique&lt;/h2&gt;
&lt;p&gt;Là aussi, peu de changements par rapport à &lt;a href=&quot;https://julien.leicher.me/writes/staying-up-to-date#3-passer-%C3%A0-la-pratique&quot;&gt;2019&lt;/a&gt;, néanmoins, mon &lt;a href=&quot;https://github.com/YuukanOO&quot;&gt;Github&lt;/a&gt; possède quelques projets plus aboutis que ce que j’avais tendance à faire par le passé.&lt;/p&gt;
&lt;p&gt;Je continue à faire énormément d’expérimentations suite à mes différentes lectures et ce blog reste un excellent moyen de partager ce que je découvre. J’aimerais trouver plus de temps pour écrire mais le temps me manque !&lt;/p&gt;
&lt;p&gt;Voilà, je crois avoir fait le tour ! Si jamais vous avez des questions, n’hésitez pas à me contacter 😉&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>Vous n&apos;avez (probablement) pas besoin d&apos;un LLM</title><link>https://julien.leicher.me/writes/yagnllm/</link><guid isPermaLink="true">https://julien.leicher.me/writes/yagnllm/</guid><description>Récemment via mes flux RSS, je suis tombé sur cet article qui nous propose de construire un agent intelligent pour surveiller les performances de nos chers sites web. Il s’agit là d’un bel exemple d’over-engineering. Voyons ça de plus prêt.</description><pubDate>Mon, 27 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Depuis que j’ai suffisamment d’expérience pour m’en sentir légitime, je n’arrête pas de mettre en avant mon &lt;strong&gt;pragmatisme&lt;/strong&gt;, cette capacité à utiliser une approche &lt;strong&gt;adaptée&lt;/strong&gt; et &lt;strong&gt;mesurée&lt;/strong&gt; au problème qu’on cherche à résoudre.&lt;/p&gt;
&lt;p&gt;Alors quand j’ai un exemple parfait du contraire et qui en plus essaie de nous vendre l’utilisation inutile d’un &lt;abbr title=&quot;Large Language Model&quot;&gt;LLM&lt;/abbr&gt;, je me dois de sauter sur l’occasion pour préciser le fond de ma pensée.&lt;/p&gt;
&lt;h2 id=&quot;larticle-en-question&quot;&gt;L’article en question&lt;/h2&gt;
&lt;p&gt;L’&lt;a href=&quot;https://tympanus.net/codrops/2025/01/23/how-to-build-a-web-performance-watchdog-agent-with-agent-ai/&quot;&gt;article est publié sur Codrops&lt;/a&gt; que je suis depuis longtemps pour sa partie &lt;a href=&quot;https://tympanus.net/codrops/collective/&quot;&gt;“The Collective” publiée chaque semaine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Il nous fait part des bienfaits de monitorer les performances d’un site web et d’être prévenu par email lorsque les performances (&lt;abbr title=&quot;First Contentful Paint&quot;&gt;FCP&lt;/abbr&gt;, &lt;abbr title=&quot;Largest Contentful Paint&quot;&gt;LCP&lt;/abbr&gt; et &lt;abbr title=&quot;Cumulative Layout Shift&quot;&gt;CLS&lt;/abbr&gt; en l’occurrence) passent sous certains seuils et nous propose un petit tutoriel pour créer notre propre agent autonome.&lt;/p&gt;
&lt;p&gt;Jusqu’ici, à priori, le but est &lt;strong&gt;louable&lt;/strong&gt;, le tout avec un plan qui ressemble à ceci :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Récupérer les métriques grâce à l’&lt;a href=&quot;https://developers.google.com/speed/docs/insights/rest/v5/pagespeedapi/runpagespeed?hl=fr&quot;&gt;API Lighthouse&lt;/a&gt; de Google&lt;/li&gt;
&lt;li&gt;Traiter et extraire les métriques pertinentes&lt;/li&gt;
&lt;li&gt;Mettre en forme ces données et inclure des actions correctives à mener en cas d’anomalies&lt;/li&gt;
&lt;li&gt;Envoyer l’email avec le HTML ainsi constitué&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;panel note&quot;&gt;&lt;p&gt;Vous voyez en quoi un LLM va pouvoir nous être utile ici ? Moi non plus.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Et pourtant, directement, on nous dirige vers un lien affilié vers &lt;a href=&quot;https://agent.ai/&quot;&gt;Agent.ai&lt;/a&gt; pour commencer ce petit tutoriel.&lt;/p&gt;
&lt;p&gt;Laissons de côté la création de l’agent, c’est du détail et passons au vif du sujet.&lt;/p&gt;
&lt;h2 id=&quot;les-étapes&quot;&gt;Les étapes&lt;/h2&gt;
&lt;h3 id=&quot;récupérer-les-métriques&quot;&gt;Récupérer les métriques&lt;/h3&gt;
&lt;p&gt;Ici pas de mystère, un appel à l’&lt;a href=&quot;https://developers.google.com/speed/docs/insights/rest/v5/pagespeedapi/runpagespeed?hl=fr&quot;&gt;API Lighthouse&lt;/a&gt; et on récupère toutes les informations qui nous intéressent.&lt;/p&gt;
&lt;p&gt;L’auteur enchaîne avec un petit &lt;code&gt;truncate&lt;/code&gt; du retour de l’API qui est plutôt bavarde (bah oui, sinon on risque d’atteindre la limite de contexte du LLM, malin !).&lt;/p&gt;
&lt;h3 id=&quot;extraire-les-métriques-pertinentes&quot;&gt;Extraire les métriques pertinentes&lt;/h3&gt;
&lt;p&gt;C’est là que tout commence à partir en vrille. N’importe quel développeur serait en mesure de &lt;strong&gt;traiter le retour de l’API directement&lt;/strong&gt; en parcourant les données contenues, détecter si les seuils sont dépassés et remettre en forme si nécessaire. Même avec un outil low-code, on pourrait facilement s’en sortir.&lt;/p&gt;
&lt;p&gt;Mais &lt;strong&gt;non&lt;/strong&gt; ! Ce serait bien trop facile, et pas suffisamment disruptif !&lt;/p&gt;
&lt;p&gt;Donnons plutôt un prompt à ChatGPT et serrons les fesses pour qu’il comprenne notre demande :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You are an assistant that extracts relevant information from Lighthouse API results.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Given the following JSON input, extract the specified details and format them in JSON:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Input JSON:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{{sanitizedMetrics}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;### Extract the following details:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. **Website URL**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - The URL of the website being analyzed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. **Metrics**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - `fcp`: First Contentful Paint (in seconds).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - `lcp`: Largest Contentful Paint (in seconds).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - `cls`: Cumulative Layout Shift value.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - `performanceScore`: Performance score (as a percentage).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. **Anomalies**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - For `fcp`: Return `true` if FCP &gt; 2 seconds, otherwise `false`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - For `lcp`: Return `true` if LCP &gt; 2.5 seconds, otherwise `false`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - For `cls`: Return `true` if CLS &gt; 0.1, otherwise `false`.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. **Suggested Actions**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Provide actionable recommendations based on detected anomalies.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;### Expected JSON Output Example:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;url&quot;: &quot;https://example.com&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;metrics&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;fcp&quot;: &quot;2.3&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;lcp&quot;: &quot;1.8&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;cls&quot;: &quot;0.12&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;performanceScore&quot;: &quot;89&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;anomalies&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;fcp&quot;: true,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;lcp&quot;: false,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;cls&quot;: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;suggestedActions&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;Optimize images to improve FCP&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;Reduce layout shifts by reserving space for ads or images&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Bon ok, je me moque et c’est pas très sympa de ma part. Sauf que quand on connaît un minimum le fonctionnement des LLMs (et au passage, &lt;a href=&quot;https://www.youtube.com/watch?v=wjZofJX0v4M&quot;&gt;les vidéos de 3Blue1Brown&lt;/a&gt; sont top), on voit tout de suite ce qui pourrait mal tourner là dedans et à quel point c’est &lt;strong&gt;sous-optimal en terme de resources et de performances&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Faire reposer ne serait-ce que la &lt;strong&gt;logique des seuils&lt;/strong&gt; et le &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring?hl=fr&quot;&gt;&lt;strong&gt;calcul du score de performance&lt;/strong&gt;&lt;/a&gt; dans un prompt me semble personnellement très &lt;strong&gt;bancal&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;panel note&quot;&gt;&lt;p&gt;Ici on pourrait me répondre que pour remplir la propriété &lt;code&gt;suggestedActions&lt;/code&gt;, cela pourrait effectivement être utile.&lt;/p&gt;&lt;p&gt;Si c’était vraiment le cas, on pourrait sortir les métriques de manière traditionnelle et n’utiliser le LLM que pour cette partie ci. Mais à dire vrai, l’API Lighthouse nous retourne déjà une liste d’actions à entreprendre dans le cas d’anomalies…&lt;/p&gt;&lt;/div&gt;
&lt;h3 id=&quot;mettre-en-forme-ces-données-dans-un-email&quot;&gt;Mettre en forme ces données dans un email&lt;/h3&gt;
&lt;p&gt;Une fois encore, mettre en forme un email à partir d’un modèle et de données, même si ça peut s’avérer frustrant en terme de mise en page, c’est quelque chose qu’on &lt;strong&gt;sait faire efficacement&lt;/strong&gt; dans n’importe quel langage &lt;strong&gt;depuis des lustres&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Et une fois encore, l’article repart sur un prompt !&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;You are an assistant that formats data into a professional and visually appealing HTML email body. Use the following JSON data to create a structured and concise email report in HTML format:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Input JSON:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{{processedMetrics}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;### Instructions:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. **Greeting and Introduction**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Include a welcoming line and specify the purpose of the email.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Mention the website URL as a clickable link.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. **Metrics Summary**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Use a styled HTML table to display key metrics with two columns: Metric Name and Value.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Ensure proper alignment, spacing, and readability.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. **Highlighted Anomalies**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - If anomalies are detected, list them as bullet points.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Highlight critical issues using bold text or distinct color.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. **Suggested Actions**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Present actionable recommendations in a numbered list.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. **Closing Statement**:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Conclude with a polite summary encouraging further action.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   - Sign off with &quot;Best regards&quot; and a placeholder for the team name.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;### Requirements:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Use clean and modern HTML with inline styles for email compatibility.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Focus on readability with a professional, minimalist design.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Output **only** the email body in HTML format.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;### Example Structure for Output:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;!DOCTYPE html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;body style=&quot;font-family: Arial, sans-serif; line-height: 1.6; max-width: 600px; margin: 0 auto; color: #333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;!-- Introduction --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;p&gt;Hello,&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;p&gt;Here is the Lighthouse metrics report for your website:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;a href=&quot;https://tympanus.net/codrops/&quot; target=&quot;_blank&quot; style=&quot;color: #4CAF50; text-decoration: none;&quot;&gt;Codrops&amp;#x3C;/a&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;!-- Metrics Summary --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;h2 style=&quot;color: #4CAF50; font-size: 20px;&quot;&gt;Metrics Summary&amp;#x3C;/h2&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;table style=&quot;border-collapse: collapse; width: 100%; margin-bottom: 20px; border: 1px solid #ddd;&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;tr style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;th style=&quot;text-align: left; border-bottom: 2px solid #ddd; padding: 8px;&quot;&gt;Metric&amp;#x3C;/th&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;th style=&quot;text-align: left; border-bottom: 2px solid #ddd; padding: 8px;&quot;&gt;Value&amp;#x3C;/th&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;First Contentful Paint (FCP)&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;1.804 seconds&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;Largest Contentful Paint (LCP)&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;2.045 seconds&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;Cumulative Layout Shift (CLS)&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;0.0006&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;Performance Score&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        &amp;#x3C;td style=&quot;padding: 8px;&quot;&gt;AVERAGE&amp;#x3C;/td&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;/tr&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/table&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;!-- Anomalies --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;h2 style=&quot;color: #FF5722; font-size: 20px;&quot;&gt;Anomalies Detected&amp;#x3C;/h2&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;ul style=&quot;padding-left: 20px; margin-bottom: 20px;&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;li&gt;No anomalies detected for FCP (below 2 seconds).&amp;#x3C;/li&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;li&gt;No anomalies detected for LCP (below 2.5 seconds).&amp;#x3C;/li&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;li&gt;No anomalies detected for CLS (below 0.1).&amp;#x3C;/li&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;/ul&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;!-- Suggested Actions --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;h2 style=&quot;color: #2196F3; font-size: 20px;&quot;&gt;Suggested Actions&amp;#x3C;/h2&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;p style=&quot;color: #555;&quot;&gt;No suggested actions. Your website is performing well based on the current metrics!&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;!-- Closing --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;p style=&quot;margin-top: 20px;&quot;&gt;We recommend continuing to monitor these metrics to maintain a great user experience and optimize performance as needed.&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &amp;#x3C;p style=&quot;margin-top: 20px;&quot;&gt;Best regards,&amp;#x3C;br&gt;&amp;#x3C;strong&gt;Your Performance Watchdog&amp;#x3C;/strong&gt;&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &amp;#x3C;/body&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/html&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apprécions qu’au final le template qui nous serait nécessaire pour faire un rendu comme on sait le faire fait lui même parti du prompt…&lt;/p&gt;
&lt;p&gt;Rien à ajouter pour l’envoi de l’email, c’est du classique et on peut donc s’arrêter là.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Pour moi cet article reflète bien le soucis avec la hype autour des &lt;abbr title=&quot;Intelligence Artificielle&quot;&gt;IA&lt;/abbr&gt; génératives. &lt;strong&gt;Ce n’est pas parce qu’on peut le faire avec ce nouvel outil qu’il faut forcément partir sur cette solution&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sans trop m’avancer, je pense qu’il sera toujours plus efficace de partir sur une solution dite &lt;em&gt;classique&lt;/em&gt; si c’est possible. En l’occurrence ici, reposer sur un LLM est un &lt;strong&gt;énorme gâchis de ressources et d’argent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Alors oui, en mettant en place ce genre d’outil, le marketing pourra mettre en avant l’utilisation de l’IA, mais au final la valeur ajoutée sera nulle voire négative.&lt;/p&gt;
&lt;p&gt;Il serait préférable de ne &lt;strong&gt;réserver ces outils qu’aux problèmes impossibles à résoudre avec des solutions éprouvées&lt;/strong&gt;, surtout quand on prend en compte l’&lt;strong&gt;impact important&lt;/strong&gt; de ces outils &lt;strong&gt;sur notre société&lt;/strong&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>#OpenToWork</title><link>https://julien.leicher.me/writes/nouveau-depart/</link><guid isPermaLink="true">https://julien.leicher.me/writes/nouveau-depart/</guid><description>Après ma petite introspection et les fêtes de fin d’année, mon choix est finalement arrêté !</description><pubDate>Fri, 17 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;clap-de-fin&quot;&gt;Clap de fin&lt;/h2&gt;
&lt;p&gt;La décision ne fût pas facile à prendre mais je pense que c’est le bon moment pour mettre un terme à mon aventure &lt;strong&gt;en tant qu’indépendant&lt;/strong&gt;. Après tout, nouvelle année, nouveau départ !&lt;/p&gt;
&lt;p&gt;Sur ces cinq années à mon compte, pas de regret, je suis content d’avoir tenter le coup et que ça ait durer tout ce temps. Ces années m’auront permis de &lt;strong&gt;prendre un peu de recul&lt;/strong&gt; et de &lt;strong&gt;connaître mes limites&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;ce-que-je-recherche&quot;&gt;Ce que je recherche&lt;/h2&gt;
&lt;p&gt;Maintenant, place à l’avenir !&lt;/p&gt;
&lt;p&gt;J’en parlais déjà dans ma rétrospective mais ce qui me manque le plus et que je souhaite retrouver, c’est avant tout le &lt;strong&gt;travail d’équipe&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;moi-en-quelques-mots&quot;&gt;Moi en quelques mots&lt;/h3&gt;
&lt;p&gt;Je suis &lt;strong&gt;développeur&lt;/strong&gt;, officiellement depuis l’acquisition de mon diplôme de fin d’étude en &lt;strong&gt;2012&lt;/strong&gt;, officieusement depuis plus longtemps encore.&lt;/p&gt;
&lt;p&gt;J’aime voir les choses prendre forme et &lt;strong&gt;résoudre des problèmes métiers complexes&lt;/strong&gt; grâce à des &lt;strong&gt;solutions élégantes et maintenables&lt;/strong&gt; permettant d’apporter un maximum de valeurs aux utilisateurs.&lt;/p&gt;
&lt;p&gt;J’aime &lt;a href=&quot;https://github.com/YuukanOO/&quot;&gt;&lt;strong&gt;expérimenter&lt;/strong&gt;&lt;/a&gt; de nouvelles technologies, de nouvelles approches me permettant d’&lt;strong&gt;élargir mes connaissances&lt;/strong&gt; et d’aborder les problèmes sous différents angles.&lt;/p&gt;
&lt;p&gt;J’aime également &lt;strong&gt;partager&lt;/strong&gt; ce que je découvre au fil des années, notamment au travers de &lt;a href=&quot;https://julien.leicher.me/writes&quot;&gt;ce blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Je suis &lt;strong&gt;honnête&lt;/strong&gt; et connaît mes lacunes, je n’ai aucun problème à admettre que j’ai tort ou que je ne maîtrise pas un sujet. C’est pour cette raison que &lt;a href=&quot;https://julien.leicher.me/writes/staying-up-to-date&quot;&gt;la veille technologique&lt;/a&gt; tient une place importante dans ma vie.&lt;/p&gt;
&lt;p&gt;Je suis de nature &lt;strong&gt;réservée&lt;/strong&gt; mais ces dernières années ont prouvé que j’étais capable de me dépasser.&lt;/p&gt;
&lt;p&gt;Si vous souhaitez en savoir plus sur mes compétences, n’hésitez pas à consulter la page &lt;a href=&quot;https://julien.leicher.me/is&quot;&gt;à propos&lt;/a&gt; qui fait office de CV. N’hésitez pas à consulter aussi ce site qui reflète à mon sens assez bien ma personnalité.&lt;/p&gt;
&lt;h3 id=&quot;ma-fiche-de-poste-idéale-en-2025&quot;&gt;Ma fiche de poste idéale en 2025&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Type de structure&lt;/strong&gt; : Client final. Étant déjà passé par une &lt;abbr title=&quot;Entreprise de Services du Numérique&quot;&gt;ESN&lt;/abbr&gt; et l’auto-entreprenariat, j’aimerai mettre mes compétences directement au service de l’entreprise finale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secteur d’activité&lt;/strong&gt; : Cette question est difficile. Étant préoccupé par les enjeux environnementaux et la place du numérique, une entreprise mettant en avant un &lt;strong&gt;numérique responsable&lt;/strong&gt; serait l’idéal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Type de poste&lt;/strong&gt; : &lt;strong&gt;Technique&lt;/strong&gt; avant tout, au sein d’une &lt;strong&gt;équipe&lt;/strong&gt; de taille raisonnable. De ce côté là rien n’a changé, avec la volonté de faire les choses &lt;strong&gt;proprement&lt;/strong&gt; et surtout de &lt;strong&gt;bien comprendre le besoin et le fonctionnel&lt;/strong&gt;. J’ai une affinité particulière pour le &lt;strong&gt;back-end&lt;/strong&gt;, mais je prends aussi plaisir à développer des interfaces utilisateurs et des applications mobiles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technologies&lt;/strong&gt; : Je suis plutôt &lt;strong&gt;polyvalent&lt;/strong&gt;, en ce moment j’adore le &lt;strong&gt;Go&lt;/strong&gt; pour sa simplicité, mais j’ai fait beaucoup de &lt;strong&gt;C#&lt;/strong&gt; et de &lt;strong&gt;Typescript&lt;/strong&gt; jusqu’ici. Les autres langages ne sont pas rédhibitoires mais il faudra compter un temps de formation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rythme de travail&lt;/strong&gt; : 2 jours de &lt;abbr title=&quot;Télétravail&quot;&gt;TT&lt;/abbr&gt;, le reste en présentiel. Pas de full-remote.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Localisation&lt;/strong&gt; : Dans un &lt;strong&gt;rayon de 30km autour de Bourg-Achard&lt;/strong&gt; (ce qui couvre notamment &lt;strong&gt;Rouen&lt;/strong&gt;, &lt;strong&gt;Pont-Audemer&lt;/strong&gt;, &lt;strong&gt;Brionne&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Salaire&lt;/strong&gt; : Raisonnable. Oui ce n’est pas précis mais ça dépend de tellement de paramètres…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Comme il s’agit d’un &lt;strong&gt;idéal&lt;/strong&gt;, je suis bien évidemment prêt à faire des concessions en fonction de la proposition.&lt;/p&gt;
&lt;p&gt;Si vous pensez que nous pourrions &lt;strong&gt;collaborer&lt;/strong&gt; ensemble, n’hésitez pas à me contacter ! Et sinon, n’hésitez pas à &lt;strong&gt;partager&lt;/strong&gt; ce post 😉.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>Ce n&apos;est pas un échec, c&apos;est juste que ça n&apos;a pas marché</title><link>https://julien.leicher.me/writes/retrospective-2024/</link><guid isPermaLink="true">https://julien.leicher.me/writes/retrospective-2024/</guid><description>Derrière ce titre clin d’œil, petite rétrospective de cette année professionnellement très calme, l’occasion de faire le point sur ma situation et sur les prochaines étapes.</description><pubDate>Mon, 16 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;D’un point de vue activité professionnelle, l’année 2024 a été, il faut le dire, &lt;strong&gt;mauvaise&lt;/strong&gt;. Comme d’habitude chez moi, baisse d’activité = moments de doute, syndrome de l’imposteur et autres joyeusetés.&lt;/p&gt;
&lt;p&gt;Alors bien sûr, je pourrais broyer du noir dans mon coin, mais je préfère admettre mes difficultés afin d’y voir plus clair et de préparer la suite. Allons-y !&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://julien.leicher.me/_astro/fun-with-flags-retrospective.B7KLyUXS_1JwK3X.webp&quot; alt=&quot;Image de la série The Big Bang Theory, épisode de la rétrospective de l&amp;#x27;émission Fun With Flags de Sheldon Cooper&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;854&quot; height=&quot;480&quot;&gt;&lt;/div&gt;
&lt;h2 id=&quot;un-début-dannée-au-ralenti&quot;&gt;Un début d’année au ralenti&lt;/h2&gt;
&lt;p&gt;En début d’année, je n’ai pas spécialement cherché de nouvelles missions. J’avais encore quelques paiements qui devaient arriver suite à ma mission de fin d’année donc j’étais plutôt tranquille de ce côté là.&lt;/p&gt;
&lt;p&gt;Côté perso, la maison de mon défunt père a été vendue ce qui a permis de clôturer la succession. Hasard ou non, cette période a coïncidé avec quelques problèmes de santé qui ont entaché ma motivation.&lt;/p&gt;
&lt;p&gt;Afin de me remotiver, j’ai repris timidement la course à pied et consacré pas mal de temps à mon &lt;a href=&quot;https://julien.leicher.me/writes/seelf-v2&quot;&gt;petit projet du moment&lt;/a&gt;. Comme à mon habitude, je continuais en parallèle ma &lt;a href=&quot;https://julien.leicher.me/writes/staying-up-to-date&quot;&gt;veille technologique&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;les-beaux-jours&quot;&gt;Les beaux jours&lt;/h2&gt;
&lt;p&gt;En Juin, toujours très peu d’activité, principalement des missions d’audit de quelques jours et quelques prises de contact mais rien n’ayant débouché sur un contrat. Je restais tout de même attentif aux éventuelles missions sur les plateformes freelance mais je constatais que la demande était beaucoup plus faible qu’à l’accoutumée.&lt;/p&gt;
&lt;p&gt;Pratiquant le VTT depuis très longtemps, j’ai décidé de me faire plaisir et d’acquérir à cette période le petit &lt;em&gt;Gravel&lt;/em&gt; de chez Intersport : le &lt;a href=&quot;https://www.intersport.fr/velo_gravel_adulte_allroad_250-nakamura-p-YF60H6~8QQ/&quot;&gt;Nakamura ALLROAD 250&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://julien.leicher.me/_astro/allroad250.Bh471Qgg_ugq3U.webp&quot; alt=&quot;Photo du Nakamura ALLROAD 250 aux abords de la Risle&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2048&quot; height=&quot;1536&quot;&gt;&lt;/div&gt;
&lt;p&gt;Comme l’activité était toujours très calme, j’ai &lt;strong&gt;profité des beaux jours&lt;/strong&gt; pour rôder mon nouveau vélo 😁 et passer du temps en famille !&lt;/p&gt;
&lt;h2 id=&quot;quand-vient-la-fin-de-lété&quot;&gt;Quand vient la fin de l’été&lt;/h2&gt;
&lt;p&gt;Forcément, avec si peu d’activité, &lt;strong&gt;remise en question&lt;/strong&gt; de mon approche et de mes compétences avec un soupçon de culpabilité à avoir autant profiter de l’été pendant que certains se démenaient au travail.&lt;/p&gt;
&lt;p&gt;À ce moment, j’essaie tant bien que mal de trouver une mission mais ça ne mord pas, l’activité semble être au ralenti ou &lt;strong&gt;peut-être est-ce moi&lt;/strong&gt; ?&lt;/p&gt;
&lt;p&gt;J’en profite tout de même pour migrer mon site de &lt;a href=&quot;https://iles.pages.dev/&quot;&gt;îles&lt;/a&gt; vers &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; et changer de style au passage.&lt;/p&gt;
&lt;p&gt;Connaissant la demande pour le C# dans ma région, je me &lt;strong&gt;remets également à jour sur le langage&lt;/strong&gt; avec quelques petits projets et &lt;a href=&quot;https://www.youtube.com/@ddd_eu/videos&quot;&gt;conférences&lt;/a&gt; &lt;a href=&quot;https://www.youtube.com/@NDC/videos&quot;&gt;bien choisies&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En Novembre, petit passage comme tous les ans à &lt;a href=&quot;https://www.codeursenseine.com/2024&quot;&gt;Codeurs en Seine&lt;/a&gt;, l’occasion de revoir pas mal de monde !&lt;/p&gt;
&lt;h2 id=&quot;un-constat-simpose&quot;&gt;Un constat s’impose&lt;/h2&gt;
&lt;p&gt;Désormais nous sommes en Décembre et il est temps de faire le bilan de cette année.&lt;/p&gt;
&lt;p&gt;Il me semble évident aujourd’hui que si je n’ai trouvé que peu de missions, c’est &lt;strong&gt;en grande partie de ma faute&lt;/strong&gt;. D’une part la &lt;strong&gt;prospection&lt;/strong&gt; reste un exercice difficile pour moi et d’autre part, la &lt;strong&gt;motivation&lt;/strong&gt; n’y était pas non plus.&lt;/p&gt;
&lt;p&gt;Pour ce dernier point, les raisons sont multiples :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;l’essor de l’IA et cette volonté maladive de vouloir à tout prix nous la vendre à toute les sauces en omettant volontairement &lt;a href=&quot;https://www.wheresyoured.at/subprimeai/&quot;&gt;tous&lt;/a&gt; &lt;a href=&quot;https://arxiv.org/pdf/2304.03271&quot;&gt;ses&lt;/a&gt; &lt;a href=&quot;https://danslesalgorithmes.net/2024/10/02/les-mythes-de-lia/&quot;&gt;aspects&lt;/a&gt; négatifs (comme toute techno, il y’a des cas d’usage intéressants, mais il faut faire preuve de discernement)&lt;/li&gt;
&lt;li&gt;la situation politique de la France, ridicule au possible&lt;/li&gt;
&lt;li&gt;la situation mondiale, les guerres (je n’ai pas eu le courage de regarder &lt;a href=&quot;https://www.youtube.com/watch?v=8Kv6STkOHdk&amp;#x26;rco=1&quot;&gt;le documentaire sur Gaza d’Aymeric Caron&lt;/a&gt; en entier), la trajectoire des différents gouvernements, …&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En bref, le fait que l’&lt;strong&gt;avenir soit si incertain&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;D’un autre côté, après en avoir échangé avec d’autres personnes dans le milieu, je ne suis pas le seul à avoir constaté un &lt;strong&gt;marché moins dynamique&lt;/strong&gt; que d’habitude.&lt;/p&gt;
&lt;p&gt;Jusqu’ici, depuis que je suis indépendant, je n’ai pas spécialement eu à chercher de missions. On me contactait directement via le bouche-à-oreille pour me proposer du travail et ça m’allait très bien.&lt;/p&gt;
&lt;h2 id=&quot;et-maintenant&quot;&gt;Et maintenant ?&lt;/h2&gt;
&lt;p&gt;Même si cette cinquième année est singulière, force est d’admettre que je me pose beaucoup de questions.&lt;/p&gt;
&lt;p&gt;Ma passion pour le &lt;strong&gt;code bien fait&lt;/strong&gt;, orienté &lt;strong&gt;métier&lt;/strong&gt;, est &lt;strong&gt;intacte&lt;/strong&gt;. J’aime trouver des &lt;strong&gt;solutions élégantes&lt;/strong&gt;, &lt;strong&gt;approfondir mes connaissances&lt;/strong&gt; et les &lt;strong&gt;partager&lt;/strong&gt; et cette année n’a pas fait exception. Même si pour le coup, c’était principalement sur des projets personnels.&lt;/p&gt;
&lt;p&gt;Mais faire partie d’une &lt;strong&gt;équipe&lt;/strong&gt; me manque. En tant qu’indépendant, j’ai souvent l’impression d’être un &lt;em&gt;outsider&lt;/em&gt; (ce qui n’est pas entièrement faux).&lt;/p&gt;
&lt;p&gt;Peut-être est-il temps de &lt;strong&gt;clore ce chapitre&lt;/strong&gt; et de &lt;strong&gt;commencer une nouvelle aventure&lt;/strong&gt; ?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>seelf v2.0.0 🍾 !</title><link>https://julien.leicher.me/writes/seelf-v2/</link><guid isPermaLink="true">https://julien.leicher.me/writes/seelf-v2/</guid><description>Déploiements distants, nouvelle documentation, logo, la v2.0.0 de seelf est enfin disponible !</description><pubDate>Fri, 19 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Étant donné que ce début d’année est &lt;strong&gt;professionnellement très calme&lt;/strong&gt;, je ne me suis pas laissé abattre et ai profité de ce temps disponible pour d’une part &lt;strong&gt;accentuer ma veille&lt;/strong&gt;, et d’autre part &lt;strong&gt;travailler sur un projet&lt;/strong&gt; qui me tient à cœur, j’ai nommé &lt;a href=&quot;https://julien.leicher.me/writes/seelf-public-release&quot;&gt;seelf&lt;/a&gt;, quelle surprise !&lt;/p&gt;
&lt;h2 id=&quot;un-travail-de-titan&quot;&gt;Un travail de titan&lt;/h2&gt;
&lt;p&gt;Cette &lt;strong&gt;v2&lt;/strong&gt; est le résultat d’un travail acharné de plusieurs mois, notamment en terme de &lt;a href=&quot;https://yuukanoo.github.io/seelf/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Autant de temps car pour ceux qui ont l’occasion de travailler sur des projets perso, vous savez probablement à quel point il est &lt;strong&gt;difficile de mener à bout un projet&lt;/strong&gt; dont vous êtes (quasi) le seul à vous préoccuper et de &lt;strong&gt;conserver la motivation&lt;/strong&gt; nécessaire à son avancée tout en étant &lt;strong&gt;extrêmement exigeant&lt;/strong&gt; envers vous-même.&lt;/p&gt;
&lt;p&gt;Néanmoins, mon &lt;strong&gt;objectif est atteint&lt;/strong&gt; et je suis on ne peut plus &lt;strong&gt;fier&lt;/strong&gt; du résultat !&lt;/p&gt;
&lt;h2 id=&quot;les-nouveautés&quot;&gt;Les nouveautés&lt;/h2&gt;
&lt;p&gt;Faisons un petit tour rapide de cette &lt;a href=&quot;https://github.com/YuukanOO/seelf/releases/tag/v2.0.0&quot;&gt;nouvelle version&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://julien.leicher.me/_astro/seelf-home.CXwVrCBN_2iKlVU.webp&quot; alt=&quot;Capture d&amp;#x27;écran de l&amp;#x27;accueil de seelf v2&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2050&quot; height=&quot;1396&quot;&gt;&lt;/div&gt;
&lt;h3 id=&quot;gestion-des-cibles-de-déploiement&quot;&gt;Gestion des cibles de déploiement&lt;/h3&gt;
&lt;p&gt;Jusque ici, vous pouviez déployer vos applications uniquement sur le serveur sur lequel était installé seelf. Désormais, il est possible de configurer des &lt;strong&gt;hôtes distants&lt;/strong&gt; sous forme de &lt;strong&gt;cibles de déploiement&lt;/strong&gt; !&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://julien.leicher.me/_astro/seelf-targets.CO3X-DRh_ZCcWHf.webp&quot; alt=&quot;Capture d&amp;#x27;écran de la liste des cibles de déploiement&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2072&quot; height=&quot;1214&quot;&gt;&lt;/div&gt;
&lt;p&gt;Une fois une cible créée, il vous est possible de &lt;strong&gt;configurer une application&lt;/strong&gt; afin de déterminer, pour chaque environnement (&lt;em&gt;production&lt;/em&gt; et &lt;em&gt;staging&lt;/em&gt;), sur &lt;strong&gt;quelle cible elle sera déployée&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Ça n’a l’air de rien comme ça, mais cela permet tout un tas de &lt;strong&gt;nouveaux cas d’utilisation&lt;/strong&gt; !&lt;/p&gt;
&lt;p&gt;Je me suis au passage pas mal pris la tête sur tout ce qui pouvait mal se passer lors de l’utilisation de cibles distantes : cible indisponible ponctuellement, supprimée, nettoyage lors du changement de cible et j’en passe.&lt;/p&gt;
&lt;h3 id=&quot;un-logo&quot;&gt;Un logo !&lt;/h3&gt;
&lt;p&gt;Comme vous l’avez sans doute constaté dans les captures ci-dessus, &lt;strong&gt;seelf&lt;/strong&gt; possède désormais un &lt;strong&gt;logo réalisé par mes soins&lt;/strong&gt;. Je ne suis pas trop mécontent du résultat, là aussi 😁.&lt;/p&gt;
&lt;h3 id=&quot;une-documentation-dédiée&quot;&gt;Une documentation dédiée&lt;/h3&gt;
&lt;p&gt;Jusqu’ici, la documentation n’était qu’un simple fichier Markdown à la racine du &lt;a href=&quot;https://github.com/YuukanOO/seelf&quot;&gt;dépôt Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Désormais, seelf possède &lt;strong&gt;une vraie &lt;a href=&quot;https://yuukanoo.github.io/seelf/&quot;&gt;documentation&lt;/a&gt; dédiée&lt;/strong&gt;, beaucoup plus fournie même s’il reste encore des choses à ajouter / affiner au cours du temps.&lt;/p&gt;
&lt;h3 id=&quot;et-tout-un-tas-de-petites-choses&quot;&gt;Et tout un tas de petites choses …&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Redéploiement automatique&lt;/strong&gt; en cas de changement de configuration, possibilité de &lt;strong&gt;changer l’URL d’une cible sans redéployer les applications&lt;/strong&gt; liées, &lt;strong&gt;page de visualisation des tâches&lt;/strong&gt; asynchrones et possibilité d’en &lt;a href=&quot;https://yuukanoo.github.io/seelf/reference/jobs.html#cancellation&quot;&gt;annuler certaines&lt;/a&gt;, et bien sûr de la &lt;strong&gt;refacto en grande pompe&lt;/strong&gt; pour toujours rendre le changement et l’évolution du code plus simple et ainsi préparer la suite.&lt;/p&gt;
&lt;p&gt;Voilà qui fait un peu le tour de cette nouvelle version, n’hésitez pas à jeter un coup d’œil à la &lt;a href=&quot;https://yuukanoo.github.io/seelf/&quot;&gt;toute nouvelle documentation&lt;/a&gt;, de &lt;a href=&quot;https://github.com/YuukanOO/seelf&quot;&gt;mettre une étoile sur le dépôt Github&lt;/a&gt; ou bien un &lt;a href=&quot;https://www.reddit.com/r/golang/comments/1c71p3b/seelf_v2_a_selfhosted_deployment_platform_written/&quot;&gt;petit&lt;/a&gt; &lt;a href=&quot;https://www.reddit.com/r/selfhosted/comments/1c7plcg/seelf_v2_a_lightweight_selfhosted_deployment/&quot;&gt;upvote&lt;/a&gt; sur Reddit 😉.&lt;/p&gt;
&lt;p&gt;Et si vous avez &lt;strong&gt;besoin d’un développeur&lt;/strong&gt; pour votre prochain projet, n’hésitez pas à &lt;a href=&quot;https://www.linkedin.com/in/julien-leicher-05372b27&quot;&gt;prendre contact&lt;/a&gt;, je suis &lt;strong&gt;disponible&lt;/strong&gt; !&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>Modéliser des agrégats grâce au pattern &quot;Decider&quot;</title><link>https://julien.leicher.me/writes/the-decider-pattern/</link><guid isPermaLink="true">https://julien.leicher.me/writes/the-decider-pattern/</guid><description>Modéliser les transitions d’un agrégat peut s’avérer complexe, d’autant plus dans un langage fonctionnel dans lequel les structures de données représentent le cœur du langage. Heureusement je découvre cette semaine le pattern Decider !</description><pubDate>Fri, 08 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Ce n’est un secret pour aucun d’entre vous, j’aime l’&lt;strong&gt;approche orientée métier&lt;/strong&gt; dans le développement logiciel, ça a totalement changé ma manière de développer. Alors quand je découvre un pattern qui va dans ce sens, je suis bien &lt;strong&gt;obligé d’en parler&lt;/strong&gt; !&lt;/p&gt;
&lt;h2 id=&quot;il-était-une-fois&quot;&gt;Il était une fois…&lt;/h2&gt;
&lt;p&gt;J’aime beaucoup le langage &lt;strong&gt;Typescript&lt;/strong&gt;. Quand j’ai un &lt;abbr title=&quot;Proof Of Concept&quot;&gt;POC&lt;/abbr&gt; ou un &lt;strong&gt;Kata&lt;/strong&gt; à réaliser, je me tourne souvent vers ce langage pour son côté très dynamique et son système de &lt;strong&gt;types&lt;/strong&gt; poussé (en attendant que je sois prêt à passer sur un langage totalement fonctionnel 😁).&lt;/p&gt;
&lt;p&gt;Seulement quand je l’utilise pour quelque chose de plus sérieux, notamment pour une application spécifique dans laquelle j’ai envie de conserver cette approche orientée métier, j’ai beaucoup plus de mal.&lt;/p&gt;
&lt;p&gt;Pourquoi donc me direz-vous ? Et bien tout simplement car les classes sont bien souvent délaissées au profit de structures de données plus simple (à base de &lt;code&gt;{}&lt;/code&gt;), en bien ou en mal, là n’est pas la question et qu’en se faisant, on perd le contrôle sur la cohérence de l’objet.&lt;/p&gt;
&lt;p&gt;Toujours est-il que partir dans le sens contraire peut vous poser bien des soucis, notamment lors de l’intégration avec d’autres librairies pour la persistance où ce genre de choses, vous amenant à tordre votre classe ou créer des représentations intermédiaires.&lt;/p&gt;
&lt;p&gt;Admettons que je décide de réaliser une application de gestion de tâches, on va faire &lt;em&gt;très très&lt;/em&gt; simple ici :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Task&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  done&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Dans la plupart des applications qu’on peut trouver en Typescript (sur Github principalement), peu de personnes se soucient des mutations de ce type &lt;code&gt;Task&lt;/code&gt; et il ne sera pas rare de voir un &lt;code&gt;task.done = true;&lt;/code&gt; directement dans un handler HTTP. Alors ça fonctionne, mais imaginez la même chose sur une application plus complexe et sur la &lt;strong&gt;maintenabilité&lt;/strong&gt; à plus ou moins long terme (sans parler du peu d’&lt;strong&gt;expressivité&lt;/strong&gt; de cette &lt;em&gt;modélisation&lt;/em&gt;)…&lt;/p&gt;
&lt;h2 id=&quot;le-pattern-decider&quot;&gt;Le pattern “Decider”&lt;/h2&gt;
&lt;p&gt;Ce pattern, découvert via la &lt;a href=&quot;https://www.youtube.com/watch?v=72TOhMpEVlA&quot;&gt;conférence&lt;/a&gt; de &lt;a href=&quot;https://mastodon.social/@thinkb4coding&quot;&gt;Jérémie Chassaing&lt;/a&gt;, va vous permettre de conserver cette approche centrée sur les &lt;strong&gt;données&lt;/strong&gt; mais en y ajoutant l’&lt;strong&gt;expressivité&lt;/strong&gt; d’un modèle &lt;strong&gt;orienté métier&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;définition&quot;&gt;Définition&lt;/h3&gt;
&lt;p&gt;Un &lt;strong&gt;Decider&lt;/strong&gt; possède 4 propriétés :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une fonction &lt;strong&gt;decide&lt;/strong&gt; : à partir d’une &lt;strong&gt;commande&lt;/strong&gt; et d’un &lt;strong&gt;état&lt;/strong&gt; détermine les &lt;strong&gt;événements&lt;/strong&gt; produits,&lt;/li&gt;
&lt;li&gt;une fonction &lt;strong&gt;evolve&lt;/strong&gt; : à partir d’un &lt;strong&gt;état&lt;/strong&gt; et d’une liste d’&lt;strong&gt;événements&lt;/strong&gt; (générés précédemment), génère un nouvel &lt;strong&gt;état&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;un &lt;strong&gt;initialState&lt;/strong&gt; qui permet de savoir d’où on part,&lt;/li&gt;
&lt;li&gt;une fonction &lt;strong&gt;isTerminal&lt;/strong&gt; : à partir d’un &lt;strong&gt;état&lt;/strong&gt;, détermine si l’état final est atteint, et que par conséquent, l’objet ne sera plus jamais modifié (permet par exemple de disposer l’objet).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Transposé en Typescript, on aura quelque chose dans ce genre là :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  decide&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  evolve&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  initialState&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  isTerminal&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; boolean&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Si on reprend notre exemple de gestion de tâches, on va alors pouvoir réfléchir en &lt;strong&gt;commandes&lt;/strong&gt;, &lt;strong&gt;états&lt;/strong&gt; et &lt;strong&gt;événements&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Ici j&apos;utilise un namespace mais on pourrait définir un fichier task.ts et exporter les membres.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; Task&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // L&apos;état en readonly, impossible de le modifier sans passer par le decider&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Readonly&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    done&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;boolean&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // Les différentes commandes possible sur notre tâche, la propriété `type` sert de discriminant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    | {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Create&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    | {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;ChangeTitle&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    | {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;MarkAsDone&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // Les événements, au passé, la propriété `type` en discriminant aussi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; =&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    | {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Created&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    | {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;TitleChanged&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    | {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;MarkedAsDone&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // Et enfin notre decider, qui prend les décisions métiers et lèvent les événements appropriés et s&apos;occupe de modifier l&apos;état&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;    decide&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;      match&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;        Create&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;          if&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;            throw&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Task already created&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;          return&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Created&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;        ChangeTitle&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;          if&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;            throw&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; Error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Task is done&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;          return&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;TitleChanged&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;        MarkAsDone&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; [] &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; [{ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;MarkedAsDone&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;    evolve&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;      match&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;        Created&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; ({ ...&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;        TitleChanged&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; ({ ...&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;        MarkedAsDone&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; ({ ...&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;      }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    initialState&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;    isTerminal&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; s&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;done&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Petits utilitaires pour éviter les switch un peu cracra et assurer l&apos;exhaustivité des cas&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; ExtractCallbacks&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Output&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  [&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;K&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Input&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;]]: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Extract&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;K&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Output&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Output&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;  cmd&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;  cases&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;ExtractCallbacks&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Output&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Output&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; cases&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; keyof&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; typeof&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; cases&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;](&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    cmd&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Extract&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Input&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;] }&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Évidemment lors de l’utilisation de ce type de pattern, les &lt;strong&gt;effets de bord&lt;/strong&gt; (persistance, appels externes, …) sont &lt;strong&gt;repoussés dans les couches plus hautes&lt;/strong&gt;, à la manière de mon &lt;a href=&quot;https://julien.leicher.me/writes/unicity-and-domain-model-purity&quot;&gt;article précédent&lt;/a&gt; et de l’approche &lt;a href=&quot;https://blog.mathieueveillard.com/functional-core-imperative-shell/&quot;&gt;Functional Core, Imperative Shell&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et qui dit &lt;strong&gt;fonctions pures&lt;/strong&gt;, dit &lt;strong&gt;testabilité&lt;/strong&gt; ! En effet, tester le &lt;strong&gt;Decider&lt;/strong&gt; est un vrai jeu d’enfant !&lt;/p&gt;
&lt;h3 id=&quot;utilisation&quot;&gt;Utilisation&lt;/h3&gt;
&lt;p&gt;Une fois que vous avez votre &lt;strong&gt;Decider&lt;/strong&gt;, votre code métier donc, écrit, vous n’y touchez plus ! Il ne nous manque q’un élément permettant d’orchestrer le tout et de choisir quoi faire de l’état et des événements.&lt;/p&gt;
&lt;p&gt;Par exemple, si on souhaite uniquement manipuler notre &lt;strong&gt;decider&lt;/strong&gt; en mémoire, on peut partir d’une fonction de ce genre :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; inMemory&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;  decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Event&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; State&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;initialState&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Command&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    const&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; events&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;decide&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;events&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;reduce&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;evolve&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; state&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cette fonction pourra s’appliquer sur n’importe quel type de &lt;strong&gt;decider&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; taskDeciderInMemory&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;inMemory&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Task&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;decider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Ici tout est typé ! Impossible de passer un `type` qui ne fait pas parti de l&apos;union `Task.Command`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;taskDeciderInMemory&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Create&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Acheter du lait&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  taskDeciderInMemory&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;ChangeTitle&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    title&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Acheter du lait, et des œufs !&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;console&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;taskDeciderInMemory&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;MarkAsDone&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; }));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// { title: &quot;Acheter du lait&quot;, done: false }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// { title: &quot;Acheter du lait, et des œufs !&quot;, done: false }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// { title: &quot;Acheter du lait, et des œufs !&quot;, done: true }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Envie de persister l’état ailleurs ? De lever les événements dans des &lt;strong&gt;Process&lt;/strong&gt; (cf. la conférence) ? Il suffit d’écrire une alternative à la fonction &lt;code&gt;inMemory&lt;/code&gt; ! C’est aussi simple que ça.&lt;/p&gt;
&lt;p&gt;Cette approche est d’ailleurs extrêmement pratique si vous pratiquez l’&lt;strong&gt;Event Sourcing&lt;/strong&gt;, il suffit en effet de persister les événements plutôt que l’état et de rappeler la fonction &lt;code&gt;evolve&lt;/code&gt; avec le flux d’événements pour réhydrater votre agrégat !&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;J’ai laissé de côté certains aspects abordés dans &lt;a href=&quot;https://www.youtube.com/watch?v=72TOhMpEVlA&quot;&gt;la conférence&lt;/a&gt; et &lt;a href=&quot;https://thinkbeforecoding.com/post/2021/12/17/functional-event-sourcing-decider&quot;&gt;l’article associé&lt;/a&gt;, notamment la &lt;strong&gt;composition&lt;/strong&gt; de plusieurs &lt;strong&gt;deciders&lt;/strong&gt; pour la coordination mais les &lt;strong&gt;possibilités sont folles&lt;/strong&gt; !&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>Représenter l&apos;unicité dans un domaine pur</title><link>https://julien.leicher.me/writes/unicity-and-domain-model-purity/</link><guid isPermaLink="true">https://julien.leicher.me/writes/unicity-and-domain-model-purity/</guid><description>Modéliser une solution à l’aide d’un domaine pur est à première vue simple à mettre en œuvre jusqu’au moment où il faut matérialiser des règles métier basées sur une collection d’entités, comme par exemple l’unicité d’une propriété.</description><pubDate>Tue, 20 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;quest-ce-quun-modèle-de-domaine-pur&quot;&gt;Qu’est-ce qu’un modèle de domaine pur ?&lt;/h2&gt;
&lt;p&gt;Si on reprend la définition d’une &lt;strong&gt;fonction pure&lt;/strong&gt; d’après &lt;a href=&quot;https://fr.wikipedia.org/wiki/Fonction_pure&quot;&gt;Wikipédia&lt;/a&gt; :&lt;/p&gt;
&lt;p&gt;En programmation informatique, une fonction pure est une fonction qui
possède les propriétés suivantes :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sa valeur de retour est la même pour les mêmes arguments (pas de variation avec des variables statiques locales, des variables non locales, des arguments mutables de type référence ou des flux d’entrée).&lt;/li&gt;
&lt;li&gt;Son évaluation n’a pas d’effets de bord (pas de mutation de variables
statiques locales, de variables non locales, d’arguments mutables de type
référence ou de flux d’entrée-sortie).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour faire simple, une &lt;strong&gt;fonction pure retourne toujours le même résultat si on lui passe les mêmes arguments&lt;/strong&gt; et &lt;strong&gt;ne va pas modifier un état externe quelconque&lt;/strong&gt; (ce qui inclut l’écriture sur un volume ou une communication réseau).&lt;/p&gt;
&lt;p&gt;Dans le cas du &lt;abbr title=&quot;Domain Driven Design&quot;&gt;DDD&lt;/abbr&gt;, lorsqu’on modélise notre solution, c’est à dire qu’on crée nos entités, nos objets-valeurs, etc…, la &lt;strong&gt;pureté du modèle consiste principalement à ne pas appeler de services externes&lt;/strong&gt; depuis ces objets.&lt;/p&gt;
&lt;p&gt;Prenons un cas concret qui devrait parler à tout le monde et qui ne respecte pas cette propriété :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;go&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; auth&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; &quot;errors&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; ErrEmailShouldBeUnique&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; errors&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;New&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;l&apos;email doit être unique!&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; User&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;    // ... Et plein d&apos;autres champs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Abstraction de la couche de persistance&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; UserRepository&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; interface&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;    IsEmailUnique&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; // Je simplifie et j&apos;omet volontairement le retour des erreurs ici&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;    // ... Et d&apos;autres méthodes pour persister un User, le récupérer, etc...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; Register&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;repo&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; UserRepository&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;repo&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;IsEmailUnique&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;ErrEmailShouldBeUnique&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }, &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Dans cet exemple, je souhaite faire apparaître la règle métier qui est : &lt;em&gt;il ne peut y’avoir qu’un seul utilisateur par adresse mail&lt;/em&gt;. Comme il s’agit d’une règle métier, mon souhait est bel et bien de la représenter &lt;strong&gt;dans mon domaine&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Pour se faire, je demande donc en paramètre le &lt;code&gt;UserRepository&lt;/code&gt; qui me permettra d’aller m’assurer que la condition est validée, dans le cas contraire, je retourne une erreur.&lt;/p&gt;
&lt;p&gt;En introduisant l’appel à ce service, je &lt;strong&gt;casse la pureté du domaine&lt;/strong&gt; car un &lt;strong&gt;effet de bord&lt;/strong&gt; (communiquer avec la base de données ici) a été ajouté.&lt;/p&gt;
&lt;p&gt;Si je dois tester la fonction &lt;code&gt;Register&lt;/code&gt;, il me faudra alors implémenter un &lt;em&gt;test double&lt;/em&gt; pour le &lt;code&gt;UserRepository&lt;/code&gt; afin de valider les deux sorties possibles.&lt;/p&gt;
&lt;h2 id=&quot;le-trilemme-ddd&quot;&gt;Le trilemme DDD&lt;/h2&gt;
&lt;p&gt;Cela dit, ce n’est pas forcément une mauvaise chose et &lt;strong&gt;cette solution peut tout à fait être viable&lt;/strong&gt; !&lt;/p&gt;
&lt;p&gt;Comme l’explique &lt;a href=&quot;https://enterprisecraftsmanship.com/posts/domain-model-purity-completeness/&quot;&gt;Vladimir Khorikov&lt;/a&gt;, il s’agit en fait d’un trilemme lorsqu’on modélise avec le DDD.&lt;/p&gt;
&lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://julien.leicher.me/_astro/ddd_trilemma.DsdM_bhA_2s3WMm.webp&quot; alt=&quot;Le trilemme DDD&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;841&quot; height=&quot;398&quot;&gt;&lt;/div&gt;
&lt;p&gt;Ce trilemme met en avant 3 choix qui s’offrent à vous quand vous tentez d’utiliser le DDD pour modéliser votre solution :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain model completeness&lt;/strong&gt; : l’exhaustivité du modèle, absolument toutes les règles se trouvent dans la couche métier,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Domain model purity&lt;/strong&gt; : le modèle de domaine est dit pur, aucune dépendance vers des services externes (&lt;em&gt;out-of-process&lt;/em&gt;) dans le domaine, les appels à effets de bord sont repoussés dans la couche applicative,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt; : il faut tout de même considérer les performances de notre modèle ce qui implique d’autres choix architecturaux.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour revenir sur ce dernier point, on pourrait effectivement charger toute la liste des utilisateurs de notre système, la passer en paramètre de la méthode &lt;code&gt;Register&lt;/code&gt; et s’assurer de l’unicité de l’email. De cette manière, on écarte l’effet de bord et on rend notre fonction pure. Cela dit, on comprend très vite que d’un point de vue performance, ce serait absolument catastrophique !&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;go&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Aïe ! Imaginer avec des milliers d&apos;utilisateurs !&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; Register&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;allUsers&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; []&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; _&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; :=&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; range&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; allUsers&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; user&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; == &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;ErrEmailShouldBeUnique&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }, &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ce trilemme nous dit que &lt;strong&gt;parmi ces 3 choix&lt;/strong&gt;, on ne peut &lt;strong&gt;en choisir que 2&lt;/strong&gt; et qu’il va donc falloir &lt;strong&gt;faire des compromis&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pousser les lectures/écritures externes en périphérie du domaine (donc plutôt dans la couche applicative), par exemple charger tout en mémoire et le passer au domaine pour valider les règles : &lt;strong&gt;exhaustivité et pureté&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;Injecter des dépendances de services dans le domaine : &lt;strong&gt;exhaustivité et performance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Diviser la prise de décision entre la couche applicative et le domaine : &lt;strong&gt;pureté et performance&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et à partir de là, c’est au final &lt;strong&gt;à vous de choisir&lt;/strong&gt; ce qui est le mieux selon votre pratique. Selon l’auteur, il est préférable de &lt;strong&gt;favoriser la pureté&lt;/strong&gt; au détriment de l’exhaustivité (opter alors pour la troisième approche) et de mon côté, je partage plutôt cet avis. Partir sur un modèle plus simple est toujours une bonne chose et vous permet de &lt;strong&gt;tester aisément des cas complexes&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;pureté-et-performance-comment-retrouver-un-peu-dexhaustivité&quot;&gt;Pureté et performance, comment retrouver un peu d’exhaustivité ?&lt;/h2&gt;
&lt;p&gt;Si j’écris cet article c’est justement car j’ai été confronté à ce choix sur &lt;a href=&quot;https://github.com/YuukanOO/seelf&quot;&gt;seelf&lt;/a&gt;. Depuis le début, j’avais choisi de partir sur un domaine aussi pur que possible pour faciliter la testabilité de l’application.&lt;/p&gt;
&lt;p&gt;Seulement, &lt;strong&gt;mettre de côté l’exhaustivité du modèle ne me satisfaisait pas vraiment&lt;/strong&gt;. En lisant les commentaires de l’article cité plus haut, on s’aperçoit qu’on peut &lt;strong&gt;rétablir un peu d’exhaustivité&lt;/strong&gt; y compris en optant pour un modèle pur. Par exemple, si je reprends le code précédent :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;go&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; Register&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;unique&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; bool&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;unique&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;ErrEmailShouldBeUnique&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }, &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;En introduisant le booléen &lt;code&gt;unique&lt;/code&gt;, on a repoussé la récupération de cette information dans la couche applicative (via la méthode &lt;code&gt;UserRepository.IsEmailUnique&lt;/code&gt;) pour &lt;strong&gt;préserver un modèle pur et simple&lt;/strong&gt; dans notre couche métier. De cette manière, on rétablit une partie de l’exhaustivité qu’on a concédé via notre choix.&lt;/p&gt;
&lt;p&gt;On peut aller encore un peu plus loin en définissant un nouveau type qui éliminera les ambiguïtés restantes :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;go&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; EmailAvailability&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; bool&lt;/span&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; // On définit un nouveau type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; UserRepository&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; interface&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;    GetEmailAvailability&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;EmailAvailability&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; Register&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;available&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; EmailAvailability&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;available&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;ErrEmailShouldBeUnique&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;        email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;email&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;    }, &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;De cette manière, à la lecture de la signature de la méthode, on comprend très vite ce qui se passe. Les &lt;strong&gt;tests restent extrêmement simples&lt;/strong&gt; car il suffit de passer &lt;code&gt;true&lt;/code&gt; ou &lt;code&gt;false&lt;/code&gt; en paramètre et de vérifier les retours. Côté performance, on ne perd rien car c’est l’implémentation du &lt;code&gt;UserRepository&lt;/code&gt; qui aura la charge de vérifier l’unicité (via un &lt;code&gt;COUNT&lt;/code&gt; si SQL par exemple) et niveau exhaustivité, on &lt;strong&gt;conserve une part non négligeable&lt;/strong&gt; de l’intention originale.&lt;/p&gt;
&lt;p&gt;Parfois, les choses sont encore plus compliquées et la règle métier repose sur plusieurs informations que seule la collection d’entités possède.&lt;/p&gt;
&lt;p&gt;Dans ce cas, on peut avoir recours à des types plus complexes qu’un simple booléen. Par exemple sur seelf, dans la prochaine version, on pourra déployer des applications sur des instances Docker distantes. L’unicité du nom d’une application est donc fonction du nom de l’application et des différentes cibles configurées. Pour représenter cette complexité, j’ai décidé d’utiliser un &lt;em&gt;bitmask&lt;/em&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;go&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; AppNamingAvailability&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; uint8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;	AppNamingProductionTargetNotFound&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; AppNamingAvailability&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; &amp;#x3C;&amp;#x3C; &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;iota&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;	AppNamingStagingTargetNotFound&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;	AppNamingTakenInProduction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;	AppNamingTakenInStaging&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;	AppNamingAvailable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; AppsReader&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; interface&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;    GetAppNamingAvailability&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;context&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Context&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;AppName&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TargetID&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TargetID&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;AppNamingAvailability&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; NewApp&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;	name&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; AppName&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;	production&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; EnvironmentConfig&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;	staging&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; EnvironmentConfig&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;	createdBy&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; domain&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;UserID&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;	available&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; AppNamingAvailability&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;err&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; error&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;	if&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; available&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; != &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;AppNamingAvailable&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; app&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;ErrInvalidAppNaming&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Dans la couche applicative, j&apos;utilise `AppsReader.GetAppNamingAvailability`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// et je peux alors retourner une erreur précise à l&apos;utilisateur suivant les&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// valeurs du masque.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Grâce à ce &lt;em&gt;bitmask&lt;/em&gt;, j’ai la possibilité de savoir pour quelle raison le nom n’est pas disponible ce qui me permet de retourner une erreur plus précise à l’utilisateur au niveau de la couche applicative.&lt;/p&gt;
&lt;p&gt;Cette solution n’est évidemment pas parfaite mais permet tout de même de &lt;strong&gt;limiter les concessions&lt;/strong&gt; sur l’exhaustivité du modèle alors pensez-y 😉 !&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>SvelteKit, seelf et localisation</title><link>https://julien.leicher.me/writes/seelf-svelte-localization/</link><guid isPermaLink="true">https://julien.leicher.me/writes/seelf-svelte-localization/</guid><description>La version 1.2.0 de seelf marque l’arrivée de la localisation de l’application en anglais et en français. Petit retour sur le choix technique que j’ai retenu.</description><pubDate>Wed, 10 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;le-cahier-des-charges&quot;&gt;Le cahier des charges&lt;/h2&gt;
&lt;p&gt;Première chose à faire, &lt;strong&gt;définir le besoin&lt;/strong&gt;. Pour la localisation de seelf, mon cahier des charges ressemblait donc à ceci :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pouvoir localiser des messages, des dates, des durées et des nombres&lt;/li&gt;
&lt;li&gt;Pouvoir ajouter facilement une nouvelle langue&lt;/li&gt;
&lt;li&gt;Ne pas pouvoir utiliser une clé de traduction non définie&lt;/li&gt;
&lt;li&gt;Pouvoir paramétrer les traductions lorsque nécessaire&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Un besoin au final assez classique quand on parle d’internationalisation. Cela dit, pour ma part, l’&lt;strong&gt;un des points essentiels&lt;/strong&gt; était que le process de &lt;strong&gt;build échoue dans le cas de l’utilisation d’une traduction manquante ou mal paramétrée&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;les-librairies-existantes-et-leurs-limites&quot;&gt;Les librairies existantes et leurs limites&lt;/h2&gt;
&lt;p&gt;La partie frontend de seelf est réalisée avec l’aide du framework &lt;a href=&quot;https://kit.svelte.dev/&quot;&gt;SvelteKit&lt;/a&gt; en utilisant le langage &lt;strong&gt;Typescript&lt;/strong&gt;. La suite logique était donc de &lt;strong&gt;regarder les librairies existantes&lt;/strong&gt; en terme de localisation avec ce framework.&lt;/p&gt;
&lt;p&gt;Un petit tour sur un moteur de recherche me permet de mettre en avant un certain nombre de librairies :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kaisermann/svelte-i18n&quot;&gt;&lt;code&gt;svelte-i18n&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sveltekit-i18n/lib&quot;&gt;&lt;code&gt;sveltekit-i18n&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://poly-i18n.vercel.app/en/kitbook&quot;&gt;&lt;code&gt;poly-i18n&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Seulement, aucune ne parvient à me convaincre suffisamment. Pour la majorité de ces librairies, l’interpolation (donc le paramétrage des traductions) me semble &lt;a href=&quot;https://github.com/kaisermann/svelte-i18n/blob/main/docs/Formatting.md#format-_-or-t&quot;&gt;bien&lt;/a&gt; &lt;a href=&quot;https://github.com/sveltekit-i18n/parsers/tree/master/parser-default#placeholders&quot;&gt;trop&lt;/a&gt; &lt;a href=&quot;https://poly-i18n.vercel.app/en/kitbook/docs/1-formatting&quot;&gt;complexe&lt;/a&gt;. Par exemple, avec &lt;code&gt;sveltekit-i18n&lt;/code&gt;, vous pouvez avoir des chaînes de traduction de ce style :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  &quot;placeholder&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Title with {{placeholder}}.&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  &quot;placeholder_with_default_value&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;{{placeholder; default:Default value;}}.&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  &quot;modifier&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;{{gender; female:She; male:He;}} has a dog.&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  &quot;combined&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;You have {{number:gt; 0:{{number}} new {{number; 1:message; default:messages;}}!; default:no messages.;}}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Et même si je comprends l’utilité de cette syntaxe dans le cadre d’un fichier de traduction en &lt;code&gt;JSON&lt;/code&gt;, cela restait trop compliqué pour mes besoins. Notons aussi qu’&lt;strong&gt;aucun typage n’est disponible&lt;/strong&gt; avec ce genre de système et que donc, on peut se retrouver à oublier un argument nécessaire à la traduction ou à fournir une valeur du mauvais type.&lt;/p&gt;
&lt;p&gt;Autre point intéressant à mentionner, chacune de ces solutions retourne la valeur de traduction sous forme de &lt;a href=&quot;https://svelte.dev/docs/svelte-store&quot;&gt;store svelte&lt;/a&gt; ce qui permet de &lt;strong&gt;changer à la volée et de manière réactive&lt;/strong&gt; la langue de l’application. Même si &lt;strong&gt;je ne remet pas en cause la performance&lt;/strong&gt; des stores tels qu’implémentés par Svelte, je trouvais dommage d’&lt;strong&gt;en payer le prix&lt;/strong&gt; sachant que &lt;strong&gt;la langue n’est que très rarement changée&lt;/strong&gt; au sein de l’application. Rafraîchir la page lorsque cela arrive me semblait tout à fait acceptable.&lt;/p&gt;
&lt;h2 id=&quot;la-solution-retenue&quot;&gt;La solution retenue&lt;/h2&gt;
&lt;p&gt;Vous l’aurez sans doute compris, au final, j’ai préféré partir sur une solution &lt;em&gt;sur-mesure&lt;/em&gt; adaptée parfaitement à mes besoins.&lt;/p&gt;
&lt;div class=&quot;panel note&quot;&gt;&lt;p&gt;J’ai simplifié volontairement les exemples ci-dessous mais vous pouvez retrouver le code complet &lt;a href=&quot;https://github.com/YuukanOO/seelf/blob/main/cmd/serve/front/src/lib/localization/index.ts&quot;&gt;dans le dépôt Github de seelf&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;h3 id=&quot;le-typage-des-traductions&quot;&gt;Le typage des traductions&lt;/h3&gt;
&lt;p&gt;Comme je le disais au début, le point essentiel était le &lt;strong&gt;typage des traductions&lt;/strong&gt;. Si je traduis (&lt;em&gt;Haha !&lt;/em&gt;) ce besoin en &lt;strong&gt;Typescript&lt;/strong&gt;, cela signifie qu’un dictionnaire de traductions est défini comme suit :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * Les traductions de l&apos;application sont soit des chaînes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * brutes, soit des fonctions prenant un nombre variable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * de paramètres et retournant une chaîne.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Record&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; | ((...&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Et oui, comme l’application utilise déjà &lt;strong&gt;Typescript&lt;/strong&gt;, autant en profiter pour s’appuyer dessus et définir les traductions de l’application dans des fichiers &lt;code&gt;.ts&lt;/code&gt; !&lt;/p&gt;
&lt;h3 id=&quot;le-formatage-des-dates-et-nombres&quot;&gt;Le formatage des dates et nombres&lt;/h3&gt;
&lt;p&gt;Autre point important, &lt;strong&gt;pouvoir localiser des dates et des nombres&lt;/strong&gt;. Pour se faire, notre service de localisation devra &lt;strong&gt;implémenter une petite interface&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; DateValue&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; FormatProvider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  date&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;DateValue&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  datetime&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;value&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;DateValue&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  duration&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;start&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;DateValue&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;end&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;DateValue&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Et comme parfois une traduction paramétrée nécessite de formater le même genre de données, on peut venir compléter notre définition de &lt;code&gt;Translations&lt;/code&gt; pour que le &lt;code&gt;this&lt;/code&gt; de la fonction soit au final un &lt;code&gt;FormatProvider&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * Désormais si je défini une traduction comme nécessitant un ou plusieurs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * paramètres, je pourrai utiliser `this.date` ou `this.datetime` pour localiser&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * des données.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; TranslationFunc&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = (&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF;font-style:italic&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;FormatProvider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, ...&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[]) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Record&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; | &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TranslationFunc&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;les-langues-ou-locales&quot;&gt;Les langues (ou locales)&lt;/h3&gt;
&lt;p&gt;Dernier point du cahier des charges, pouvoir &lt;strong&gt;ajouter facilement une nouvelle langue&lt;/strong&gt;. De la même manière que pour les traductions, les langues sont donc définies par un type :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Locale&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  code&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  displayName&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lors de la définition de la langue par défaut (l’anglais sur seelf), j’ai donc un fichier &lt;code&gt;en.ts&lt;/code&gt; qui ressemble grosso modo à ça :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // (...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;  &quot;auth.signin.title&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Sign in&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;  &quot;auth.signin.description&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;    &quot;Please fill the form below to access your dashboard.&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;  &quot;deployment.details_tooltip&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;deployNumber&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;    `View deployment #&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;deployNumber&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; details and logs`&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // (...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;satisfies&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  code&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  displayName&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;English&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; satisfies&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Locale&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;panel note&quot;&gt;&lt;p&gt;Le mot-clé &lt;code&gt;satisfies&lt;/code&gt; apparu &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#the-satisfies-operator&quot;&gt;dans la version 4.9 de Typescript&lt;/a&gt; me permet de m’assurer que les traductions respectent les valeurs possibles (chaîne ou fonction) sans pour autant modifier le type de &lt;code&gt;translations&lt;/code&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Quand je défini une nouvelle langue, il me suffit donc de m’assurer que les clés de traductions respectent le type défini par &lt;code&gt;typeof translations&lt;/code&gt; dans le fichier &lt;code&gt;en.ts&lt;/code&gt;. Par exemple pour le français, on a le fichier &lt;code&gt;fr.ts&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * Représente les clés de traduction propres à l&apos;application,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * `en` étant l&apos;import du fichier `en.ts`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; AppTranslations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; = (&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;typeof&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; en&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)[&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;translations&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  code&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;fr&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  displayName&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Français&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;    // (...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;    &quot;auth.signin.title&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Connexion&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;    &quot;auth.signin.description&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;      &quot;Remplissez le formulaire ci-dessous pour accéder au tableau de bord.&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;    &quot;deployment.details_tooltip&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: (&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;deployNumber&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;      `Voir les détails et logs du déploiement #&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;deployNumber&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;    // (...)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; satisfies&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Locale&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;AppTranslations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Grâce à tous ces &lt;strong&gt;types&lt;/strong&gt;, je m’assure que les langues définies possèdent toutes les clés ainsi que les mêmes paramètres que la langue par défaut. Impossible donc d’oublier de traduire une clé ou de se tromper dans les types de paramètres.&lt;/p&gt;
&lt;h3 id=&quot;le-service-de-localisation&quot;&gt;Le service de localisation&lt;/h3&gt;
&lt;p&gt;Dernière étape et non des moindres, utiliser tout ce qu’on a vu précédemment et &lt;strong&gt;venir traduire toute l’interface&lt;/strong&gt;. Pour se faire, j’ai donc un service de localisation respectant encore une fois &lt;strong&gt;une petite interface&lt;/strong&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Extrait toutes les clés d&apos;un certain type&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; KeysOfType&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;O&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; = {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  [&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;K&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; keyof&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; O&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;]: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;O&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;K&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; T&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; K&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; never&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}[&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;keyof&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; O&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Extrait le type des arguments d&apos;une fonction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; TranslationsArgs&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; = &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; (...&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;args&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: infer &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;P&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; P&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; never&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;// Le service de localisation (simplifié pour l&apos;article ici)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; LocalizationService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; FormatProvider&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  translate&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TKey&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; KeysOfType&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TKey&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  translate&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TKey&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; KeysOfType&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TranslationFunc&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;    key&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TKey&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;    args&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TranslationsArgs&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;TKey&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;]&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  ): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; LocalizationServiceImplementation&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Translations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  implements&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; LocalizationService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /** Implémentation du service en utilisant les services fournis dans Intl ;) */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * Plus loin, une implémentation de LocalizationService&amp;#x3C;AppTranslations&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * est exportée et donc disponible au sein de l&apos;application avec quelque chose&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; * dans ce genre.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; LocalizationServiceImplementation&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;AppTranslations&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Et c’est là que la magie opère. Grâce au type &lt;code&gt;KeysOfType&amp;#x3C;&gt;&lt;/code&gt;, je m’assure que si la &lt;strong&gt;traduction est une fonction&lt;/strong&gt; (&lt;code&gt;KeysOfType&amp;#x3C;T, TranslationFunc&gt;&lt;/code&gt;) alors des &lt;strong&gt;paramètres doivent être fournis et posséder le bon type&lt;/strong&gt;, et grâce au &lt;code&gt;T extends Translations&lt;/code&gt;, le premier argument de la fonction &lt;code&gt;translate&lt;/code&gt; &lt;strong&gt;doit être une clé valide&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Côté page ou composant, il nous suffit alors d’importer le service qui implémente cette interface et d’utiliser la fonction &lt;code&gt;translate&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;svelte&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  import&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; l&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; &apos;$lib/localization&apos;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;translate&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&apos;auth.signin.title&apos;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;translate&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&apos;deployment.details_tooltip&apos;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, [&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;42&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Petit bonus, la plupart du temps dans des applications multi-langues, il est difficile de savoir si une propriété d’un composant doit être une clé de traduction ou bien une chaîne déjà localisée. Avec l’approche exposée ici, j’ai un type &lt;code&gt;AppTranslationsString&lt;/code&gt; qui me permet de typer certaines propriétés pour rendre le tout explicite :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;svelte&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; lang&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  // Où AppTranslationsString = KeysOfType&amp;#x3C;typeof en[&apos;translations&apos;], string&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  import&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; l&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, { &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; AppTranslationsString&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; &apos;$lib/localization&apos;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; let&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; label&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;AppTranslationsString&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;click&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;translate&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;label&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Alors bien sûr &lt;strong&gt;tout n’est pas encore parfait&lt;/strong&gt;. J’aimerai par exemple avoir un chargement des langues uniquement si elles sont utilisées par l’utilisateur pour éviter de charger des traductions inutiles.&lt;/p&gt;
&lt;p&gt;Cela dit, je voulais &lt;strong&gt;partager&lt;/strong&gt; avec vous cette petite solution qui me semble élégante et un peu à contre-courant.&lt;/p&gt;
&lt;p&gt;Cette solution sur-mesure m’a effectivement évité d’avoir à faire des concessions sur mon cahier des charges en prenant une solution existante qui aurait été perfectible.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item><item><title>Analyse statique et génération de code</title><link>https://julien.leicher.me/writes/static-analysis-ftw/</link><guid isPermaLink="true">https://julien.leicher.me/writes/static-analysis-ftw/</guid><description>Prenons de la hauteur et imaginons à quoi pourrait ressembler le développement d’applications si nous n’avions plus qu’à nous préoccuper du code métier.</description><pubDate>Mon, 20 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;Il y a quelques semaines, poussé par ma curiosité, j’ai assisté à un live d’&lt;a href=&quot;https://alexsoyes.com/&quot;&gt;Alexandre Soyer&lt;/a&gt; intitulé &lt;strong&gt;Coder avec l’IA - Comment gagner 4h par jour avec L’IA&lt;/strong&gt;, un titre accrocheur s’il en est ! En tant que développeur·se, force est d’admettre que l’IA est effectivement un enjeu important et qu’il vaut mieux s’y &lt;strong&gt;préparer&lt;/strong&gt; et s’&lt;strong&gt;adapter&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;J’utilise d’ailleurs depuis quelques mois &lt;a href=&quot;https://github.com/features/copilot&quot;&gt;Github Copilot&lt;/a&gt; et il faut bien avouer qu’il me fait gagner un sacré paquet de temps (même si le flou autour des données collectées reste questionnable).&lt;/p&gt;
&lt;p&gt;Bref, revenons à nos moutons. Alex a avancé pas mal de points sur le métier de développeur·se et le code que nous produisons et à quel point &lt;strong&gt;écrire une grosse partie de notre code est un jeu d’enfant pour une IA&lt;/strong&gt;. Après tout il ne s’agit que de texte avec un vocabulaire restreint.&lt;/p&gt;
&lt;p&gt;Malgré tout, je pense que nous sommes tous d’accord pour admettre qu’&lt;strong&gt;écrire du code ne représente qu’une fraction&lt;/strong&gt; de notre métier. On passe tout autant de temps, voir plus, à &lt;strong&gt;comprendre&lt;/strong&gt; le problème, &lt;strong&gt;élaborer&lt;/strong&gt; une solution et &lt;strong&gt;anticiper&lt;/strong&gt; les erreurs. Je partage l’avis d’Alex sur le fait que l’IA ne sera qu’un outil de plus pour optimiser une partie de notre travail et non pas le remplacer.&lt;/p&gt;
&lt;p&gt;En revanche, il y’a un point qui me fait réfléchir et qui rejoint une réflexion que j’ai depuis quelques temps :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On fait souvent des trucs pas compliqué, mais complexe&lt;/p&gt;
&lt;footer&gt;&lt;cite&gt;— Alexandre Soyer&lt;/cite&gt;&lt;/footer&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;le-constat&quot;&gt;Le constat&lt;/h2&gt;
&lt;p&gt;Je ressens la même chose, comme il le dit « On fait souvent les mêmes trucs, projet après projet », « 50% de notre temps, c’est : CRUD, formulaires, requêtes HTTP, tableaux, API GET, POST », etc…&lt;/p&gt;
&lt;p&gt;Ce qui reste paradoxal car en tant que développeur·se, nous sommes extrêmement friands de tout ce qui nous évite de nous répéter et nous fait gagner du temps. Nous aimons la simplicité, surtout quand il s’agit de &lt;strong&gt;glue code&lt;/strong&gt;, du code qui est &lt;strong&gt;nécessaire&lt;/strong&gt; mais dont on préférerait se passer et encore plus &lt;strong&gt;ne pas avoir à maintenir&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Du coup, une fois un problème considéré comme résolu, la solution devrait pouvoir être intégrée sans aucun effort. Gérer l’authentification, la pagination, l’affichage de tableaux, la validation des formulaires, l’exposition et la documentation d’une API REST, etc…, toutes ces choses qu’on retrouve de manière systématique.&lt;/p&gt;
&lt;p&gt;Et pourtant, en &lt;strong&gt;2023&lt;/strong&gt;, on est encore loin d’avoir atteint quelque chose de satisfaisant.&lt;/p&gt;
&lt;p&gt;Alors bien sûr, il existe des frameworks à la pelle, des librairies, des approches, des patterns mais commencer un nouveau projet reste quelque chose de laborieux et ce constat peut être fait côté back ou front, peu importe.&lt;/p&gt;
&lt;h2 id=&quot;ia-pas-de-soucis&quot;&gt;IA pas de soucis&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Badum tssss&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Du coup forcément, &lt;strong&gt;2023&lt;/strong&gt; oblige, nous avons enfin la solution à tous nos maux : l’&lt;strong&gt;IA&lt;/strong&gt; ! Avant ça nous avions des outils de &lt;strong&gt;scaffolding&lt;/strong&gt; mais ce n’était &lt;strong&gt;pas assez disruptif&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;L’IA va donc se charger d’écrire ce code laborieux pour nous, en voilà une bonne idée ! De cette manière, nous pourrons nous &lt;strong&gt;concentrer sur le code métier&lt;/strong&gt;, propre à notre problème, &lt;strong&gt;augmenter notre vélocité&lt;/strong&gt; et proposer &lt;strong&gt;plus de valeur&lt;/strong&gt; à nos utilisateurs (vous n’avez pas une impression de déjà vu ?) !&lt;/p&gt;
&lt;h2 id=&quot;mais-ne-peut-on-pas-aller-encore-plus-loin&quot;&gt;Mais ne peut-on pas aller encore plus loin ?&lt;/h2&gt;
&lt;p&gt;Personnellement, je pense que concernant ce code si trivial, ce code &lt;strong&gt;nécessaire&lt;/strong&gt; mais &lt;strong&gt;sans grande valeur fonctionnelle&lt;/strong&gt;, nous pouvons faire mieux. De mon point de vue, on pourrait même &lt;strong&gt;s’en passer&lt;/strong&gt; ou en tout cas le &lt;strong&gt;rendre invisible&lt;/strong&gt; pour ne plus s’en préoccuper.&lt;/p&gt;
&lt;p&gt;Prenons un exemple simple que je trouve assez parlant, mais gardez en tête que le principe s’applique quelque soit le langage. Nous avons donc ci-dessous un &lt;a href=&quot;https://github.com/nestjs/nest/tree/master/sample/11-swagger&quot;&gt;code classique&lt;/a&gt; tiré de l’exemple Swagger de &lt;a href=&quot;https://nestjs.com/&quot;&gt;NestJS&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Injectable&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Body&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Controller&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Get&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Post&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; &quot;@nestjs/common&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  ApiBearerAuth&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  ApiOperation&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  ApiResponse&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  ApiTags&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  ApiProperty&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; &quot;@nestjs/swagger&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;IsInt&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;IsString&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt; &quot;class-validator&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * The name of the Cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * &lt;/span&gt;&lt;span style=&quot;color:#54B9FF;font-style:italic&quot;&gt;@example&lt;/span&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; Kitty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiProperty&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;example&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;description&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;The age of the Cat&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  age&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiProperty&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    example&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Maine Coon&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;The breed of the Cat&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  breed&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; CreateCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;IsString&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;IsInt&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; age&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;IsString&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; breed&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;Injectable&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; CatsService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  private&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; cats&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[] = [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  create&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;CreateCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cats&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  findOne&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cats&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiBearerAuth&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiTags&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;cats&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;Controller&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;cats&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; CatsController&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  constructor&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;private&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt; catsService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;CatsService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) {}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;Post&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiOperation&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;summary&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Create cat&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiResponse&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;status&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;403&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;description&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;Forbidden.&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  async&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt; create&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;Body&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;createCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;CreateCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Promise&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;catsService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;createCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;Get&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;:id&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  @&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;ApiResponse&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    status&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    description&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;The found record&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;    type&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  findOne&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(@&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;Param&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFD493&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;catsService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;findOne&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(+&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On a là quelque chose d’&lt;strong&gt;ultra commode&lt;/strong&gt;. Un modèle, un service, un contrôleur. Le service nous retourne un DTO lors de la lecture et on expose tout ça via une API REST. Je vous passe toute la partie configuration du conteneur d’injection de dépendances.&lt;/p&gt;
&lt;p&gt;D’ores et déjà, on peut remarquer des choses assez étonnantes : &lt;code&gt;findOne(@Param(&quot;id&quot;) id: string)&lt;/code&gt;, &lt;code&gt;@IsString() readonly breed: string;&lt;/code&gt;, même si on comprend pourquoi ces annotations sont nécessaires, notre code est noyé sous tout un tas de considérations d’infrastructure, qui &lt;strong&gt;répète souvent des choses qu’on peut déduire des types&lt;/strong&gt; eux-mêmes.&lt;/p&gt;
&lt;p&gt;Si on prend de la hauteur, ce que nous voulons ici, en tant que développeur·se web, c’est bien &lt;strong&gt;exposer des cas d’utilisations métier&lt;/strong&gt; et les rendre accessibles depuis l’extérieur, ici sous forme d’API REST mais cela pourrait prendre une autre forme, ça n’a pas d’importance.&lt;/p&gt;
&lt;p&gt;Au final, ne serait-il pas plus logique d’avoir quelque chose dans ce genre :&lt;/p&gt;
&lt;div class=&quot;codeblock&quot;&gt;&lt;pre class=&quot;astro-code houston&quot; style=&quot;background-color:#17191e;color:#eef0f9;overflow-x:auto&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * The name of the Cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * &lt;/span&gt;&lt;span style=&quot;color:#54B9FF;font-style:italic&quot;&gt;@example&lt;/span&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt; Kitty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * The age of the Cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  age&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * The breed of the Cat&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;  breed&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; CreateCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; age&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; breed&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; CatsService&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;  private&lt;/span&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt; readonly&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; cats&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[] = [];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * Create cat.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  create&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;CreateCatDto&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;    this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cats&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;push&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt; cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;  /**&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   * Find a cat by its id.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F98F;font-style:italic&quot;&gt;   */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00DAEF&quot;&gt;  findOne&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8;font-style:italic&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;number&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;): &lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt;Cat&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#54B9FF&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;color:#ACAFFF&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;cats&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:#4BF3C8&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#EEF0F9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nous pourrions nous contenter d’écrire ce code strictement nécessaire à l’accomplissement de notre métier et ensuite, s’appuyer sur un générateur pour produire la partie manquante en se basant à la fois sur les composants du langage utilisé (ici Typescript), comme par exemple les &lt;strong&gt;types&lt;/strong&gt;, les &lt;strong&gt;commentaires&lt;/strong&gt; de code, &lt;strong&gt;quand c’est nécessaire&lt;/strong&gt; quelques &lt;strong&gt;annotations&lt;/strong&gt; ou doc tags et pourquoi pas des &lt;strong&gt;conventions utilisées dans l’équipe&lt;/strong&gt; (nommage, emplacement des fichiers, etc…).&lt;/p&gt;
&lt;h2 id=&quot;analyse-statique-et-parsing-du-code&quot;&gt;Analyse statique et parsing du code&lt;/h2&gt;
&lt;p&gt;Ce qu’il faut bien comprendre, c’est que quand je parle de générer du code, il ne s’agit pas de le faire écrire par une IA, je parle bien d’une &lt;strong&gt;génération déterministe&lt;/strong&gt; à base d’&lt;strong&gt;analyse statique&lt;/strong&gt; du code comme on sait le faire depuis longtemps. Ce &lt;strong&gt;code généré&lt;/strong&gt; ne ferait &lt;strong&gt;plus réellement parti de notre base de code&lt;/strong&gt; comme avec les outils de scaffolding et pourrait évoluer au gré du temps, nécessitant uniquement une mise à jour puis re-génération sur notre projet facilitant ainsi sa maintenance.&lt;/p&gt;
&lt;p&gt;Dans mon exemple au dessus, je pourrai par exemple demander à cet outil de me générer soit une API REST, soit un outil en ligne de commande permettant d’appeler les méthodes &lt;code&gt;create&lt;/code&gt; et &lt;code&gt;findOne&lt;/code&gt; de mon service, soit totalement autre chose.&lt;/p&gt;
&lt;p&gt;Via l’analyse statique et le parsing du code, je peux &lt;strong&gt;déduire&lt;/strong&gt; quels sont les &lt;strong&gt;paramètres attendus&lt;/strong&gt;, les &lt;strong&gt;retours&lt;/strong&gt;, la &lt;strong&gt;documentation&lt;/strong&gt; et même les &lt;strong&gt;dépendances de mon service&lt;/strong&gt; et comment les construire.&lt;/p&gt;
&lt;h2 id=&quot;adapter-les-outils-à-nos-pratiques-et-non-linverse&quot;&gt;Adapter les outils à nos pratiques et non l’inverse&lt;/h2&gt;
&lt;p&gt;Le but serait de pouvoir &lt;strong&gt;configurer cette génération&lt;/strong&gt;, non pas avec un fichier de configuration comme on en voit souvent (yaml, toml, json, …), mais avec un fichier qui utiliserai le même langage que la cible de génération, permettant ainsi de tirer parti des fonctionnalités de l’IDE.&lt;/p&gt;
&lt;p&gt;Cette configuration de la génération pourrait permettre à une équipe de &lt;strong&gt;s’approprier l’outil&lt;/strong&gt; et mettre en place des conventions réutilisées de projet en projet pour un maximum d’efficacité.&lt;/p&gt;
&lt;p&gt;Par exemple, une équipe pourrait configurer l’outil pour que toutes les classes terminant par &lt;code&gt;Service&lt;/code&gt; et possédant une méthode publique &lt;code&gt;create&lt;/code&gt; aboutissent à une méthode &lt;code&gt;POST&lt;/code&gt; sur une route &lt;code&gt;/api/{nom de la classe sans le suffixe Service}&lt;/code&gt; en utilisant la documentation du code pour générer les spécifications OpenAPI.&lt;/p&gt;
&lt;p&gt;Suite à la commande de génération, le serveur web serait alors &lt;strong&gt;prêt&lt;/strong&gt;, &lt;strong&gt;documenté&lt;/strong&gt;, &lt;strong&gt;monitoré&lt;/strong&gt;, &lt;strong&gt;configurable&lt;/strong&gt;, les &lt;strong&gt;services correctement instanciés&lt;/strong&gt; suivant leurs dépendances sans d’autre intervention de la part de l’équipe et sans au final avoir à se préoccuper de ce qui a été généré.&lt;/p&gt;
&lt;h2 id=&quot;formidable--je-signe-où&quot;&gt;Formidable ! Je signe où ?&lt;/h2&gt;
&lt;p&gt;Cela peut sembler utopique mais il existe déjà pas mal d’outils qui font des choses dans le même état d’esprit. Par exemple, on a des &lt;a href=&quot;https://openapi-generator.tech/&quot;&gt;générateurs pour construire du code à partir d’une spécification OpenAPI&lt;/a&gt;, ce qui nous permet d’être sûr que les URLs sont les bonnes et que les types utilisés aussi.&lt;/p&gt;
&lt;p&gt;En Typescript, on sait &lt;a href=&quot;https://github.com/fabien0102/ts-to-zod&quot;&gt;générer un validateur Zod depuis des types Typescript&lt;/a&gt;. &lt;a href=&quot;https://www.prisma.io/&quot;&gt;Prisma&lt;/a&gt; génère le code typé nécessaire aux interactions avec une base de données depuis un schéma défini.&lt;/p&gt;
&lt;p&gt;Cependant, il s’agit toujours d’initiatives isolées pour des briques spécifiques.&lt;/p&gt;
&lt;p&gt;Le seul projet qui se rapproche de ce que j’ai réellement en tête est &lt;a href=&quot;https://encore.dev/&quot;&gt;Encore&lt;/a&gt; qui, en Go, vous permet d’&lt;strong&gt;écrire des cas d’utilisations&lt;/strong&gt;, de les &lt;strong&gt;exposer&lt;/strong&gt;, d’appeler d’autres &lt;strong&gt;services distribués&lt;/strong&gt; facilement, de gérer la &lt;strong&gt;configuration&lt;/strong&gt;, le &lt;strong&gt;monitoring&lt;/strong&gt;, des &lt;strong&gt;tâches Cron&lt;/strong&gt; et de &lt;strong&gt;provisionner&lt;/strong&gt; toute votre infrastructure en se basant sur votre propre code et en &lt;strong&gt;générant&lt;/strong&gt; tout ce qui est nécessaire pour que votre application fonctionne.&lt;/p&gt;
&lt;p&gt;De mon côté, je me suis amusé à faire quelques &lt;a href=&quot;https://github.com/YuukanOO/ease&quot;&gt;expérimentations en Go&lt;/a&gt; juste pour valider la faisabilité technique mais je n’ai aucun doute sur le fait qu’on pourrait &lt;strong&gt;imaginer quelque chose de malléable et d’extensible&lt;/strong&gt; pour couvrir tous les besoins triviaux qu’on a de projet en projet.&lt;/p&gt;
&lt;p&gt;La tâche est bien évidemment &lt;strong&gt;colossale&lt;/strong&gt; mais est-ce qu’il ne s’agirait pas là d’un futur souhaitable ?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded></item></channel></rss>