Programmation avancée

Développement d'applications prêtes pour la production.

IUT du Limousin / BUT3 INFO / R5A05 / CM

Objectifs du module

Organisation des séquences

Modalités d'évaluation

unil.im/r5a05-2024

1. Propos introductifs

2. Gestion des dépendances

3. Cycle de vie des requêtes

4. Authentification et autorisation

5. Qualité et conformité

6. Gestion des performances

7. Cycle d'intégration et exploitation

Propos introductifs

Programmation avancée ?

L'architecture d'une application

L'environnement en entreprise

Les frameworks de développement

Notes concernant l'open source

Propos introductifs / Programmation avancée ?

DI/IoC, composer, npm/yarn

SOLID, ACID, DRY

OOP, MVC, DI

HTTP, HTTPS/TLS, REST, OAuth

monolithique, micro-services

EDA, SPA, MPA/SSR

documentation

normes, conventions, tests

conformité, audits, LDAP, SSO

DevOps, CI/CD

monitoring, mise à l’échelle

performances, mise à l’échelle

Propos introductifs / Programmation avancée ?

  • Principes & design patterns
    SOLID, ACID, DRY & OOP, MVC, IoC, DI
  • Gestion des dépendances
    DI container, semver, maven, composer, npm/yarn
  • Normes industrielles
    HTTP, HTTPS/TLS, REST, OAuth
  • Architecture applicative
    monolithique, micro-services, EDA, SPA, MPA/SSR
  • Qualité
    documentation, normes, conventions, tests
  • Environnement des entreprises
    conformité, audits, SCM, LDAP, SSO
  • Exploitation
    CI/CD, DevOps, monitoring, mise à l’échelle

Propos introductifs / L'architecture d'une application

  • Plusieurs façons de concevoir une application web
  • On distingue traditionnellement
    1. L'architecture côté traitement (calcul, backend)
    2. L'architecture côté interface (rendu, frontend)
  • Les choix d'architecture impactent
    • les technologies utilisées
    • les modalités d'exploitation
  • L'arbitrage se fait donc en fonction
    • des objectifs du projet
    • des ressources disponibles

Propos introductifs / L'architecture d'une application

Côté traitement:
Monolithique vs. Micro-services
Source: Dashbird

Propos introductifs / L'architecture d'une application

Côté interface:
Single Page Application (SPA) vs. Multi Page Application (MPA/SSR)
Source: Logrocket

Propos introductifs / L'architecture d'une application

SPA par l'exemple
Interface d'Excel 365

Propos introductifs / L'architecture d'une application

SPA par l'exemple
Source d'Excel 365 sans les balises <script>

Propos introductifs / L'architecture d'une application

Monolithique + MPA = ❤️

Propos introductifs / Les frameworks de développement

Source: Wikipedia

Propos introductifs / Les frameworks de développement

Pourquoi utiliser un framework ?
  1. Temps

    Gagner du temps sur les tâches communes/basiques/chronophages

  2. Uniformité

    Imposer un cadre commun pour l’ensemble des projets

  3. Conformité

    Apporter des garanties en termes de sécurité, qualité et respest des bonnes pratiques

Propos introductifs / Les frameworks de développement

Projet Laravel
Projet Symfony

Propos introductifs / Les frameworks de développement

Comment choisir un framework ?
  1. Critères

    Besoins et contraintes du projet

    Écosystème, communauté et antériorité

  2. Limites

    Choix structurant = choix important

    Pas de solution miracle

    Ne conditionne pas la qualité d'un projet

Propos introductifs / Les frameworks de développement

Quelles conséquences sur le marché du travail ?
  • Maitrise d’un framework = compétence valorisée, mais pas exclusive
  • Les entreprises cherchent des profils
    1. Qui savent s’adapter (prioritairement)
    2. Qui connaissent la technologie qu'elles utilisent (de préférence)
    3. Qui maîtrisent le framework qu'elles utilisent (idéalement)

Propos introductifs / L'environnement en entreprise

😀
Ressources
😡
Contraintes
Environnement
(toolchain/stack, SSO, SCM, CI, dépôts privés, etc.)
Temps + Budget
(concurrence, rentabilité, coût, taille des équipes, etc.)
Connaissances
(documentation, normes et conventions, etc.)
Risques
(conformité, sécurité, turnover/maladie, etc.)
Équipes + Expertise
(architecte applicatif, dba, PO, etc.)
Engagements
(SLA / performances / mise à l’échelle)

Propos introductifs / Notes concernant l’open source

Composants open source

  • Librairies
    ex. Librairie d'export HTML vers PDF, etc.
  • Frameworks
    ex. Laravel, Symfony, etc.
  • Utilitaires et logiciels
    ex. MariaDB, PhpMyAdmin, TablePlus, etc.

