<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TechniveK</title>
    <description>The latest articles on DEV Community by TechniveK (@technivek).</description>
    <link>https://dev.to/technivek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2046119%2F1029ad89-6aaf-43ae-a6de-25b0fed1e653.jpg</url>
      <title>DEV Community: TechniveK</title>
      <link>https://dev.to/technivek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/technivek"/>
    <language>en</language>
    <item>
      <title>Le cache dans les applications web : le secret des performances</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Tue, 24 Sep 2024 20:25:12 +0000</pubDate>
      <link>https://dev.to/technivek/le-cache-dans-les-applications-web-le-secret-des-performances-4djp</link>
      <guid>https://dev.to/technivek/le-cache-dans-les-applications-web-le-secret-des-performances-4djp</guid>
      <description>&lt;p&gt;Aujourd’hui, les utilisateurs n’ont pas de temps à perdre. Ils veulent des pages qui se chargent en un clin d'œil, une navigation rapide, et des performances de haut vol. Pour arriver à ce niveau de fluidité, les développeurs disposent d’une arme puissante : le cache.&lt;/p&gt;

&lt;p&gt;Le terme "cache" est omniprésent dans les discussions sur la performance web. Le but, bien sûr, n'est pas d'appliquer du cache partout comme un sauvage ! Non, il faut comprendre quand, pourquoi et surtout comment implémenter le bon cache au bon endroit. Il en existe plein de types différents : cache navigateur, cache serveur, cache d’API et même cache en base de données, chacun jouant un rôle particulier.&lt;br&gt;
Dans cet article, je te propose de passer en revue tout ça, pour que tu puisses faire de ton application un petit bolide ! 🚨 🚘&lt;/p&gt;




&lt;h2&gt;
  
  
  Le cache : essentiel pour plusieurs raisons
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Amélioration des performances : en stockant des résultats souvent demandés, le cache permet d'éviter de répéter des calculs coûteux ou de recharger des données à chaque requête.&lt;/li&gt;
&lt;li&gt;Réduction de la charge du serveur : en limitant les appels à la base de données ou aux services tiers, tu réduis la charge globale du serveur.&lt;/li&gt;
&lt;li&gt;Meilleure expérience utilisateur : un site rapide améliore l’expérience des utilisateurs et les encourage à rester plus longtemps.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Le cache navigateur : garder les ressources localement
&lt;/h2&gt;

&lt;p&gt;Commençons par le plus familier : le &lt;strong&gt;cache navigateur&lt;/strong&gt;. Comme son nom l’indique, c’est le navigateur de l’utilisateur qui gère ce type de cache. C’est un mécanisme &lt;strong&gt;côté client&lt;/strong&gt; qui stocke les ressources &lt;u&gt;localement&lt;/u&gt; sur l'appareil de l'utilisateur. Lorsque quelqu'un visite ton site, son navigateur télécharge les ressources nécessaires (HTML, CSS, JavaScript, images, polices de caractères, etc.) et les garde en cache pour une durée déterminée. Si l'utilisateur revient plus tard sur cette page, le navigateur ne redemande pas ces fichiers au serveur : il les charge directement &lt;strong&gt;depuis le cache local&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment ça fonctionne ?
&lt;/h3&gt;

&lt;p&gt;Lorsque tu fais une requête HTTP, le serveur peut ajouter des &lt;strong&gt;en-têtes HTTP&lt;/strong&gt; (headers) qui indiquent au navigateur comment et combien de temps il doit conserver les ressources en cache.&lt;/p&gt;

&lt;p&gt;Les &lt;strong&gt;headers principaux&lt;/strong&gt; sont :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cache-Control&lt;/strong&gt; : il indique au navigateur pendant combien de temps conserver un fichier (ex. Cache-Control: max-age=86400 pour 24h).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ETag&lt;/strong&gt; : une sorte d'empreinte numérique (hash) du contenu. Si le contenu ne change pas, l'ETag reste le même et le navigateur sait qu’il peut réutiliser la ressource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last-Modified&lt;/strong&gt; : le navigateur compare la date de la dernière modification pour savoir s'il doit recharger une ressource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exemple avec Symfony : configuration des en-têtes de cache&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dans Symfony, tu peux facilement contrôler le cache navigateur au niveau des &lt;strong&gt;réponses HTTP&lt;/strong&gt;. Voici comment ajouter des directives de cache dans un contrôleur :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcx2abom2yrmpd3ezxg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcx2abom2yrmpd3ezxg1.png" alt="Headers HTTP browser cache" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le navigateur se fie aux en-têtes HTTP envoyés par ton serveur pour savoir &lt;strong&gt;combien de temps&lt;/strong&gt; il peut garder une ressource en cache avant de la retélécharger. En gros, ces en-têtes disent au navigateur : "Tu peux conserver ces fichiers pendant X temps", ou bien "Vérifie auprès du serveur si ce fichier a changé avant de l'utiliser".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemple concret : une page de FAQ statique&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine que tu gères une page de FAQ qui ne change pas souvent. Avec un cache navigateur, tu peux demander au navigateur de garder la page en mémoire pendant une journée, sans jamais revenir vers le serveur.&lt;/p&gt;

&lt;p&gt;En ajustant les directives max-age et immutable, tu peux t’assurer que cette page ne sera téléchargée qu’une seule fois par jour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6vdn5cqkfnm7zunznkw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6vdn5cqkfnm7zunznkw.png" alt="Headers HTTP complementary browser cache" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Les avantages :
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rapidité pour l’utilisateur : il ne télécharge pas les fichiers à chaque visite.&lt;/li&gt;
&lt;li&gt;Réduction des coûts serveurs : moins de requêtes signifie moins de charge.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Les limites :
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ça reste côté client : ce type de cache n'est accessible qu'à partir de l’appareil de l’utilisateur. Ce qui signifie que chaque nouvel utilisateur, ou chaque nouveau navigateur, doit d'abord télécharger les ressources avant que le cache ne soit utilisé.&lt;/li&gt;
&lt;li&gt;Le cache navigateur ne peut rien faire pour accélérer la toute première visite d’un utilisateur.&lt;/li&gt;
&lt;li&gt;Tu dois bien gérer les versions des fichiers pour ne pas que le cache garde des ressources obsolètes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Le cache HTTP : l’optimisation côté serveur
&lt;/h2&gt;

&lt;p&gt;Passons maintenant au &lt;strong&gt;cache HTTP&lt;/strong&gt;. Là où le cache navigateur est une optimisation côté client, le cache HTTP se situe côté serveur. &lt;br&gt;
Il est géré par des outils comme Varnish et il fonctionne comme un proxy qui s’interpose entre ton application web et l’utilisateur. Il peut servir des milliers de requêtes par seconde sans avoir à interroger PHP ou la base de données.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment ça marche ?
&lt;/h3&gt;

&lt;p&gt;Varnish &lt;strong&gt;intercepte&lt;/strong&gt; toutes les requêtes HTTP qui arrivent sur ton serveur et vérifie si une version de la page demandée est déjà &lt;strong&gt;disponible en cache&lt;/strong&gt;. Si c'est le cas, il répond directement avec la page mise en cache, sans avoir à solliciter le serveur web ou la base de données. L’idée, c’est de stocker les réponses les plus fréquemment demandées pour éviter que ton application ne doive les générer à chaque fois. Résultat : des réponses ultra-rapides et un soulagement pour ton serveur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemple de configuration Symfony pour Varnish :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Symfony te permet de contrôler facilement quels éléments doivent être mis en cache par Varnish grâce aux mêmes en-têtes HTTP que pour le cache navigateur.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowm77m6rage4m2bsg76h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowm77m6rage4m2bsg76h.png" alt="Headers HTTP cache Varnish" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemple concret : une liste d'articles de blog&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Les articles de blog changent rarement une fois publiés. Tu peux donc utiliser un &lt;strong&gt;ETag&lt;/strong&gt; pour permettre au cache HTTP de vérifier si le contenu a changé, &lt;u&gt;sans que le navigateur ait besoin de télécharger&lt;/u&gt; à nouveau toute la page.&lt;/p&gt;

&lt;p&gt;Lorsque l’utilisateur consulte la page, l’ETag généré à partir du contenu est comparé à celui que le cache a déjà stocké. Si rien n’a changé, la réponse &lt;code&gt;304 Not Modified&lt;/code&gt; est renvoyée, sans transférer le contenu à nouveau.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Les avantages du cache Varnish :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimisation pour tous les utilisateurs : contrairement au cache navigateur, Varnish agit pour tous les utilisateurs, même ceux qui visitent ton site pour la première fois.&lt;/li&gt;
&lt;li&gt;Soulage le serveur : en servant directement des réponses en cache, Varnish évite de solliciter ton application ou ta base de données, ce qui améliore considérablement les performances sous charge.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Le cache système : optimiser les données à long terme
&lt;/h2&gt;

