Préface

Le but de ce document est d'établir des guidelines pour améliorer l'efficacité, la maintenabilité et la cohérence du code.
Pour atteindre ce but, ce document définit des principles directeurs, une organisation de fichiers, et des règles d'architecture.

Pour voir les bonnes pratiques et les conventions à utiliser, veuillez consulter le document suivant : Angular Standard

Principes directeurs

Stack Driven

Adoptez une approche drivée par la stack pour garantir une base solide et un code durable.

Pour appliquer le principe "Stack Driven" dans un projet Angular, voici deux points clés à développer :

  • Maitrisez la stack (HTML, CSS, JS, TS)

    La stack, c'est la base !
    En effet, le HTML, le CSS, le JavaScript et TypeScript constituent les fondations de votre application Angular. Il est crucial de maîtriser ces technologies pour assurer une intégration fluide et efficace avec le framework.

  • Standardiser l'utilisation du framework Angular

    En alignant l'équipe avec les standards et les meilleures pratiques d’Angular, vous assurez la création d’un code robuste et maintenable. De plus, la standardisation facilite la collaboration entre les développeurs et assure une uniformité dans la codebase.

    Il est également possible de normaliser l'utilisation des features d'Angular pour produire un code durable aligné avec l'écosystème.
    Voici quelques conseils :

    • Utilisez les inputs et outputs : pour communiquer entre les composants parents/enfants.
    • Utilisez des pipes : pour transformer les données à afficher (ex. dates, nombres).
    • Utilisez des directives : pour partager des comportements liés aux éléments HTML.
    • Utilisez des guards : pour contrôler l'accès aux routes/zones de votre application.
    • Utilisez des interceptors : pour centraliser la logique des requêtes HTTP.
    • Utilisez des resolvers uniquement : pour charger des données avant l'affichage. Si vous souhaitez gérer le chargement (spinner, skeleton), alors utilisez un service.

    Documentation : Angular Architecture Guide

KISS

On pourrait résumer ce principe en 1 phrase: Les solutions les plus simples sont souvent les meilleures.

Le principle KISS peut être traduit de plusieurs manières: 'Keep it Short and Simple', 'Keep it Simple and Straightforward', 'Keep It Simple and Stupid', ...
D'un point de vue programmation, un code simple se matérialise par un code clean.

Pour appliquer le principe KISS dans un projet Angular, voici quelques conseils :

  • Évitez l'abstraction prématurée du code. En d'autres mots, évitez de prédire les besoins futurs et limitez exclusivement l'abstraction aux parties réutilisables du code clairement identifiées.
  • Adoptez une approche déclarative. Préférez une approche déclarative et non impérative, ce qui rend le code plus lisible et plus facile à maintenir.
  • Préférez les solutions natives avant d'utiliser une dépendance tierce. En évitant les dépendances tierces, vous réduisez la complexité de votre application et limitez la dette technique.

Documentation: Keep It Simple, Stupid (KISS)

Single Responsability Principle

Le principe de responsabilité unique peut être résumé en une phrase: Un composant, une classe, une fonction ne devrait avoir qu'une seule raison de changer.
C'est un principe fondamental de l'ingénierie logicielle qui vise à réduire la complexité du code en le divisant en unités plus petites et plus simples.

Application du principe SRP dans une application Angular :

  • Au niveau composant:
    • Factorisez vos composants en unités autonomes (voir CDD).
    • Suivez le pattern smart/dumb component.
    • Définissez uniquement la logique de présentation dans le composant.
  • Au niveau service:
    • Utilisez des services pour déléguer la logique métier.
    • Utilisez des services pour manager la gestion de données partagées.
    • Créez des services pour centraliser les appels HTTP aux apis.

Try to be DRY

Privilégiez la réutilisabilité du code sans sacrifier la clarté/lisibilité. Évitez d'avoir un code générique avec des conditions mutuellement exclusives. Cela rend le code complexe, difficile à tester et à modifier.
Si vous vous trouvez dans cette situation, refactorisez en plusieurs éléments distincts. Même s'il y a une duplication partielle, cela est souvent préférable à un code trop générique.