Modèles de financement

  • Supporté par une entreprise
    Disparition de l’entreprise, rachat, etc.
  • Supporté par une fondation
    Modalités de financement, taille de la structure, etc.
  • Entièrement indépendant
    Investissement du ou des développeurs dans le temps

Utilisation d’un composant

  • Vérifier l'origine du projet
  • Vérifier la licence d'utilisation
  • Vérifier les modalités de maintenance
  • Contribuer à son tour, selon ses compétences
    • Aider les autres utilisateurs sur les forums
    • Documenter et/ou traduire dans d’autres langues
    • Participer aux correctifs et mises à jour

Gestion des dépendances

Gestion sémantique de version

Gestionnaires de dépendances

Autoloading

Injection de dépendances

Gestion des dépendances / Gestion sémantique de version

Source: Invision

Gestion des dépendances / Gestionnaires de dépendances

  • Outils en ligne de commande (CLI)
  • Tâches
    1. Définir la liste des dépendances externes du projet et les versions à utiliser
    2. Télécharger les dépendances du projet à partir d'un dépôt (privé ou public)
    3. (selon le langage) Définir l’auto-loading (chargement des fichiers)
    4. (selon le gestionnaire) Définir des tâches/scripts spécifiques au projet
  • Dépendances externes = code générique écrit en dehors du projet
  • Dépôt public = accès libre
  • Dépôt privé = accès restreint (généralement à une entreprise)

Gestion des dépendances / Gestionnaires de dépendances

Nom Langage Fichier Dépôt public
Composer PHP composer.jsoncomposer.lock packagist.org
NPM Javascript package.jsonpackage-lock.json npmjs.com
Yarn Javascript package.jsonyarn.lock npmjs.com
pip Python requirements.txt pypi.org
Maven Java pom.xml central.sonatype.org

Gestion des dépendances / Gestionnaires de dépendances

    
          
    
Composer (PHP)
    
          
    
NPM (Javascript)

Gestion des dépendances / Autoloading

    
          
    
Sans autoloading
    
          
    
Avec autoloading

Gestion des dépendances / Injection de dépendances

L’« Injection de dépendances » est un terme à 25 dollars pour un concept à 5 centimes. [...] L'injection de dépendances consiste à donner à un objet ses variables d'instance. [...].
James Shore

Gestion des dépendances / Injection de dépendances

    
          
    
    
          
    
Exemple extrait de PHP: The Right Way

Gestion des dépendances / Injection de dépendances

    
          
    
Exemple extrait de PHP: The Right Way

Gestion des dépendances / Injection de dépendances

  • Conteneur d'injection ≠ Injection de dépendances
  • Conteneur d’injection = un des mécanismes qui permet de mettre en oeuvre l’injection de dépendances
  • Rôles :
    • Centraliser l'instantiation des classes au même endroit
    • Gérer le cycle de vie des objets
  • Intégré dans les frameworks (avec des implémentations différentes)
  • En pratique, il y a très peu d'interaction avec le conteneur (autowiring)

Gestion des dépendances / Injection de dépendances

    
          
    
Autowiring (exemple adapté depuis Laravel)

Cycle de vie des requêtes

Contexte d'exécution

Définir la requête

Traiter la requête

Générer la réponse

Gérer les erreurs

Cycle de vie des requêtes / Contexte d'exécution de la charge de travail

2 contextes d'exécution :

  1. Traitement dans la requête (synchrone, depuis le navigateur)
    = client (requête) → serveur (réponse)

  2. Traitement en arrière-plan (asynchrone, hors navigateur)
    = événement déclencheur → traitement → résultat
    = action de l'utilisateur, timer, etc. → tâche → un fichier, un e-mail, etc.

Cycle de vie des requêtes / Contexte d'exécution de la charge de travail

Pourquoi ?

  1. Logique métier / fonctionnelle
  2. Traitement long
  3. Performance
  4. Sécurité / conformité

Cycle de vie des requêtes / Contexte d'exécution de la charge de travail

Les étapes de traitement synchrone (requête) :

  1. Le client émet la requête (GET / POST) avec ou sans paramètres (form-data, url-encoded, etc.)
  2. Le serveur Web traite la requête (proxy/fichiers statiques/terminaison TLS/HTTPS, etc.) et la transfère à l’application
  3. L’application reçoit et traite la requête puis génère la réponse
    1. Couche 1 : pré-traitement (middleware)
    2. Couche 2 : validation (si données entrantes/formulaires)
    3. Couche 3 : action (traitement)
    4. facultatif: rendu HTML (si la réponse est une page Web)
  4. L’application transmet la réponse au serveur Web
  5. Le serveur Web transmet la réponse au client

Cycle de vie des requêtes / Définir la requête

1. Définir la requête

  • Le routage (routing)
  • Les pré-traitements