&lt;p&gt;Le cache système intervient au niveau serveur et permet de stocker les résultats des calculs complexes, des requêtes lourdes en base de données, ou des réponses provenant de services tiers (API).&lt;/p&gt;

&lt;p&gt;Dans Symfony, la manière la plus courante d’utiliser ce cache, c’est via le composant &lt;code&gt;Cache&lt;/code&gt; qui te donne accès à une abstraction de plusieurs systèmes de cache (comme Redis, Memcached ou le file system).&lt;/p&gt;

&lt;p&gt;Symfony propose une interface simple et efficace avec la fonction &lt;code&gt;get()&lt;/code&gt;. Cette méthode permet d’accéder au cache si la clé existe déjà, ou de la créer si elle n’existe pas encore, tout cela en une seule ligne de code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec le FilesystemAdapter (cache sur disque)
&lt;/h3&gt;

&lt;p&gt;Le &lt;code&gt;FilesystemAdapter&lt;/code&gt; est une implémentation qui stocke les données en cache directement sur le disque de ton serveur. C’est pratique quand tu veux démarrer rapidement sans avoir besoin d'une solution de cache externe comme Redis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx84ij4ak517jv7cwsfl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx84ij4ak517jv7cwsfl9.png" alt="Filesystem cache" width="800" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Symfony te permet de gérer &lt;strong&gt;plusieurs types de cache&lt;/strong&gt; via différents &lt;strong&gt;adaptateurs&lt;/strong&gt;. Tu n’es donc pas limité au filesystem. Si tu veux passer à un cache en mémoire ultra-rapide, tu peux utiliser des services comme &lt;strong&gt;Redis&lt;/strong&gt; ou &lt;strong&gt;Memcached&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec RedisAdapter (cache en mémoire)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnl960ngqod85khfdgohv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnl960ngqod85khfdgohv.png" alt="Cache Redis Symfony" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Redis est un excellent choix pour les applications qui ont besoin d'un cache performant. Avec Redis, tu stockes les données directement en mémoire, ce qui est beaucoup plus rapide que le disque dur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avantages :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moins de requêtes externes : tu diminues la charge sur les serveurs API.&lt;/li&gt;
&lt;li&gt;Meilleures performances : tes utilisateurs reçoivent des réponses plus rapidement.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Le cache en base de données : optimiser les requêtes
&lt;/h2&gt;

&lt;p&gt;On ne peut pas parler de cache sans mentionner le cache en base de données. Dans Symfony, tu peux éviter de refaire constamment les mêmes requêtes SQL coûteuses en utilisant des systèmes comme Doctrine Cache ou Redis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comment ça fonctionne ?
&lt;/h3&gt;

&lt;p&gt;L’idée est de stocker les résultats de requêtes fréquemment demandées en mémoire. Quand la même requête est effectuée à nouveau, plutôt que de l’envoyer à la base de données, Symfony renvoie la réponse depuis le cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple : Cache des produits les plus vendus
&lt;/h3&gt;

&lt;p&gt;Imaginons que tu veuilles afficher la liste des produits les plus vendus dans une boutique e-commerce, une requête qui peut être lourde à exécuter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54kirey63s9i884lw0e1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54kirey63s9i884lw0e1.png" alt="SQL query cache" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Autre méthode : utilisation du cache intégré à Doctrine (Result Cache)
&lt;/h3&gt;

&lt;p&gt;Doctrine offre aussi son propre système de cache des résultats des requêtes. C’est particulièrement pratique pour cacher &lt;u&gt;directement au niveau de l'ORM&lt;/u&gt;, en rendant le processus de mise en cache encore plus transparent.&lt;/p&gt;

&lt;p&gt;Pour activer le &lt;strong&gt;Result Cache&lt;/strong&gt; dans Doctrine, tu dois d’abord &lt;strong&gt;configurer un adaptateur de cache&lt;/strong&gt; (comme Redis ou le système de fichiers) dans ton fichier &lt;code&gt;doctrine.yaml&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fga807ts3xvlgu69g7d5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fga807ts3xvlgu69g7d5b.png" alt="doctrine.yaml" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensuite, tu peux mettre en cache le résultat d’une requête directement dans ton repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemple : caching avec Doctrine Result Cache&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cx20bw9uk9k4a8mdrwn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cx20bw9uk9k4a8mdrwn.png" alt="Result Cache Doctrine" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;useResultCache(true, 3600, 'top_selling_products')&lt;/code&gt; : On active le cache pour la requête avec une durée d’expiration d'une heure. La clé du cache est &lt;code&gt;top_selling_products&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache Redis via Doctrine&lt;/strong&gt; : Doctrine enverra les résultats dans Redis (ou tout autre cache configuré), ce qui évite d'exécuter la même requête SQL plusieurs fois durant la période de cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Avantages :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Réduction de la charge sur la base de données&lt;/strong&gt; : En mettant en cache les résultats, tu réduis considérablement la charge sur la base de données, surtout pour les requêtes lourdes ou fréquentes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amélioration des temps de réponse&lt;/strong&gt; : En servant des résultats directement depuis le cache (qui peut être en mémoire avec Redis), tu réduis les temps d'accès par rapport à des requêtes SQL régulières.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicité d’utilisation&lt;/strong&gt; : Avec Symfony et Doctrine, la gestion du cache est rendue très facile grâce à des méthodes comme &lt;code&gt;get()&lt;/code&gt; du composant &lt;code&gt;Cache&lt;/code&gt; ou le &lt;strong&gt;Result Cache&lt;/strong&gt; intégré à Doctrine.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Maintenant que tu maîtrises chaque type de cache, la question qui brûle les lèvres : comment tout ça fonctionne ensemble ?&lt;/p&gt;

&lt;p&gt;En fait, c'est la complémentarité des caches qui te permet d’avoir une application vraiment performante. Le cache navigateur accélère les visites répétées d'un utilisateur en stockant les ressources directement sur son appareil. Le cache HTTP (Varnish), lui, optimise la charge côté serveur en servant les pages générées directement depuis sa mémoire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L’objectif ?&lt;/strong&gt; Gagner sur tous les tableaux. Le navigateur fait en sorte que l'utilisateur ne retélécharge pas inutilement des fichiers. Varnish, de son côté, fait en sorte que ton serveur ne soit pas surchargé par des requêtes redondantes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Que ce soit côté client ou côté serveur, tout est une question de bon équilibre. Chaque type de cache a ses avantages et ses inconvénients, mais en les combinant judicieusement, tu peux atteindre des performances spectaculaires.&lt;/p&gt;

&lt;p&gt;Alors, prends le temps de bien configurer tes caches, car ils peuvent faire toute la différence entre une application lente et une application rapide comme l’éclair !&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>softwareengineering</category>
      <category>symfony</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Maîtriser les principes SOLID : pour un code propre et performant</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Sat, 14 Sep 2024 06:36:22 +0000</pubDate>
      <link>https://dev.to/technivek/maitriser-les-principes-solid-pour-un-code-propre-et-performant-4a4m</link>
      <guid>https://dev.to/technivek/maitriser-les-principes-solid-pour-un-code-propre-et-performant-4a4m</guid>
      <description>&lt;p&gt;Dans le monde du développement, il y a un grand fossé entre écrire du code et écrire du bon code. Tu sais, ce code que tu peux modifier dans 6 mois sans avoir à tout réécrire, ce code qui ne t’oblige pas à passer des heures à le comprendre avant de le débugger.&lt;/p&gt;

&lt;p&gt;Et si je te disais qu’il existe une méthode pour structurer ton code de manière à le rendre facilement maintenable, modulaire, et sans prise de tête ? Accueillons sans plus attendre les &lt;strong&gt;principes SOLID&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ces cinq principes, introduits par l'incontournable Robert C. Martin, aka Uncle Bob, montrent comment organiser tes classes et tes méthodes pour qu’elles puissent évoluer sans s’effondrer.&lt;/p&gt;