Documentation: T-DRY (Try to be DRY)

Component-Driven Development

Le développement piloté par composants (CDD) est une méthodologie qui se concentre sur la création et l'assemblage de composants réutilisables pour bâtir l'application.
Cette approche encourage la création de petits composants autonomes pouvant être facilement réutilisés et combinés.

Documentation: Component Driven User Interfaces
An overview of component driven development and atomic design principles
Why Component driven development?
Component-Driven Development
A Guide to Component Driven Development (CDD)

Smart/Dumb components

Le pattern smart/dumb components dans un projet Angular contribue à un code organisé en séparant la gestion du state et le code de présentation.
Les composants dumb se concentrent uniquement sur la présentation et le rendu de l'UI. Ils reçoivent leurs données via les inputs et émettent des événements via les outputs.

Les composants smart gèrent le state et sont par nature plus spécifiques à l'application, jouant souvent le rôle de contrôleur (accès aux services, fetching de données, etc.). Les pages de l'application sont ainsi souvent des composants smart.

Documentation: Smart Components vs Presentational Components

La structure du projet

La structure LIFT

La structure proposée s'appuie sur la recommandation officielle LIFT.
Do structure the application such that you can Locate code quickly, Identify the code at a glance, keep the Flattest structure you can, and Try to be DRY.
  • Locate:

    Organisez le code pour que la localisation des éléments soit intuitive, simple et rapide.

  • Identify:

    Nommez le fichier pour identifier instantanément ce qu'il représente et ce qu'il contient. Pour ce faire, le nom du fichier doit être descriptif. Évitez les fichiers mélangeant plusieurs composants ou services.

  • Flat:

    Conservez la structure la plus à plat aussi longtemps que possible. Envisagez de créer des sous-dossiers lorsqu'un dossier atteint sept fichiers ou plus.

  • T-DRY (Try to be DRY):

    Respectez le principe DRY. Évitez toutefois de sacrifier la lisibilité juste pour suivre à la lettre le principe DRY.

L'arborescence des fichiers

  • api
    • models
      • entity-a.api.model.ts
      • entity-b.api.model.ts
    • services
      • domain-a.api.service
      • domain-b.api.service
  • app
    • components
      • ui
        • shared-button
          • shared-button.component.html|scss|ts
    • constants
      • constant-a.ts
      • constant-b.ts
    • directives
      • shared-directive.ts
    • enums
      • enum-a.ts
      • enum-b.ts
    • features
      • feature-a
        • components
          • scoped-component-a
            • scoped-component-a.component.html|scss|ts
          • scoped-component-b
            • scoped-component-b.component.html|scss|ts
        • models
          • scoped-model-a.model.ts
          • scoped-model-b.model.ts
        • pages
          • page-a
            • page-a.page.html|scss|ts
          • page-b
            • page-b.page.html|scss|ts
        • services
          • scoped-service-a.service.ts
          • scoped-service-b.service.ts
        • feature-a.component.html|scss|ts
        • feature-a.routes.ts
    • guards
      • auth.guard.ts
      • module-import.guard.ts
      • no-auth.guard.ts
    • helpers
      • helper-a.ts
      • helper-b.ts
    • interceptors
      • token.interceptor.ts
      • error.interceptor.ts
    • models
      • model-a.ts
      • model-b.ts
    • services
      • service-a.service.ts
      • service-b.service.ts
    • pages
      • page-not-found
        • page-not-found.page.html|scss|ts
    • pipes
      • shared-pipe.ts
    • app.component.html|scss|ts
    • app.routes.ts
  • assets
    • i18n
      • en.json
      • fr.json
      • nl.json
    • documents
      • file-a.pdf
      • file-b.pdf
    • icons
      • custom-icon-a.svg
      • custom-icon-b.svg
    • images
      • image-a.svg
      • image-b.svg
  • favicon.icon
  • index.html
  • maint.ts
  • styles.scss

Tous vos composants, vos services, directives, ... doivent être déclarés en tant que standalone.

