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

Architecture : Services et Injection de Dépendance

Au début d’un projet ASP.NET Core, on a tendance à écrire toute la logique métier directement dans les méthodes des Contrôleurs. C’est ce qu’on appelle l’anti-pattern “Fat Controller”.

Pourquoi séparer le code ?

Les contrôleurs ont pour unique responsabilité de gérer le protocole HTTP :

  1. Recevoir la requête (et désérialiser le JSON).
  2. Valider les entrées basiques.
  3. Appeler la logique métier.
  4. Renvoyer une réponse HTTP appropriée (200 OK, 404 Not Found, etc.).

Ils ne devraient JAMAIS contenir :

  • La logique de calcul (ex: formule du coût du reset).
  • Les appels directs à la base de données (si possible).
  • La logique de validation complexe (ex: vérifier si l’utilisateur a assez d’argent).

Cette séparation permet de :

  • Tester la logique métier sans lancer un serveur web (Tests Unitaires).
  • Réutiliser la logique métier ailleurs (ex: dans une tâche planifiée).
  • Lire plus facilement le code.

Création d’un Service

Un service est une classe C# standard qui contient la logique métier.

Exemple : GameService.cs

public class GameService
{
    private readonly AppDbContext _context;

    public GameService(AppDbContext context)
    {
        _context = context;
    }

    public void Click(int userId)
    {
        var progression = _context.Progressions.FirstOrDefault(p => p.UserId == userId);
        if (progression == null) 
        {
            throw new Exception("Progression introuvable");
        }

        progression.Count += (int)(1 * Math.Pow(1.5, progression.Multiplier));
        _context.SaveChanges();
    }
}

Injection de Dépendance (DI)

Pour utiliser ce service dans un contrôleur, il faut l’enregistrer dans le conteneur de dépendances.

Dans Program.cs :

// Enregistrement du service
// AddScoped : Une nouvelle instance est créée pour chaque requête HTTP
builder.Services.AddScoped<GameService>();

Ensuite, on l’injecte dans le constructeur du contrôleur :

[ApiController]
[Route("api/[controller]")]
public class GameController : ControllerBase
{
    private readonly GameService _gameService;

    public GameController(GameService gameService)
    {
        _gameService = gameService;
    }

    [HttpPost("click")]
    public IActionResult Click()
    {
        // On récupère l'ID de l'utilisateur (via le token JWT par exemple)
        int userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);

        try 
        {
            _gameService.Click(userId);
            return Ok();
        }
        catch (Exception ex)
        {
            return BadRequest(ex.Message);
        }
    }
}

Refactoring étape par étape

  1. Identifiez un bloc de logique dans votre contrôleur.
  2. Créez une méthode dans un Service correspondant (UserService, GameService, InventoryService).
  3. Déplacez le code (et les dépendances comme le DbContext) dans ce Service.
  4. Appelez le Service depuis le Contrôleur.

Votre contrôleur va maigrir et devenir beaucoup plus clair !