&lt;p&gt;Tu es prêt à devenir un développeur plus &lt;strong&gt;SOLID&lt;/strong&gt; 💪 que jamais ? On y va. 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Le * S * ➡️ Single Responsibility Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Une classe ne doit avoir qu'une seule responsabilité."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L'idée est simple : chaque classe doit avoir une seule raison de changer. Cela signifie qu'une classe ne doit s'occuper que d'une seule tâche ou d'une seule fonctionnalité. Si tu mélanges plusieurs responsabilités dans une même classe, tu finiras par complexifier ton code et rendre les modifications plus risquées.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec Symfony
&lt;/h3&gt;

&lt;p&gt;Prenons l'exemple d'une classe qui gère à la fois la validation d'un formulaire d'utilisateur et l'envoi d'emails de confirmation. Voici un exemple de ce qu'il &lt;strong&gt;&lt;u&gt;ne faut pas faire&lt;/u&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffie5d4gdn04ghjeri1w4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffie5d4gdn04ghjeri1w4.png" alt="Symfony UserService bad way" width="800" height="806"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ici, la classe &lt;code&gt;UserService&lt;/code&gt; viole le principe de responsabilité unique. Elle gère à la fois la création de l'utilisateur, la validation des données, et l'envoi d'un email. Chaque fois que l'une de ces responsabilités évolue, la classe doit être modifiée.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution : On divise les responsabilités !&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmxit77upqwknn3vwh5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmxit77upqwknn3vwh5o.png" alt="Symfony UserService right way" width="800" height="786"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintenant, chaque classe a une responsabilité distincte : &lt;code&gt;UserValidator&lt;/code&gt; pour la validation, &lt;code&gt;EmailService&lt;/code&gt; pour l'envoi d'emails et &lt;code&gt;UserService&lt;/code&gt; pour la logique de gestion des utilisateurs. Ton code devient beaucoup plus facile à maintenir !&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Le * O * ➡️ Open/Closed Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Les entités doivent être ouvertes à l'extension, mais fermées à la modification."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cela signifie que tu dois pouvoir ajouter de nouvelles fonctionnalités à une classe sans avoir à modifier son code existant.&lt;br&gt;
Pourquoi ? Parce que chaque fois que tu modifies du code existant, tu risques d'introduire des bugs. Mieux vaut ajouter des fonctionnalités que de retoucher le code déjà écrit et testé.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec Symfony
&lt;/h3&gt;

&lt;p&gt;Imaginons que tu développes un service de paiement dans une boutique Symfony. Ce service gère différentes méthodes de paiement : carte de crédit, PayPal, etc. Si tu veux ajouter un nouveau mode de paiement (Bitcoin, par exemple), le principe Open/Closed te dit qu'il ne faut pas toucher au code déjà existant pour éviter les régressions.&lt;/p&gt;

&lt;p&gt;Voici une mauvaise approche, où l'ajout de nouvelles fonctionnalités nécessite des modifications dans le code existant :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71rgi756vra4c2s8wtam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71rgi756vra4c2s8wtam.png" alt="Symfony PaymentService bad way" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le problème ici, c'est qu'à chaque fois que tu veux ajouter un nouveau moyen de paiement, tu dois modifier la méthode &lt;code&gt;processPayment&lt;/code&gt;, ce qui viole le principe &lt;strong&gt;Open/Closed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution : Utiliser l'héritage et les interfaces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pour résoudre ce problème et respecter le principe Open/Closed, tu vas utiliser une interface et des classes spécifiques pour chaque type de paiement. Comme ça, tu pourras ajouter de nouvelles méthodes de paiement sans toucher au code existant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco592ebsjwcb163v0y5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco592ebsjwcb163v0y5l.png" alt="Symfony PaymentMethodInterface" width="800" height="247"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe6btpw2o0q1ddjlch6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe6btpw2o0q1ddjlch6m.png" alt="Symfony CreditCardPayment class" width="800" height="253"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxvnpxxs7p4cqmim1pmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxvnpxxs7p4cqmim1pmp.png" alt="Symfony PaypalPayment class" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Quelques explications
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chaque méthode de paiement implémente l'interface &lt;code&gt;PaymentMethodInterface&lt;/code&gt;, ce qui garantit qu'elles ont toutes la méthode pay().&lt;/li&gt;
&lt;li&gt;Dans chaque classe, on utilise l'attribut &lt;code&gt;#[AsTaggedItem('payment.method')]&lt;/code&gt; pour dire à Symfony que ces classes représentent des méthodes de paiement. Elles sont toutes taguées sous le même nom, &lt;code&gt;payment.method&lt;/code&gt;, pour être facilement récupérables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Le service principal&lt;/strong&gt; &lt;code&gt;PaymentService&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Maintenant, notre &lt;code&gt;PaymentService&lt;/code&gt; doit savoir quelle méthode de paiement utiliser, celle qui a été sélectionnée par l'utilisateur par exemple. Voici comment créer le &lt;code&gt;PaymentService&lt;/code&gt; pour qu'il prenne en compte la méthode de paiement envoyée par le contrôleur.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyxrz2zvy22i8vd0wzab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyyxrz2zvy22i8vd0wzab.png" alt="Symfony PaymentService" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans ton &lt;strong&gt;contrôleur&lt;/strong&gt;, tu vas récupérer la méthode de paiement &lt;strong&gt;depuis la requête&lt;/strong&gt; (via un query parameter par exemple) et la passer au &lt;code&gt;PaymentService&lt;/code&gt;. Ton controller pourrait ressembler à ça :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39s830ugwihhglcjiuvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39s830ugwihhglcjiuvn.png" alt="Symfony PaymentController" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il est important que chaque méthode soit taguée correctement pour être injectée dans le &lt;code&gt;PaymentService&lt;/code&gt; grâce au &lt;code&gt;TaggedIterator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;En résumé&lt;/strong&gt;, l'itérable &lt;code&gt;$paymentMethods&lt;/code&gt; dans le &lt;code&gt;PaymentService&lt;/code&gt; permet de récupérer dynamiquement toutes les méthodes de paiement disponibles, et le contrôleur passe cette information en fonction de la requête. Le service choisit ensuite la bonne méthode en fonction de la requête et procède au paiement.&lt;/p&gt;

&lt;p&gt;Cette approche respecte le principe &lt;strong&gt;Open/Closed&lt;/strong&gt; tout en exploitant les fonctionnalités modernes de Symfony pour rendre le code plus flexible et extensible.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Le * L * ➡️ Liskov Substitution Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Les objets d'une classe dérivée doivent pouvoir remplacer les objets de leur classe mère sans altérer le bon fonctionnement du programme."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ce principe peut paraître un peu complexe à première vue, mais il est essentiel pour assurer la cohérence de ton code. En résumé, une classe fille doit pouvoir être utilisée partout où une classe mère est attendue, sans briser le comportement du programme.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec Symfony
&lt;/h3&gt;

&lt;p&gt;Prenons une classe &lt;code&gt;Article&lt;/code&gt; et une classe &lt;code&gt;FeaturedArticle&lt;/code&gt;, qui hérite de &lt;code&gt;Article&lt;/code&gt;. Si &lt;code&gt;FeaturedArticle&lt;/code&gt; ne respecte pas le contrat de la classe mère &lt;code&gt;Article&lt;/code&gt;, tu risques d’avoir des problèmes lors de son utilisation dans certaines parties de ton code.&lt;/p&gt;

&lt;p&gt;Voici un cas qui viole le &lt;strong&gt;principe de Liskov&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8l9odef3u3zbpahqpxn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8l9odef3u3zbpahqpxn.png" alt="Symfony Liskov principle bad way" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;À première vue, ça peut sembler correct, mais si tu passes un &lt;code&gt;FeaturedArticle&lt;/code&gt; dans une logique qui attend un &lt;code&gt;Article&lt;/code&gt;, tu risques d’avoir des résultats inattendus.&lt;/p&gt;