Suivez l'approche CDD (Component-Driven Development) pour organiser vos composants.

Adoptez le on push change detection strategy pour améliorer les performances de votre application.

Utilisez la nouvelle syntaxe offert par le control-flow pour simplifier et booster la lisibilité de votre code.

Organisez votre code par feature. Cela permet de regrouper les composants, services, modèles et autres par domaine.

Alignez vos features sur vos routes.
Cela permet de faciliter la navigation dans votre application.

Séparez le code propre de vos apis dans un dossier distinct de votre application.

Regroupez vos composants de base partagés dans un sous-dossier ui.

Si vous avez des pages de CRUD, créez des pages distinctes pour la création, l'édition et le détail.
Cela permet de séparer les responsabilités et de rendre le code plus facile à maintenir.
Note: Créez un composant de formulaire réutilisable partagé entre la création et l'édition.

Exemple concret

Si vous avez les routes suivantes dans votre application :

- /dashboard
- /users
    - /users/list
    - /users/detail/:id
    - /users/create
    - /users/edit/:id
                                

Alors organisez votre application comme ceci :

src/
|-- app/
|   |-- features/
|   |   |-- dashboard/              # Feature module for the /dashboard route
|   |   |   |-- pages/              # Folder for page components in the dashboard feature
|   |   |   |-- components/         # Folder for reusable components specific to the dashboard feature
|   |   |   |-- services/           # Folder for services specific to the dashboard feature
|   |   |-- users/                  # Feature module for the /users route
|   |   |   |-- pages/              # Folder for page components in the users feature
|   |   |   |   |-- create/         # Page used to create a new user
|   |   |   |   |-- edit/           # Page used to edit an existing user
|   |   |   |   |-- list/           # Page used to list all users
|   |   |   |   |-- detail/         # Page used to display the details of a user
|   |   |   |-- components/         # Folder for reusable components specific to the users feature
|   |   |   |-- services/           # Folder for services specific to the users feature
|-- ...
                                

Documentation: Angular File Structure and Best Practices
Tips for Structuring Your Angular Project
How to define a highly scalable folder structure for your Angular project
Angular schematics extension VS Code
Github: Angular Folder Structure
Angular App Architecture: Don’t get Lost in Structure
How to structure your Angular apps like a Googler
Angular.io: Workspace and project file structure
Angular.io Styleguide: Application structure and NgModules
Angular project structure best practice

Règles d'Architecture

Routing & Navigation

Le nommage des dossiers features doit refléter les routes et leurs structures.

Utilisez WithComponentInputBinding pour lier les paramètres de route en tant qu’inputs.

Utilisez le lazy loading pour toutes les routes sauf la route par défaut.

N'hésitez pas à créer un niveau de route pour gérer des zones de votre application (admin, user, ...).

Les formulaires

Les reactives forms et les template-driven forms sont deux approches valides. Toutefois, le mieux est de choisir une approche et de s'y tenir.
En conséquence, utilisez toujours les les template-driven forms. Ils sont déclaratifs, simple et sont facilement compatibles avec les signals.

Styling & CSS

Utilisez Tailwind CSS pour un style utility-first.
Avec cette approche, vous ne devez pas écrire de CSS personnalisé.
Si nécessaire, vous pouvez ajouter des classes de style partagées réutilisant les utilitaires de tailwind via le fichier tailwind.config.js.
Note: Le tri des classes Tailwind dans le template peut être géré via un plugin VS Code `prettier-plugin-tailwindcss`.

Gestion des états partagés

N’utilisez pas de librairies de store externes.
Utilisez des services partagés.
Utilisez des signaux pour gérer les states en exposant uniquement des signaux en read-only pour la lecture des states et des méthodes publiques pour les actions sur les states.

Gestion des Erreurs

Rendez les erreurs compréhensibles pour l’utilisateur final.
Utilisez un gestionnaire d’erreurs global pour les exceptions non interceptées.
Gérez les erreurs des appels HTTP dans les ApiServices. Transformez-les pour qu’elles soient compréhensibles et utiles, puis retournez des données cohérentes.