You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Application-Web/Documentation/Conception.md

7.2 KiB

Conception

Organisation

Notre projet est divisé en plusieurs parties:

  • src/API, qui définit les classes qui implémentent les actions de lapi
  • src/App, qui définit les contrôleurs et les vues de lapplication web
  • src/Core, définit les modèles, les classes métiers, les librairies internes (validation, http), les gateways, en somme, les élements logiques de lapplication et les actions que lont peut faire avec.
  • sql, définit la base de donnée utilisée, et éxécute les fichiers sql lorsque la base de donnée nest pas initialisée.
  • profiles, définit les profiles dexecution, voir Documentation/how-to-dev.md pour plus dinfo
  • front contient le code front-end react/typescript
  • ci contient les scripts de déploiement et la définition du workflow dintégration continue et de déploiement constant vers notre staging server (maxou.dev//public/).
  • public point dentrée, avec :
    • public/index.php point dentrée pour la webapp
    • public/api/index.php point dentrée pour lapi.

Backend

Validation et résilience des erreurs

Motivation

Un controlleur a pour but de valider les données d'une requête avant de les manipuler.

Nous nous sommes rendu compte que la vérification des données d'une requête était redondante, même en factorisant les différents types de validation, nous devions quand même explicitement vérifier la présence des champs utilisés dans la requête.

public function doPostAction(array $form) {
    $failures = [];
    $req = new HttpRequest($form);
    $email = $req['email'] ?? null;
    if ($email == null) {
        $failures[] = "Vous n'avez pas entré d'adresse email.";
        return;
    }
    if (Validation::isEmail($email)) {
        $failures[] = "Format d'adresse email invalide.";
    }
    if (Validation::isLenBetween($email, 6, 64))) {
        $failures[] = "L'adresse email doit être d'une longueur comprise entre 6 et 64 charactères.";
    }

    if (!empty($failures)) {
        return ViewHttpResponse::twig('error.html.twig', ['failures' => $failures]);
    }

    // traitement ...
}

Nous sommes obligés de tester à la main la présence des champs dans la requête, et nous avons une paire condition/erreur par type de validation, ce qui, pour des requêtes avec plusieurs champs, peut vite devenir illisible si nous voulons être précis sur les erreurs.

Ici, une validation est une règle, un prédicat qui permet de valider une donnée sur un critère bien précis (injection html, adresse mail, longueur, etc.).
Bien souvent, lorsque le prédicat échoue, un message est ajouté à la liste des erreurs rencontrés, mais ce message est souvent le même, ce qui rajoute en plus de la redondance sur les messages d'erreurs choisis lorsqu'une validation échoue.

Schéma

Toutes ces lignes de code pour définir que notre requête doit contenir un champ nommé email, dont la valeur est une adresse mail, d'une longueur comprise entre 6 et 64.
Nous avons donc trouvé l'idée de définir un système nous permettant de déclarer le schéma d'une requête, et de reporter le plus d'erreurs possibles lorsqu'une requête ne valide pas le schéma :

public function doPostAction(array $form): HttpResponse {
    $failures = [];
    $req = HttpRequest::from($form, $failures, [
        'email' => [DefaultValidators::email(), DefaultValidators::isLenBetween(6, 64)]
    ]);

    if (!empty($failures)) { //ou $req == null
        return ViewHttpResponse::twig('error.html.twig', ['failures' => $failures])
    }

    // traitement ...
}

Ce système nous permet de faire de la programmation déclarative, nous disons à quoi ressemble la forme de la requête que l'on souhaite, plustot que de définir comment réagir face à notre requête.
Ce système permet d'être beaucoup plus clair sur la forme attendue d'une requête, et de garder une lisibilité satisfaisante peu importe le nombre de champs que celle-ci contient.

Nous pouvons ensuite emballer les erreurs de validation dans des ValidationFail et FieldValidationFail, ce qui permet ensuite d'obtenir plus de précision sur une erreur, comme le nom du champ qui est invalidé, et qui permet ensuite à nos vues de pouvoir manipuler plus facilement les erreurs et facilement entourer les champs invalides en rouge, ainsi que d'afficher toutes les erreurs que l'utilisateur a fait, d'un coup.

HttpRequest, HttpResponse

Nous avons choisi d'encapsuler les requêtes et les réponses afin de faciliter leur manipulation.
Cela permet de mieux repérer les endroits du code qui manipulent une requête d'un simple tableau, et de garder à un seul endroit la responsabilitée d'écrire le contenu de la requête vers client.

src/App définit une ViewHttpResponse, qui permet aux controlleurs de retourner la vue qu'ils ont choisit. C'est ensuite à la classe src/App/App d'afficher la réponse.

index.php

Il y a deux points d'entrés, celui de la WebApp (public/index.php), et celui de l'API (public/api/index.php).
Ces fichiers ont pour but de définir ce qui va être utilisé dans l'application, comme les routes, la base de donnée, les classes métiers utilisés, comment gérer l'authentification dans le cadre de l'API, et une session dans le cadre de la web app.

L'index définit aussi quoi faire lorsque l'application retourne une réponse. Dans les implémentations actuelles, elle délègue simplement l'affichage dans une classe (IQBall\App\App et IQBall\API\API).

API

Nous avons définit une petite API (src/Api) pour nous permettre de faire des actions en arrière plan depuis le front end.
Par exemple, l'API permet de changer le nom d'une tactique, ou de sauvegarder son contenu.
C'est plus pratique de faire des requêtes en arrière-plan plustot que faire une requête directement à la partie WebApp, ce qui aurait eu pour conséquences de recharger la page

Frontend

Utilisation de React

Notre application est une application de création et de visualisation de stratégies pour des match de basket.
Léditeur devra comporter un terrain sur lequel il est possible de placer et bouger des pions, représentant les joueurs.
Une stratégie est un arbre composé de plusieurs étapes, une étape étant constituée dun ensemble de joueurs et dadversaires sur le terrain, aillant leur position de départ, et leur position cible, avec des flèches représentant le type de mouvement (dribble, écran, etc) effectué.
les enfants dune étape, sont dautres étapes en fonction des cas de figures (si tel joueur fait tel mouvement, ou si tel joueur fait telle passe etc).
Pour rendre le tout agréable à utiliser, il faut que linterface soit réactive : si lon bouge un joueur, il faut que les flèches qui y sont liés bougent aussi, il faut que les joueurs puissent bouger le long des flèches en mode visualiseur etc…

Le front-end de léditeur et du visualiseur étant assez ambitieux, et occupant une place importante du projet, nous avons décidés de leffectuer en utilisant le framework React qui rend simple le développement dinterfaces dynamiques, et dutiliser typescript parce quici on code bien et quon impose une type safety a notre code.