2. Traiter la requête

  • Valider les données entrantes
  • Prendre en charge l’envoi de fichiers
  • Manipuler les données persistantes

3. Générer la réponse

4. Gérer les erreurs

Cycle de vie des requêtes / Définir la requête (routing)

Action URL Route Nom
Page d’accueil blog.fr/ / index
Liste des posts blog.fr/posts /posts posts_list
Affichage du post #1 blog.fr/posts/1 /posts/1 posts_show

Cycle de vie des requêtes / Définir la requête (routing)

Symfony (PHP)

    
          
    
Fichier src/Controller/PostController.php

Cycle de vie des requêtes / Définir la requête (routing)

Spring (Java)

    
          
    
Fichier src/main/java/com/example/servingwebcontent/PostController.java

Cycle de vie des requêtes / Définir la requête (routing)

Laravel (PHP)

    
          
    
Fichier routes/web.php
    
          
    
Fichier app/Http/Controllers/PostController.php

Cycle de vie des requêtes / Définir la requête (pré-traitements)

  • Vérifications communes/partagées entre plusieurs routes
  • Vérifications réalisées en amont du traitement
    = avant d’arriver à la méthode du contrôleur
  • Exemples :
    • Vérifier que l’application n’est pas en maintenance
    • Vérifier la validation du jeton CSRF
    • Vérifier si l’utilisateur est identifié et s’il a le droit de réaliser l’action
    • Inscrire dans le journal un type spécifique d’actions
  • Selon les frameworks, l'implémentation diffère :
    • Laravel : middleware / guard
    • Symfony : events / firewall
    • Java : intercepting filters

Cycle de vie des requêtes / Traiter la requête

1. Définir la requête

  • Le routage (routing)
  • Les pré-traitements

2. Traiter la requête

  • Valider les données entrantes
  • Prendre en charge l’envoi de fichiers
  • Manipuler les données persistantes

3. Générer la réponse

4. Gérer les erreurs

Cycle de vie des requêtes / Définir la requête (validation)

  • Validation côté serveur (back) ⇒ obligatoire
  • Validation côté client (front) ⇒ additionnelle, facultative
        
              
        
    
  • Les frameworks embarquent presque tous des mécanismes pour définir des règles de validation des données entrantes

Cycle de vie des requêtes / Traiter la requête (validation)

PHP natif (procédural)

    
          
    

Cycle de vie des requêtes / Traiter la requête (validation)

Symfony

    
         
    
Fichier src/Entity/Author.php

Cycle de vie des requêtes / Traiter la requête (validation)

Laravel

    
          
    
Fichier app/Http/Controllers/AuthorController.php
    
          
    
Fichier app/Http/Requests/CreateAuthorRequest.php

Cycle de vie des requêtes / Traiter la requête (envoi de fichiers)

    
          
    
Exemple adapté à partir de la documentation PHP.net

Cycle de vie des requêtes / Traiter la requête (envoi de fichiers)

    
          
    
Laravel

Cycle de vie des requêtes / Traiter la requête (manipuler les données persistantes)

ORM

Cycle de vie des requêtes / Traiter la requête (manipuler les données persistantes)

    
          
    
Eloquent ORM (Laravel)
    
          
    
Doctrine ORM (Symfony)

Cycle de vie des requêtes / Générer la réponse

1. Définir la requête

  • Le routage (routing)
  • Les pré-traitements

2. Traiter la requête

  • Valider les données entrantes
  • Prendre en charge l’envoi de fichiers
  • Manipuler les données persistantes

3. Générer la réponse

4. Gérer les erreurs

Cycle de vie des requêtes / Générer la réponse

API

    
          
    
Exemple de retour d'une API au format JavaScript Object Notation (JSON)

Cycle de vie des requêtes / Générer la réponse

MPA/SSR

    
          
    
Page HTML compilée

Cycle de vie des requêtes / Générer la réponse

    
          
    

Cycle de vie des requêtes / Générer la réponse

    
          
    

Cycle de vie des requêtes / Gérer les erreurs

1. Définir la requête

  • Le routage (routing)
  • Les pré-traitements

2. Traiter la requête

  • Valider les données entrantes
  • Prendre en charge l’envoi de fichiers
  • Manipuler les données persistantes

3. Générer la réponse

4. Gérer les erreurs

Cycle de vie des requêtes / Gérer les erreurs

Malheureusement, peu importe la prudence que nous mettons lors de l'écriture de notre code, les erreurs sont un fait de la vie. PHP.net

Cycle de vie des requêtes / Gérer les erreurs

  • 2 types d'erreurs
    • Erreurs techniques (réseau, système de fichiers, etc.)
    • Erreurs fonctionnelles (cas imprévu, non pris en charge, etc.)
  • Dans tous les cas
    • S'assurer que l'erreur soit correctement traitée en interne
    • S'assurer que l'erreur soit correctement affichée à l'utilisateur
    • S'assurer que l'erreur soit tracée/journalisée/identifiable