&lt;p&gt;Par exemple, imagine un système où les titres sont mis à jour automatiquement :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b9m258tu4e5pkcsyiqm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1b9m258tu4e5pkcsyiqm.png" alt="Symfony updateArticleTitle bad way" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le titre sera "&lt;strong&gt;Featured: Nouveautés&lt;/strong&gt;", même si ce n'était pas prévu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;Pour respecter le principe de Liskov, tu dois faire en sorte que les classes dérivées n'altèrent pas le comportement attendu de la classe mère. Par exemple avec Symfony, si tu as un formulaire de base pour un &lt;code&gt;Article&lt;/code&gt;, et que tu veux étendre son comportement pour un &lt;code&gt;FeaturedArticle&lt;/code&gt;, tu peux étendre le &lt;code&gt;FormType&lt;/code&gt; tout en conservant les propriétés de base.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Crée un &lt;code&gt;FormType&lt;/code&gt; pour un article de base :
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlzyhv0avq4ppyz54675.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlzyhv0avq4ppyz54675.png" alt="Symfony ArticleType" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Crée un FormType pour un FeaturedArticle qui hérite de ArticleType :
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05jvaia65ecrhuqi1x0k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F05jvaia65ecrhuqi1x0k.png" alt="Symfony FeaturedArticleType" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans le contrôleur, utilise ce &lt;code&gt;FormType&lt;/code&gt; pour un article à la une :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnt8os1cxnv9n0or9c7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnt8os1cxnv9n0or9c7f.png" alt="Symfony ArticleController" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avec cette approche, le &lt;strong&gt;principe de Liskov&lt;/strong&gt; est respecté. Tu peux utiliser &lt;code&gt;FeaturedArticleType&lt;/code&gt; partout où &lt;code&gt;ArticleType&lt;/code&gt; est utilisé, tout en ajoutant des champs spécifiques sans casser le comportement de base.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Le * I * ➡️ Interface Segregation Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Une classe ne doit pas être obligée d'implémenter des méthodes qu'elle n'utilisera jamais."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Si tu veux garder ton code flexible, ce principe est primordial. Il dit que si une classe implémente une interface, elle ne doit implémenter &lt;strong&gt;que ce dont elle a besoin&lt;/strong&gt;. Si l'interface est trop générale et oblige une classe à implémenter des &lt;u&gt;méthodes inutiles&lt;/u&gt;, elle deviendra plus complexe et difficile à maintenir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec Symfony
&lt;/h3&gt;

&lt;p&gt;Imaginons que tu aies une interface pour un export de données, avec plusieurs types d’export (CSV, JSON, XML). Si tu crées une interface unique pour tout, tu ne respectes pas le principe de ségrégation des interfaces :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff60vjcac88u6p3uuluo4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff60vjcac88u6p3uuluo4.png" alt="PHP DataExportInterface bad way" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si tu implémentes cette interface dans une classe qui n'a besoin que de JSON, tu te retrouves à devoir définir des méthodes inutiles, comme &lt;code&gt;exportToCSV()&lt;/code&gt; et &lt;code&gt;exportToXML()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution : Créer des interfaces spécifiques
&lt;/h3&gt;

&lt;p&gt;La solution est de &lt;strong&gt;diviser&lt;/strong&gt; cette interface en &lt;strong&gt;plusieurs petites interfaces&lt;/strong&gt; qui sont spécifiques à chaque besoin :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkde6p5cf7m4iupb1vydx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkde6p5cf7m4iupb1vydx.png" alt="PHP Export Interfaces right way" width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maintenant, chaque classe implémente seulement les interfaces dont elle a besoin :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu4jh9lv88x9uouw3478.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcu4jh9lv88x9uouw3478.png" alt="Symfony JsonExporter right way" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ton code est plus léger et maintenable, &lt;strong&gt;sans méthodes superflues&lt;/strong&gt; !&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Le * D * ➡️ Dependency Inversion Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Tous deux doivent dépendre d'abstractions."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🔔 Dernier principe ! 🔔&lt;br&gt;
Si tu es arrivé jusque là, je te félicite car ce n'était pas simple !&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alors, qu'est-ce qu'il nous dit ce dernier principe ?&lt;/strong&gt;&lt;br&gt;
Il nous dit tout simplement de ne pas dépendre de classes concrètes, mais plutôt d’abstractions (des interfaces ou des classes abstraites).&lt;/p&gt;

&lt;h3&gt;
  
  
  Exemple avec Symfony
&lt;/h3&gt;

&lt;p&gt;Imaginons un service qui envoie des notifications. Une mauvaise approche serait de faire dépendre directement ce service des implémentations concrètes :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof7q0mn28f9dhabso49c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fof7q0mn28f9dhabso49c.png" alt="Symfony NotificationService bad way" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ici, on voit que ce service dépend directement des implémentations concrètes d'email et de SMS (les classes EmailService et SmsService). Si tu veux changer le système d'envoi d'email, il faudra que tu modifies cette classe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Une meilleure approche est de faire en sorte que &lt;code&gt;NotificationService&lt;/code&gt; dépende d'une &lt;strong&gt;abstraction&lt;/strong&gt; (une interface), plutôt que &lt;u&gt;d'implémentations concrètes&lt;/u&gt; :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interface pour les services de notification&lt;/strong&gt; :&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6awl6ltru7za38u1h8s0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6awl6ltru7za38u1h8s0.png" alt="Symfony NotificationInterface right way" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implémentation pour les emails&lt;/strong&gt; 📧 :&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xahn7sgcaf66pqzioeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xahn7sgcaf66pqzioeg.png" alt="Symfony EmailNotificationService right way" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implémentation pour les SMS&lt;/strong&gt; 📱 :&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7b1tte04i4i5ghprags.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7b1tte04i4i5ghprags.png" alt="Symfony SmsNotificationService right way" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Le service &lt;code&gt;NotificationService&lt;/code&gt; dépend maintenant de &lt;code&gt;NotificationInterface&lt;/code&gt;, ce qui te permet de changer facilement l'implémentation sans toucher au service principal.&lt;/p&gt;

&lt;p&gt;Maintenant on va injecter dynamiquement ces services dans un &lt;code&gt;NotificationManager&lt;/code&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63wg8hgrk3m87d1d9xem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63wg8hgrk3m87d1d9xem.png" alt="Symfony NotificationManager right way" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilisation dans le contrôleur
&lt;/h2&gt;

&lt;p&gt;Dans le contrôleur, on peut choisir dynamiquement le service de notification à utiliser :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fax6t5x33ivs7w9fm7lrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fax6t5x33ivs7w9fm7lrq.png" alt="Symfony NotificationController right way" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On injecte toutes les implémentations de &lt;code&gt;NotificationServiceInterface&lt;/code&gt; automatiquement grâce au tag &lt;code&gt;#[AsTaggedItem('notification.service')]&lt;/code&gt; sur chaque service.&lt;/li&gt;
&lt;li&gt;Le contrôleur peut choisir dynamiquement quel service utiliser via une query string dans la requête.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette solution suit parfaitement le dernier principe SOLID car :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le &lt;code&gt;NotificationManager&lt;/code&gt; ne dépend que de l'interface &lt;code&gt;NotificationServiceInterface&lt;/code&gt;, et non des implémentations spécifiques.&lt;/li&gt;
&lt;li&gt;Le code est &lt;strong&gt;ouvert à l'ajout de nouveaux services&lt;/strong&gt; (par exemple, une notification par push) sans modifier le &lt;code&gt;NotificationManager&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Et voilà ! Tu as maintenant une &lt;strong&gt;solide&lt;/strong&gt; 😉 compréhension des &lt;strong&gt;principes SOLID&lt;/strong&gt; et de leur application dans un projet Symfony. Le but de ces principes est de rendre ton code plus &lt;strong&gt;flexible&lt;/strong&gt;, &lt;strong&gt;facile à maintenir&lt;/strong&gt;, et surtout &lt;strong&gt;testable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ces concepts peuvent parfois sembler abstraits, mais lorsqu'ils sont appliqués, ils t'offrent un énorme gain en termes de qualité et de durabilité du code !&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>solidprinciples</category>
      <category>php</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Le Design Pattern Observer : un système de notification puissant</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Thu, 12 Sep 2024 11:55:50 +0000</pubDate>
      <link>https://dev.to/technivek/le-design-pattern-observer-un-systeme-de-notification-puissant-3fjf</link>
      <guid>https://dev.to/technivek/le-design-pattern-observer-un-systeme-de-notification-puissant-3fjf</guid>
      <description>&lt;p&gt;Dans le développement de tes applications, tu auras souvent besoin que ton application puisse réagir à des événements de manière flexible. Par exemple, imaginons que tu souhaites notifier plusieurs systèmes lorsque certaines actions se produisent (comme l'inscription d'un utilisateur). C'est là que le Pattern Observer entre en jeu. Ce pattern te permet d'établir une relation entre des objets, afin que lorsque l'état de l'un change, tous les autres soient automatiquement informés et mis à jour.&lt;/p&gt;

&lt;p&gt;Symfony implémente déjà efficacement ce pattern grâce à l'Event Dispatcher, ce qui le rend bien pratique et puissant à utiliser dans tes projets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qu'est-ce que le Pattern Observer ?
&lt;/h2&gt;

