Angular Enterprise
Bonnes pratiques en Angular
- Cours
- 17 pratiques
- 5 min de lecture
Ici, nous avons rassemblé une liste des meilleures pratiques à suivre si vous voulez faire une bonne application avec une base de code propre et maintenable. Il est également important de connaître les mauvaises pratiques à éviter en premier, mais ensuite la liste des bonnes pratiques vous aidera à faire passer votre application au niveau supérieur de qualité.
- adopter une convention de dénomination stricte et intelligente pour les fichiers et les dossiers, par exemple créer des dossiers:
containers, components, models, mocks, configs, services, factories, utils, guards, pipes...dans chaque fonctionnalité. Puis suffixez chaque fichier dans ces dossiers avec le nom du dossier au singulier. - regrouper les fichiers de modèle car ils sont généralement simples et faciles à lire, il est donc préférable d'éviter de créer un fichier par modèle, mais de regrouper les modèles par types de modèles. Par exemple pour une fonctionnalité de produit:
product.entity.model+product.state.model+product.payload.model, ainsi il est facile de trouver un type de modèle et de lire tous les modèles associés dans un seul fichier. - utilisation de base components et base services afin de faciliter le développement et la maintenance (migration, tests/mocking ...), il est préférable de centraliser ces dépendances à l'aide de wrappers. Exemples de base components :
BasePresentationalComponent, BaseContainerComponent, BaseModuleComponent. Exemples de base services :BaseNavigationService(pour le proxy de toute la navigation de route et la gestion du retour arrière),BaseHttpService(pour le proxy de toutes les requêtes http),BaseUiService(pour le proxy de toutes les interactions UI telles que modal, toaster...),BaseStoreService(pour le proxy de tous les services de gestion d'état). - gérer les race conditions pour les opérations asynchrones telles que les requêtes http, en effet l'utilisateur peut cliquer plusieurs fois sur exactement le même bouton ou un autre bouton similaire dans une liste et entre-temps le réseau peut être lent, donc la requête ne se terminera pas assez rapidement et cela déclenchera un comportement inattendu dans l'application. Les opérateurs RxJS tels que
switchMap,exhaustMapouconcatMappermettent de choisir explicitement comment les requêtes concurrentes sont gérées. - créer vos composants de présentation avec une implémentation polymorphe afin de pouvoir réutiliser les mêmes composants mais avec une interface différente.
- éviter de dupliquer les objets dans le store et préférer l’utilisation d’identifiant de référence et des sélecteurs afin d’avoir un store plus léger et plus robuste, en effet le risque de désynchronisation augmente si vous avez plusieurs fois le même objet à des endroits différents. Il est également recommandé de garder votre structure d'état à plat, par exemple avec
@ngrx/entityou la fonctionnalitéwithEntitiesdu SignalStore de NgRx. - utiliser le pattern de programmation appelé « strategy pattern » afin d’éviter de se retrouver avec des switch case un peu partout dans l’application. Il est donc recommandé de s’appuyer sur le polymorphisme (une interface ou une classe abstraite commune avec plusieurs implémentations fournies via l’injection de dépendances) pour les composants mais aussi pour les services.
- utilisation de
feature toggledans les fichiers d'environnement ou à partir d'un endpoint de configuration de votre API afin de pouvoir activer ou désactiver de nouvelles fonctionnalités dans chaque environnement, cela peut être très utile si une fonctionnalité est boguée ou non terminée. - utilisation de
templatespour donner les bonnes pratiques à suivre et une liste de todos pour lesmerge requestset lestickets(GitLab, GitHub, Jira...). Utilisez autant que possible des templates génériques afin que l'équipe s'y habitue et commence naturellement à suivre ces bonnes pratiques. - améliorer la testabilité en créant une porte dérobée pour tester tous les différents templates conditionnels dans l'interface utilisateur.
- tester l'écran avec tout type de données, par exemple un texte court et un texte long afin de vérifier le responsive de la mise en page.
- extraire la logique des templates conditionnels dans une fonction du composant, par exemple si vous devez afficher un texte en fonction de nombreuses conditions, créez une fonction utilisant
switch(true)pour gérer cela. Dans certains cas, si le template est assez grand, vous pouvez utiliser le bloc@switch(ngSwitchdans les anciennes versions) ouNgComponentOutletafin d'afficher différents composants en fonction de la condition. - au lieu d'utiliser
::ng-deeppour remplacer le style des composants enfants depuis le composant parent, une meilleure approche consiste à utiliser des variables CSS, c'est une approche plus propre, maintenable et évolutive. - générer des DTO qui correspondent à 100% au service back-end, idéalement automatiquement à partir de la spécification OpenAPI (Swagger), et ne jamais les modifier pour pouvoir les régénérer automatiquement à l'avenir. Créez ensuite manuellement un modèle front-end pour la sortie du service qui étendra probablement le DTO et formatera les champs ou même ajoutera des champs supplémentaires utiles sur le front-end. Ainsi, votre DTO correspondra toujours au backend et votre modèle sera toujours disponible pour ajouter de nouveaux champs supplémentaires sur le front-end sans gâcher le DTO avec des champs facultatifs. En plus de cela, vous devriez utiliser
readonlysur tous les champs ou un type globalDeepReadonlyafin de vous assurer que vos données sont immuables. - mettre en place une mock factory à utiliser dans tous les types de tests : unitaires ou e2e. Cette mock factory est une simple fonction qui retourne un objet modèle par défaut et prend un seul paramètre pour surcharger partiellement l'objet modèle.
- privilégier les composants standalone, la fonction
inject()et les signaux (signal,computed,input,output) pour le nouveau code, et utiliser la stratégie de détection des changementsOnPush(ou la détection des changements zoneless) partout pour garder un rendu prévisible et rapide. - utiliser le control flow intégré (
@if,@foravectrack,@switch) et les blocs@deferau lieu des anciennes directives structurelles afin d'obtenir des bundles plus petits et de meilleures performances.