Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Semaine 6

Cette semaine nous allons restructurer notre application pour la rendre plus maintenable, plus testable et plus propre.

Objectifs

Nous avons trois objectifs principaux pour cette séance :

  1. Architecture en Services : Sortir la logique métier des Contrôleurs.
  2. Gestion globale des erreurs : Supprimer les répétitions de try/catch grâce à un Middleware.
  3. Intégrité des données : Utiliser des Transactions pour sécuriser les achats.

Partie 1 : Architecture en Services

Actuellement, vos contrôleurs contiennent probablement toute la logique : accès à la BDD, calculs, règles métier.

Nous allons alléger les contrôleurs pour qu’ils ne s’occupent que de leur rôle : recevoir des requêtes HTTP et renvoyer des réponses HTTP.

1. Extraction des Services

Consignes

  1. Créez un dossier Services.
  2. Créez les classes suivantes :
    • UserService
    • GameService
    • InventoryService
  3. N’oubliez pas d’enregistrer ces services dans Program.cs avec builder.Services.AddScoped<...>();.
  4. Déplacez la logique de vos contrôleurs vers ces services.

Exemple de responsabilité :

  • Le Contrôleur récupère l’ID utilisateur depuis le Token, appelle le Service, et transforme le résultat en 200 OK ou renvoie une erreur.
  • Le Service contient le DbContext, cherche l’utilisateur en BDD, vérifie s’il a assez d’argent, effectue l’achat, sauvegarde en BDD, et retourne le nouvel état (ou lance une exception).

Référez-vous au cours sur L’architecture Services.


Partie 2 : Middleware et Gestion d’Erreurs

Une fois vos services en place, nous allons nettoyer la gestion des erreurs.

2. Création des Exceptions Personnalisées

Commencez par créer un dossier Exceptions.

Vous créerez une classe GameException qui hérite de Exception et qui aura les propriétés Message, Code et StatusCode.

Remplacez les exceptions génériques dans votre code par ces exceptions personnalisées.

3. Le Middleware de Gestion d’Erreurs

Créez un dossier Middlewares et ajoutez une classe ErrorHandlingMiddleware.cs.

Ce middleware devra :

  1. Contenir un bloc try/catch qui englobe l’appel au middleware suivant (await _next(context)).
  2. Dans le catch, appeler une méthode privée qui va gérer l’exception.
  3. Cette méthode devra définir le StatusCode de la réponse HTTP et écrire le JSON d’erreur dans le corps de la réponse.

Vous devrez mapper vos exceptions personnalisées aux codes HTTP et codes d’erreurs fonctionnels attendus par le frontend.

Par exemple :

  • GameException -> Status Code + ErrorResponse
  • Exception (toutes les autres) -> 500 Internal Server Error + Code INTERNAL_SERVER_ERROR

Référez-vous au cours sur les Middlewares pour la structure de base.

4. Enregistrement dans Program.cs

N’oubliez pas d’enregistrer votre middleware dans le pipeline de requête dans Program.cs.

L'ordre est important ! Le middleware d'erreur doit être placé le plus tôt possible pour pouvoir attraper les erreurs de tous les composants suivants.

5. Refactoring des Contrôleurs

C’est l’étape la plus importante. Vous allez devoir supprimer la gestion d’erreur manuelle de vos contrôleurs.

Vos méthodes de contrôleur ne devraient plus retourner de ActionResult en cas d’erreur (NotFound(), BadRequest()), mais laisser l’exception se propager.

Objectif : Le code de vos contrôleurs doit se concentrer uniquement sur le “Happy Path” (le cas où tout fonctionne).

Exemple conceptuel de ce à quoi cela doit ressembler :

[HttpGet("{id}")]
public ActionResult<UserPublic> GetUser(int id)
{
    // Le service lance une exception si l'user n'existe pas.
    // Le contrôleur ne s'en occupe plus.
    var user = _userService.GetById(id); 
    return Ok(user);
}

Vous devrez donc aussi modifier vos Services pour qu’ils lèvent les exceptions que vous avez créées au lieu de retourner null ou des codes d’erreur.

Partie 3 : Transactions

Dans la fonction d’achat d’un objet (dans InventoryService), vous effectuez deux opérations critiques :

  1. Débiter l’utilisateur (modification de Progression).
  2. Ajouter l’objet (modification de Inventory).

Si le serveur plante ou si une erreur survient entre ces deux étapes, vous risquez de débiter l’utilisateur sans lui donner l’objet, ou l’inverse.

6. Sécuriser les achats

Utilisez une transaction explicite d’Entity Framework pour englober ces deux opérations.

using var transaction = _context.Database.BeginTransaction();
try {
    // 1. Débiter l'argent
    // 2. Ajouter l'item
    // 3. SaveChanges
    
    transaction.Commit();
} catch {
    transaction.Rollback();
    throw;
}

Référez-vous au cours sur les Transactions.

Tâches à réaliser

  1. Extraire la logique métier vers des Services (UserService, GameService, InventoryService).
  2. Créer les exceptions personnalisées.
  3. Implémenter le ErrorHandlingMiddleware.
  4. Configurer Program.cs pour utiliser les services et le middleware.
  5. Refactoriser tout le GameController, UserController et InventoryController pour utiliser les services et supprimer les try/catch.
  6. Ajouter une Transaction dans la méthode d’achat de l’InventoryService.
  7. (Bonus) Ajouter des logs dans le middleware pour les erreurs 500.