&lt;p&gt;Le Pattern Observer permet de définir une relation entre le sujet observé et un ou plusieurs observateurs. Lorsqu'un changement d'état survient sur l'objet observé, tous les observateurs sont notifiés pour qu'ils puissent réagir en conséquence.&lt;/p&gt;

&lt;p&gt;Voilà comment ça fonctionne :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👨 Sujet (Observable) : L'objet principal qui change d'état.&lt;/li&gt;
&lt;li&gt;👀 Observateurs (Observers) : Les objets qui réagissent aux changements de l'observable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Exemple
&lt;/h2&gt;

&lt;p&gt;Imaginons que tu as un site où un utilisateur peut s'inscrire. À chaque inscription, tu veux envoyer un email de bienvenue, ajouter l'utilisateur à une liste de newsletters et informer un système d'analytics.&lt;/p&gt;

&lt;p&gt;Plutôt que de tout coder de manière rigide au même endroit, tu peux déléguer ces tâches à différents observateurs qui seront informés dès que l'événement "utilisateur inscrit" est déclenché.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implémentation du Pattern Observer dans Symfony
&lt;/h2&gt;

&lt;p&gt;Dans Symfony, tu vas utiliser l'Event Dispatcher pour mettre en place le Pattern Observer. Ca va te permettre d'organiser le code proprement et de le rendre extensible sans modification de la logique de base.&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 1️⃣ : Déclarer l'Événement
&lt;/h3&gt;

&lt;p&gt;On va commencer par créer un événement qui sera dispatché lorsqu'un utilisateur s'inscrit. Ce dernier contiendra les informations de l'utilisateur.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hbzuk6dcvudeiamh19j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hbzuk6dcvudeiamh19j.png" alt="PHP UserRegisteredEvent class" width="800" height="822"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 2️⃣ : Dispatcher l'Événement
&lt;/h3&gt;

&lt;p&gt;Maintenant, dans ton contrôleur ou service, tu vas dispatch cet événement quand un utilisateur s'inscrit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxaspwaq04yvzda0383z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxaspwaq04yvzda0383z.png" alt="Symfony RegistrationController" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 3️⃣ : Créer des Observateurs (Listeners)
&lt;/h3&gt;

&lt;p&gt;Ensuite, tu vas devoir créer les observateurs, qui seront appelés à chaque fois que l'événement d'inscription est dispatché. Ici, tu as l'exemple où on envoie un email et on ajoute l'utilisateur à une liste de newsletters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Observateur 1 : Envoyer un email de bienvenue
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqcrd908we9dzqpshnfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyqcrd908we9dzqpshnfz.png" alt="Symfony WelcomeEmailListener" width="800" height="679"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Observateur 2 : Ajouter l'utilisateur à une liste de newsletters
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61eynfg9p8soqu2rir45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61eynfg9p8soqu2rir45.png" alt="Symfony NewsletterSubscriberListener" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 4️⃣ : Configuration des Observateurs
&lt;/h3&gt;

&lt;p&gt;Tu dois maintenant enregistrer les listeners dans la configuration pour qu’ils écoutent l’événement &lt;code&gt;user.registered&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Dans &lt;code&gt;config/services.yaml&lt;/code&gt;, il faut ajouter les observateurs comme services :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh6hj23vvjscud2qfd2x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh6hj23vvjscud2qfd2x.png" alt="Symfony services yml observers" width="800" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ℹ️ &lt;strong&gt;Depuis la version 5.3 de Symfony&lt;/strong&gt;, tu peux utiliser des attributs PHP pour configurer les services et les écouteurs d'événements (event listeners). C'est une approche plus moderne qui te permet de déclarer les événements directement dans les classes, au lieu d’utiliser le fichier services.yaml.&lt;br&gt;
Tu peux donc utiliser l'attribut &lt;code&gt;#[AsEventListener]&lt;/code&gt; directement sur la méthode de ton listener.&lt;/p&gt;