Cycle de vie des requêtes / Gérer les erreurs

Mécanisme de gestion des exceptions

  • Spécifique au langage ou à la technologie utilisée
  • Les frameworks PHP intègrent tous un gestionnaire d'exceptions
  • Exemples
    • Afficher un message d'erreur à l'utilisateur pour l'aider à résoudre le problème (échec de validation)
    • Afficher un message d'erreur à l'utilisateur pour contacter l'assistance (cas non identifié)
    • Décider de ne pas bloquer le reste du traitement si l'exception levée ne le nécessite pas

Cycle de vie des requêtes / Gérer les erreurs

Source: Symfony

Cycle de vie des requêtes / Gérer les erreurs

Source: Laravel

Cycle de vie des requêtes / Gérer les erreurs

La journalisation

Enregistrer des informations lors de l'utilisation de l'application :

  • Erreurs avec trace de la pile d'exécution (par défaut)
  • Informations de conformité (ex. action destructive d'un utilisateur, etc.)
  • Informations de performances (ex. délai d'une requête SQL, temps de réponse d'une API, etc.)

Cycle de vie des requêtes / Gérer les erreurs

  1. Écrire des informations dans le journal
        
              
        
    
  2. Exploiter les informations inscrites
    • Dans un fichier (ex. storage/logs/laravel.log)
    • Dans une base de données
    • Dans un service tiers (Sentry, Datadog, etc.)
    • ...

Authentification et autorisation / Authentification

  • Vérification de l'identité = besoin récurrent
  • Authentification ≠ autorisation
  • 3 possibilités
    1. Soit l’application intègre directement l’authentification
    2. Soit l’application délègue l’authentification à un autre service
    3. Soit l’application gère les deux situations (ex. Saas)
  • Les frameworks facilitent la gestion de l'authentification
    1. Ressources pour prendre en charge l’authentification intégrée
      ex. middleware, guards, firewalls, etc.
    2. Ressources pour prendre en charge l’authentification déléguée
      ex. API/services/protocoles (IAM, IdP, OpenID/OAuth, LDAP, etc.)

Authentification et autorisation / Authentification

Authentification intégrée

L'application gère le cycle de vie des utilisateurs (inscription/création → modification → suppression).

  • 1 utilisateur = 1 ou plusieurs mécanismes d’authentification

    Adresse email/mdp, jeton par e-mail, WebAuthn, etc.

  • Selon l'architecture, après authentification, la session est conservée entre les requêtes :
    • SPA ou MPA (stateful) ⇒ session par témoin dans cookie
      (HttpSecure, sameSite)
    • SPA ou API (stateless) ⇒ par jeton
      (iOS Keychain ou Android KeyStore)

Authentification et autorisation / Authentification

Authentification déléguée

L’application reçoit des informations de la part du fournisseur d’identité.

  • De manière synchrone (au moment de la connexion de l’utilisateur)
  • De manière asynchrone (à l’initiative de l’IdP ou de l’app.)

    Informations de mise à jour de l’utilisateur auprès du fournisseur d’identité (changement d’email, suppression du compte, etc.)

Certains protocoles vont plus loin (ex: déconnexion automatique)

Authentification et autorisation / Authentification

Protocole OpenID Connect (flux hybride simplifié)

Authentification et autorisation / Authentification

Protocole OpenID Connect (flux hybride simplifié)

Exemple d'id_token (format JWT) · source: Documentation Microsoft

Authentification et autorisation / Authentification

Protocole OpenID Connect (flux hybride simplifié)

Exemple d'id_token décodé (format JSON) · source: Documentation Microsoft

Authentification et autorisation / Autorisation

  • Le mécanisme d'autorisation dépend du projet / des règles de gestion

    Rôles et/ou permissions et/ou assignation explicite et/ou ...

  • Pattern d'autorisation courant
    1. une action = une permission
    2. un rôle = plusieurs permissions
    3. un utilisateur = 1 ou plusieurs rôles

Authentification et autorisation / Autorisation

    
          
    
Autorisation côté contrôleur
    
          
    
Autorisation côté vue

Qualité et conformité

Pourquoi écrire des tests ?

  • Écrite du code de qualité = écrire du code testable
  • Gain de temps dans la durée
  • Confort et sérénité d’esprit

Pourquoi appliquer des règles et conventions ?

  • Partage du code source
  • Gain de temps
  • Cohérence

Qualité et conformité / Mocking

  • Mocking = simuler le comportement d'objets
  • Tester une requête à un service météo
        
             
        
    
  • Tester l’envoi d’un email
        
             
        
    

Merci 👋

Place au projet XYZ.