&lt;p&gt;Je vais te montrer comment adapter les deux observateurs avec des attributs (donc plus besoin de config particulière dans &lt;code&gt;config/services.yaml&lt;/code&gt; 😉 :&lt;/p&gt;

&lt;h3&gt;
  
  
  Observateur 1
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjizo7wjqjwx0stvwdczn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjizo7wjqjwx0stvwdczn.png" alt="Symfony WelcomeEmailListener" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Observateur 2
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmaygocqlbuv17cvgeh40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmaygocqlbuv17cvgeh40.png" alt="Symfony NewsletterSubscriberListener" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Petite explication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;L'attribut &lt;code&gt;#[AsEventListener]&lt;/code&gt; indique que la méthode est un listener pour un événement spécifique.&lt;/li&gt;
&lt;li&gt;Le premier argument de l'attribut est le nom de l'événement que l'observateur écoute (UserRegisteredEvent::NAME).&lt;/li&gt;
&lt;li&gt;Et enfin, le paramètre method précise la méthode de la classe qui sera exécutée lorsque l'événement sera déclenché (dans notre cas, onUserRegistered). 😄&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Étape 5️⃣ : Tester l'implémentation
&lt;/h3&gt;

&lt;p&gt;Lorsque tu enregistres un utilisateur avec le contrôleur &lt;code&gt;RegistrationController&lt;/code&gt;, l'événement est dispatché et Symfony appelle automatiquement les observateurs correspondants. L'email est envoyé, et l'utilisateur est ajouté à la liste de newsletters sans que ces actions/logiques ne soient mélangées à ton code métier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi Utiliser le Pattern Observer ?
&lt;/h2&gt;

&lt;p&gt;Parce que c'est un &lt;strong&gt;indispensable&lt;/strong&gt; ! Il permettra à ton application d'être :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plus flexible&lt;/strong&gt; : tu peux ajouter ou modifier des observateurs sans toucher à ton code principal. Il suffit d'ajouter un listener !&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moins couplée&lt;/strong&gt; : les différentes parties de ton appli &lt;strong&gt;&lt;u&gt;ne sont pas&lt;/u&gt;&lt;/strong&gt; étroitement liées. Par exemple, l'envoi d'emails et l'ajout à la newsletter ne sont pas codés directement dans le contrôleur d'inscription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plus scalable&lt;/strong&gt; : ce pattern permet de réagir facilement à des événements qui pourraient impliquer plusieurs systèmes ou services (notifications, analytics, envois, etc.).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bonus : Le lien avec le DDD (Domain-Driven Design)
&lt;/h2&gt;

&lt;p&gt;Le Pattern Observer trouve souvent sa place dans les architectures basées sur le DDD (Domain-Driven Design), où les événements sont des éléments clés (Domain Events). Ces événements permettent de faire réagir différentes parties du système, souvent en dehors du domaine principal. Mais ça, c’est une discussion pour un prochain article complet sur le &lt;strong&gt;DDD&lt;/strong&gt; !&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>designpatterns</category>
      <category>php</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Découverte de l'Architecture Hexagonale : l'hexa c'est carré !</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Wed, 11 Sep 2024 19:48:47 +0000</pubDate>
      <link>https://dev.to/technivek/decouverte-de-larchitecture-hexagonale-pour-un-code-robuste-et-evolutif-524l</link>
      <guid>https://dev.to/technivek/decouverte-de-larchitecture-hexagonale-pour-un-code-robuste-et-evolutif-524l</guid>
      <description>&lt;p&gt;Dans le développement logiciel, on cherche souvent à résoudre les problèmes d'évolutivité, de maintenabilité et de testabilité du code. C’est là qu’entre en scène l'Architecture Hexagonale, aussi appelée "&lt;strong&gt;Ports and Adapters&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;L'idée de cette approche développée par Alistair Cockburn est que le domaine métier (le cœur de ton application) soit isolé du reste du système et puisse être utilisé indépendamment des technologies externes (framework, base de données, UI, API externes).&lt;/p&gt;

&lt;p&gt;Allez, on va plonger dans les concepts fondamentaux de l'Architecture Hexagonale et voir ensemble comment elle peut transformer la manière dont tu conçois ton code ! 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  C'est quoi exactement l'Architecture Hexagonale ?
&lt;/h2&gt;

&lt;p&gt;L'Architecture Hexagonale est une façon de structurer une application pour qu’elle soit &lt;strong&gt;&lt;u&gt;indépendante des détails externes&lt;/u&gt;&lt;/strong&gt;. Elle insiste sur la séparation des préoccupations en découpant l'application en plusieurs couches distinctes.&lt;/p&gt;

&lt;p&gt;Le but est de rendre l'application indépendante des technologies spécifiques, en organisant les interactions entre le cœur de l'application (la logique métier) et le monde extérieur (base de données, interfaces utilisateur, API, frameworks, etc.), tout ça via des &lt;strong&gt;Ports&lt;/strong&gt; et des &lt;strong&gt;Adapters&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le concept de base
&lt;/h3&gt;

&lt;p&gt;Imagine ton application comme un hexagone avec, au centre, le &lt;strong&gt;domain&lt;/strong&gt; (la logique métier). Ce cœur métier &lt;strong&gt;&lt;u&gt;ne doit pas&lt;/u&gt;&lt;/strong&gt; être directement lié aux détails d'implémentation (comme Symfony, Doctrine, une API tierce, etc.). Pour y arriver, il va falloir créer des interfaces appelées &lt;code&gt;Ports&lt;/code&gt;, et ces interfaces vont être implémentées par des &lt;code&gt;Adapters&lt;/code&gt;, qui s'occupent de la &lt;strong&gt;communication&lt;/strong&gt; avec les &lt;strong&gt;services externes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;🛂 &lt;strong&gt;Ports&lt;/strong&gt; : autrement dit, les interfaces par lesquelles le monde extérieur peut interagir avec le noyau métier. Les &lt;code&gt;ports&lt;/code&gt;, ce sont les points d’entrée ou de sortie de l’application (API, requêtes HTTP, commandes CLI, etc.).&lt;/p&gt;

&lt;p&gt;🎯 &lt;strong&gt;Adapters&lt;/strong&gt; : les &lt;code&gt;adapters&lt;/code&gt; sont les implémentations concrètes de ces interfaces. Ils traduisent les demandes ou réponses externes en actions compréhensibles pour la logique métier. Cela inclut les interactions avec les bases de données, les frameworks, les API, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure d'une application hexagonale
&lt;/h2&gt;

&lt;p&gt;Pour mieux comprendre, on va se baser sur un exemple simple en Symfony. Imaginons que ton application doit permettre à des utilisateurs de &lt;strong&gt;passer des commandes&lt;/strong&gt;, et qu'elle doit interagir avec une base de données pour stocker ces commandes.&lt;/p&gt;

&lt;p&gt;Eh bien, regarde comment tu pourrais structurer ton application selon l'architecture hexagonale. 👀&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Le Cœur Métier
&lt;/h3&gt;

&lt;p&gt;C’est le &lt;strong&gt;noyau&lt;/strong&gt; de ton application, la partie qui n'a &lt;strong&gt;&lt;u&gt;aucune dépendance externe&lt;/u&gt;&lt;/strong&gt;. C’est là que réside la &lt;strong&gt;logique métier&lt;/strong&gt;, comme la création d’une commande, la validation de ses données, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0z335i01ix3eeloi1xqm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0z335i01ix3eeloi1xqm.png" alt="PHP Order class hexa" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ici, la classe &lt;code&gt;Order&lt;/code&gt; représente le modèle de base d'une commande. Tu remarqueras que cette classe ne sait rien de la manière dont elle sera stockée, ni d’où viennent les données.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Les Ports (Interfaces)
&lt;/h3&gt;

&lt;p&gt;Les &lt;code&gt;Ports&lt;/code&gt; définissent les &lt;strong&gt;interfaces&lt;/strong&gt; que les &lt;code&gt;adaptateurs&lt;/code&gt; vont devoir implémenter pour que la logique métier puisse fonctionner. Ils peuvent être des &lt;strong&gt;&lt;u&gt;points d'entrée&lt;/u&gt;&lt;/strong&gt; dans le système (comme des requêtes HTTP) ou des &lt;strong&gt;&lt;u&gt;points de sortie&lt;/u&gt;&lt;/strong&gt; (comme les appels à la base de données).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxj5iyx2rxg9fxy5m1fsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxj5iyx2rxg9fxy5m1fsa.png" alt="PHP OrderRepositoryInterface hexa" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;L'interface &lt;code&gt;OrderRepositoryInterface&lt;/code&gt; va servir de port de sortie. Elle ne fait que &lt;strong&gt;définir les méthodes&lt;/strong&gt; dont la logique métier a besoin pour interagir avec la base de données. Le domaine &lt;strong&gt;&lt;u&gt;ne sait pas&lt;/u&gt;&lt;/strong&gt; comment ces données vont être sauvegardées.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Les Adapters (Implémentations)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;L'Adapter&lt;/code&gt;, c'est tout simplement l'implémentation concrète d'un &lt;code&gt;Port&lt;/code&gt;. L'Adapter va permettre de traduire : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;toutes les interactions externes vers la logique métier&lt;/li&gt;
&lt;li&gt;la logique métier vers des actions concrètes (comme sauvegarder dans une base de données).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqic52zcfdsnrbgy3yy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqic52zcfdsnrbgy3yy0.png" alt="PHP DoctrineOrderRepository hexa" width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L’adapter&lt;/strong&gt; ici traduit l’appel à &lt;code&gt;OrderRepositoryInterface&lt;/code&gt; en une interaction avec Doctrine (bibliothèque de gestion de base de données) mais ta logique métier n'a aucune idée que Doctrine est utilisé.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Un exemple de contrôleur dans Symfony
&lt;/h3&gt;

&lt;p&gt;Le contrôleur va agir comme un &lt;strong&gt;&lt;u&gt;point d’entrée&lt;/u&gt;&lt;/strong&gt; pour notre application. C’est lui qui interagit avec l’utilisateur via les requêtes HTTP, puis délègue les tâches au domaine via les &lt;strong&gt;Ports&lt;/strong&gt; et &lt;strong&gt;Adapters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3rzjnmudoh09puzt96q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3rzjnmudoh09puzt96q.png" alt="Symfony OrderController hexa" width="800" height="696"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ici, tu peux voir que le contrôleur ne connaît que l’interface &lt;code&gt;OrderRepositoryInterface&lt;/code&gt; et pas l’&lt;strong&gt;implémentation concrète&lt;/strong&gt;. Ca permet de rendre le code testable, extensible, et indépendant des frameworks et technologies externes (Symfony, Doctrine, etc.).&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi utiliser l'Architecture Hexagonale ?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;En séparant ta logique métier des implémentations externes (base de données, HTTP, API externes), tu rends ton code plus flexible. Changer de base de données ou de framework devient plus simple et moins risqué.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comme la logique métier n'est pas liée à des éléments externes, tu peux facilement mock ou remplacer les adaptateurs dans tes tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avec cette séparation claire, il est plus facile d'ajouter de nouvelles fonctionnalités, de modifier des comportements ou de réutiliser certains composants sans toucher à la logique métier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tu peux réutiliser le même cœur métier avec différents types d'adaptateurs. Par exemple, la même logique métier peut être utilisée pour une application web, une API REST ou encore une interface en ligne de commande.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mais attention !
&lt;/h2&gt;

&lt;p&gt;Même si l'&lt;strong&gt;architecture hexagonale&lt;/strong&gt; est puissante, structurer ton application de cette façon peut être un peu overkill, surtout pour des petits projets. Pour des petites applications, l'architecture hexagonale peut ajouter une complexité inutile, car tout découpler n’est pas toujours nécessaire.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>cleanarchi</category>
      <category>php</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Simplifier la gestion des permissions avec le Pattern Decorator en PHP et Symfony</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Tue, 10 Sep 2024 11:35:02 +0000</pubDate>
      <link>https://dev.to/technivek/simplifier-la-gestion-des-permissions-avec-le-pattern-decorator-en-php-et-symfony-29h0</link>
      <guid>https://dev.to/technivek/simplifier-la-gestion-des-permissions-avec-le-pattern-decorator-en-php-et-symfony-29h0</guid>
      <description>&lt;p&gt;Gérer les permissions dans une application peut vite devenir compliqué, surtout quand tu dois ajouter des couches d’autorisations supplémentaires au fil du temps. Plutôt que d’avoir une classe géante qui gère tous les types d’autorisations possibles, le Pattern Decorator te permet d’ajouter ou modifier dynamiquement les permissions en empilant des décorateurs. Intéressant non ?&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi le Pattern Decorator ?
&lt;/h2&gt;

&lt;p&gt;Le Pattern Decorator permet d'enrichir un objet avec de nouvelles fonctionnalités sans modifier sa structure de base. Dans le cas de la gestion des permissions, il te permet d’ajouter des règles ou des couches d’autorisation au fur et à mesure que tu en as besoin. Et le gros avantage, c’est que tu n’as pas à modifier ton code existant à chaque fois que tu ajoutes une nouvelle règle.&lt;/p&gt;

&lt;p&gt;En gros, tu peux partir d’un objet simple et lui « coller » des fonctionnalités au fur et à mesure que tu en as besoin. Tu ne me crois toujours pas ? Check la suite !&lt;/p&gt;

&lt;h2&gt;
  
  
  Exemple concret : Gestion des permissions utilisateur
&lt;/h2&gt;

&lt;p&gt;Disons que ton application doit gérer différents types d'utilisateurs (admin, éditeur, lecteur). Chaque utilisateur a des permissions différentes : un admin peut tout faire, un éditeur peut modifier du contenu, et un lecteur ne peut que consulter.&lt;/p&gt;

&lt;p&gt;Plutôt que d'enfermer ces règles dans une énorme classe conditionnelle, on va utiliser le Pattern Decorator pour gérer les permissions d'accès dynamiquement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 1 : L'interface de base
&lt;/h3&gt;

&lt;p&gt;On va commencer par définir une interface PermissionInterface qui représente le concept de "permission". Chaque permission va offrir la méthode checkAccess(), qui vérifie si l'utilisateur a accès à une ressource spécifique.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jlk7aswg8t4by3ussci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jlk7aswg8t4by3ussci.png" alt="PHP PermissionInterface" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 2 : La classe de base (utilisateur sans privilèges)
&lt;/h3&gt;

&lt;p&gt;Ensuite, on va créer une classe de base pour un utilisateur avec un accès basique, sans permissions spécifiques.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp4qsha283aydfqqfhnl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgp4qsha283aydfqqfhnl.png" alt="PHP BasicUser class" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 3 : Les décorateurs pour les permissions
&lt;/h3&gt;

&lt;p&gt;Et maintenant, on va créer des décorateurs pour ajouter différentes permissions à notre utilisateur. Par exemple, un décorateur pour un utilisateur avec un accès administrateur :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzpqk40q7or5uzsuwhs9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzpqk40q7or5uzsuwhs9.png" alt="PHP AdminPermissionDecorator" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tu commences à comprendre le principe... On va créer un autre décorateur pour un éditeur, qui peut seulement modifier du contenu, mais pas tout :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjknplhmtxnxqgsw88g8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjknplhmtxnxqgsw88g8.png" alt="PHP EditorPermissionDecorator" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et enfin, on peut avoir un décorateur pour un utilisateur en mode lecture seule (comme un lecteur) :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfp209tu6knxy89kaum9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfp209tu6knxy89kaum9.png" alt="PHP ReadOnlyPermissionDecorator" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 4 : Utilisation dans Symfony
&lt;/h3&gt;

&lt;p&gt;Il est temps d'utiliser ces magnifiques décorateurs dans un contrôleur Symfony pour gérer les permissions d’un utilisateur en fonction de son rôle.&lt;/p&gt;

&lt;p&gt;On va utiliser un cas classique : un utilisateur veut accéder à la page d'édition d'une page (ou d'un article, peu importe). Selon ses permissions (admin, éditeur, etc.), il peut avoir accès ou non à cette page. C'est ici que l'empilement des permissions avec le PatternDecorator va intervenir.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2bvg8tbzrzudt7x8wul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg2bvg8tbzrzudt7x8wul.png" alt="Symfony PageController" width="800" height="922"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Création de l'utilisateur : On commence par un utilisateur basique sans permissions particulières (BasicUser).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ajout des permissions via les décorateurs : À chaque étape, tu peux "décorer" cet utilisateur en lui ajoutant des permissions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Empilement des permissions : Chaque décorateur s'ajoute dynamiquement à l'utilisateur, enrichissant ses droits d'accès. &lt;br&gt;
Au final, le système va vérifier toutes les permissions cumulées via les décorateurs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Les avantages du Pattern Decorator
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gestion flexible des permissions&lt;/strong&gt; : Grâce au Pattern Decorator, tu peux facilement ajouter ou retirer des permissions sans toucher au code existant. Si demain tu ajoutes un nouveau type de rôle, il te suffit de créer un nouveau décorateur.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Meilleure séparation des responsabilités&lt;/strong&gt; : Chaque rôle ou permission est encapsulé dans une classe distincte. Le code est donc plus clair, mieux organisé, et facile à maintenir.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extension facile&lt;/strong&gt; : Le jour où tu as besoin d’un rôle supplémentaire (par exemple, un modérateur), tu crées simplement un nouveau décorateur et le reste de ton système de gestion des permissions reste intact.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mais prudence
&lt;/h2&gt;

&lt;p&gt;Même si ce pattern est très pratique, fais attention à ne pas abuser du nombre de décorateurs. Trop de couches peuvent rendre ton code plus difficile à suivre. Assure-toi que chaque nouvelle permission ajoute vraiment de la valeur et évite de complexifier inutilement ton architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  En résumé
&lt;/h2&gt;

&lt;p&gt;Le Pattern Decorator est une solution idéale pour gérer des permissions de façon dynamique dans une application Symfony. En empilant des décorateurs, tu peux ajouter des rôles et des règles d'accès de manière modulaire, tout en gardant ton code propre et facile à maintenir.&lt;/p&gt;

&lt;p&gt;Si tu as apprécié cet article, n'hésite pas à me suivre et commenter !&lt;/p&gt;

</description>
      <category>designpatterns</category>
      <category>php</category>
      <category>todayilearned</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Optimiser la gestion des comportements avec le Pattern Strategy en PHP et Symfony</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Mon, 09 Sep 2024 15:24:04 +0000</pubDate>
      <link>https://dev.to/technivek/simplifier-la-gestion-des-comportements-avec-le-pattern-strategy-en-php-et-symfony-51km</link>
      <guid>https://dev.to/technivek/simplifier-la-gestion-des-comportements-avec-le-pattern-strategy-en-php-et-symfony-51km</guid>
      <description>&lt;p&gt;Parfois, tu as un morceau de code qui peut se comporter différemment selon la situation. Par exemple, tu peux avoir besoin de calculer un tarif différemment en fonction du type de client, ou appliquer un algorithme de tri spécifique. Au lieu d'enfermer ces comportements dans de gros &lt;code&gt;if&lt;/code&gt; ou des &lt;code&gt;switch&lt;/code&gt;, tu peux utiliser le &lt;strong&gt;Design Pattern Strategy&lt;/strong&gt; pour mieux structurer ton code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi le Pattern Strategy ?
&lt;/h2&gt;

&lt;p&gt;Le Pattern Strategy te permet de séparer la logique d'action du reste du code, pour que tu puisses facilement changer ou &lt;strong&gt;remplacer cette logique sans avoir à modifier tout ton programme&lt;/strong&gt;. Si tu as plusieurs façons d’exécuter une action, c’est ici que ce pattern devient très utile. Plutôt que d'avoir un gros bloc conditionnel, tu délègues le comportement à un objet spécifique.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exemple concret : Calcul de réduction en fonction du type de client
&lt;/h2&gt;

&lt;p&gt;Imaginons que tu développes une boutique en ligne et que tu aies besoin de calculer une réduction pour différents types de clients (standard, premium, VIP). Au lieu d’écrire des conditions pour chaque type de client un peu partout dans ton code, tu vas pouvoir utiliser le &lt;strong&gt;Pattern Strategy&lt;/strong&gt; pour encapsuler chaque calcul de réduction dans une classe dédiée.&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 1 : Interface de la stratégie
&lt;/h3&gt;

&lt;p&gt;On va commencer par créer une interface &lt;strong&gt;DiscountStrategy&lt;/strong&gt; qui définit une méthode &lt;code&gt;calculate()&lt;/code&gt; que chaque stratégie de calcul de réduction devra implémenter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhshzdhdnn27ozt2whrfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhshzdhdnn27ozt2whrfm.png" alt="PHP DiscountStrategyInterface" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 2 : Stratégies spécifiques
&lt;/h3&gt;

&lt;p&gt;Ensuite, on va créer les différentes stratégies de calcul de réduction. Par exemple, pour les clients standards, il n’y a pas de réduction :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14htn46m6pslu0ph8w2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14htn46m6pslu0ph8w2b.png" alt="PHP NoDiscountStrategy class" width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour les clients premium, on va appliquer une réduction de 10 % :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4z26ab282m9ed1itv5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4z26ab282m9ed1itv5p.png" alt="PHP PremiumDiscountStrategy class" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et pour les clients VIP, une énorme réduction de 20 % :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vhbqqseaeq55o6qucdj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vhbqqseaeq55o6qucdj.png" alt="PHP VIPDiscountStrategy class" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 3 : Classe de contexte
&lt;/h3&gt;

&lt;p&gt;Maintenant, on va créer une classe qui utilisera ces stratégies. Cette classe ne se préoccupe pas du type de réduction à appliquer, elle délègue cette logique à la stratégie qu’on lui passe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ewxe5hyztbvsiqfluo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg5ewxe5hyztbvsiqfluo.png" alt="PHP DiscountContext class" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 4 : Utilisation dans Symfony
&lt;/h3&gt;

&lt;p&gt;Génial ! On va voir maintenant comment on peut intégrer tout ça dans un contrôleur Symfony. Tu vas choisir une stratégie en fonction du type de client, mais toute la logique du calcul reste dans les classes de stratégie, ce qui rend ton contrôleur bien plus simple et propre.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lhxudrgglc2mbxpqzno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lhxudrgglc2mbxpqzno.png" alt="Symfony DiscountController" width="800" height="735"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que ça t'apporte
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibilité&lt;/strong&gt; : Grâce au Pattern Strategy, tu peux facilement ajouter ou modifier des comportements sans toucher à ton code principal. Il te suffit d’ajouter une nouvelle stratégie de calcul.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code propre et lisible&lt;/strong&gt; : Ton code devient beaucoup plus lisible en éliminant les blocs de &lt;code&gt;if&lt;/code&gt; ou &lt;code&gt;switch&lt;/code&gt; massifs. Chaque stratégie a sa propre classe avec une seule responsabilité, ce qui respecte le &lt;strong&gt;Single Responsibility Principle (SRP)&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Évolutivité&lt;/strong&gt; : Si un jour, tu dois ajouter une réduction pour les super VIP avec une réduction indécente de 50 %, tu crées simplement une nouvelle stratégie et le reste de ton code ne change pas !&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  En résumé
&lt;/h2&gt;

&lt;p&gt;Le Pattern Strategy est un excellent choix lorsque tu as plusieurs façons d’exécuter une même action et que tu veux que ton code reste flexible et évolutif. Plutôt que de te retrouver avec des conditions à n’en plus finir, tu encapsules les comportements dans des classes dédiées, ce qui te permet de les interchanger facilement.&lt;/p&gt;

&lt;p&gt;Et toi tu as déjà utilisé le Pattern Strategy dans tes projets Symfony ? N'hésite pas à commenter cet article !&lt;/p&gt;

</description>
      <category>designpatterns</category>
      <category>softwareengineering</category>
      <category>php</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Comprendre le Design Pattern Factory en PHP et Symfony</title>
      <dc:creator>TechniveK</dc:creator>
      <pubDate>Mon, 09 Sep 2024 11:18:19 +0000</pubDate>
      <link>https://dev.to/technivek/comprendre-le-pattern-design-factory-en-php-avec-symfony-59bp</link>
      <guid>https://dev.to/technivek/comprendre-le-pattern-design-factory-en-php-avec-symfony-59bp</guid>
      <description>&lt;p&gt;Si tu cherches à rendre ton code plus flexible et maintenable, le Design Pattern Factory est une excellente solution. Il te permet de déléguer la création d’objets à une méthode spécialisée, ce qui peut être super utile lorsque tu dois gérer différents types d’objets qui partagent une interface commune.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi choisir le Pattern Factory ?
&lt;/h2&gt;

&lt;p&gt;Imaginons que tu as plusieurs types d'objets à instancier dans ton projet, chacun avec sa propre logique spécifique. Si tu commences à multiplier les &lt;code&gt;new ClassName()&lt;/code&gt; partout, ton code devient rapidement difficile à maintenir. Et c'est là qu'intervient le &lt;strong&gt;Pattern Factory&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tu vas voir, c'est très simple : au lieu d'instancier directement un objet avec &lt;code&gt;new&lt;/code&gt;, tu passes par une &lt;strong&gt;méthode factory&lt;/strong&gt; qui se charge de choisir et de créer l’objet qu’il te faut. Ça permet de &lt;u&gt;découpler la logique de création&lt;/u&gt; du reste de ton code. Plus facile à maintenir, plus flexible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exemple concret : Notification Factory
&lt;/h2&gt;

&lt;p&gt;Imaginons une application qui envoie des notifications. Tu pourrais avoir besoin d’envoyer des emails, des SMS, ou des notifications push. Plutôt que d’ajouter plein de &lt;code&gt;if&lt;/code&gt; dans ton code pour savoir quelle classe instancier, tu délègues ça à une &lt;strong&gt;factory&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 1 : Interface commune
&lt;/h3&gt;

&lt;p&gt;D'abord, tu vas définir une interface que chaque type de notification doit implémenter. Chaque notification doit avoir une méthode &lt;code&gt;send()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy40v1elc4gq1q92ah0iz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy40v1elc4gq1q92ah0iz.png" alt="PHP NotificationInterface" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 2 : Implémentations spécifiques
&lt;/h3&gt;

&lt;p&gt;Chaque type de notification a sa propre classe, qui implémente cette interface. Par exemple, pour envoyer un email :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjek3mnelymnp9qsi094j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjek3mnelymnp9qsi094j.png" alt="PHP EmailNotification class" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pareil pour les SMS :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrwutl0jwgkrphfekbnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrwutl0jwgkrphfekbnf.png" alt="PHP SMSNotification class" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et pour les notifications push :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40c1y5qg0wxsej6wl01w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40c1y5qg0wxsej6wl01w.png" alt="PHP PushNotification class" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 3 : La Factory
&lt;/h3&gt;

&lt;p&gt;Maintenant, on va créer la &lt;strong&gt;factory&lt;/strong&gt;. C’est elle qui va décider quelle notification instancier selon le type que tu lui passes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8i7k6woxs7q98kpwpv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8i7k6woxs7q98kpwpv7.png" alt="PHP NotificationFactory class" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Étape 4 : Utilisation dans Symfony
&lt;/h3&gt;

&lt;p&gt;Et voilà comment tu peux utiliser cette factory dans un contrôleur Symfony. Plutôt que d’écrire une tonne de &lt;code&gt;if&lt;/code&gt; pour savoir quel type de notification envoyer, tu laisses la factory décider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxezm162mxh2owd2rgmh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxezm162mxh2owd2rgmh4.png" alt="Symfony NotificationController" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que ça t’apporte
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Séparation des préoccupations&lt;/strong&gt; : Le contrôleur n’a pas à connaître la logique de création des notifications. Il s'occupe juste de son boulot : envoyer un message. Tout le reste est géré par la factory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Facilité de maintenance&lt;/strong&gt; : Si un jour tu dois ajouter un nouveau type de notification (par exemple une notification via Slack), il te suffit d’ajouter une nouvelle classe et de l’intégrer à la factory. Tu n’as pas à toucher au reste du code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensibilité&lt;/strong&gt; : Le Design Pattern Factory te permet d’ajouter facilement de nouveaux types de notifications sans casser ce qui fonctionne déjà. C’est du pur &lt;strong&gt;Open/Closed Principle&lt;/strong&gt; du SOLID : ton code est ouvert à l’extension, mais fermé à la modification.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Mais attention !
&lt;/h2&gt;

&lt;p&gt;Le &lt;strong&gt;Pattern Factory&lt;/strong&gt; peut ajouter un peu de complexité, surtout dans des projets simples où un &lt;code&gt;new&lt;/code&gt; classique ferait largement l'affaire. Il faut donc savoir l’utiliser &lt;strong&gt;quand c’est vraiment pertinent&lt;/strong&gt;. Si tu sais que ton projet va évoluer et que tu vas devoir ajouter de nouveaux types d'objets régulièrement, c'est un excellent choix.&lt;/p&gt;

&lt;h2&gt;
  
  
  En résumé
&lt;/h2&gt;

&lt;p&gt;Le &lt;strong&gt;Design Pattern Factory&lt;/strong&gt; te permet de centraliser la création d’objets et de rendre ton code plus flexible. En déléguant la création d'objets à une méthode dédiée, tu facilites la maintenance et tu prépares ton projet à évoluer. Ce pattern s'intègre très bien dans un projet Symfony et peut vraiment t’aider à garder ton code propre et organisé.&lt;/p&gt;

&lt;p&gt;Alors, prêt à intégrer ce pattern dans ton prochain projet Symfony ? Si tu as déjà utilisé le Factory, n’hésite pas à partager ton expérience. Toujours curieux de savoir comment les autres s’y prennent !&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>designpatterns</category>
      <category>todayilearned</category>
      <category>php</category>
    </item>
  </channel>
</rss>
