ASP.NET CoreMaintainabilityArchitecture.NETBackend

Structurez un projet ASP.NET Core lisible, testable et maintenable avec des frontières claires, même lorsque l’équipe, les fonctionnalités et le code évoluent.

Yva Hajatiana
11 mars 2026
6 min de lecture
Partager :X / TwitterLinkedIn
Maquette modulaire représentant l’architecture logicielle

Structurer un projet ASP.NET Core maintenable

Un projet ASP.NET Core devient rarement ingérable du jour au lendemain. Il se dégrade progressivement: contrôleurs trop gros, services qui font tout, dépendances circulaires, conventions incohérentes et logique métier dispersée.

La maintenabilité n'est donc pas une qualité abstraite. C'est le résultat d'une structure explicite qui aide les développeurs à savoir où mettre le code et où chercher quand quelque chose casse.

Le problème n'est pas la taille, c'est la confusion

Beaucoup de bases de code supportent très bien des centaines de fichiers tant que la structure reste cohérente. L'inverse est aussi vrai: une petite application peut devenir pénible à faire évoluer si tout mélange transport HTTP, logique métier, accès aux données et intégrations externes.

Les symptômes sont connus:

  • contrôleurs de 400 lignes
  • services nommes Helper, Manager, Processor
  • dépôt générique utilisé partout
  • duplication de validation
  • logique métier cachée dans des handlers HTTP

Une structure de base qui marche bien

Une structure maintenable doit séparer l'intention métier des détails techniques.

src/
  Project.Api/
    Endpoints/
    Contracts/
    Middleware/
    DependencyInjection/
  Project.Application/
    Commands/
    Queries/
    Validators/
    Services/
  Project.Domain/
    Entities/
    ValueObjects/
    Enums/
    Events/
  Project.Infrastructure/
    Persistence/
    Messaging/
    ExternalServices/

Cette organisation n'est pas magique, mais elle donne des frontières utiles.

Poser les conventions de configuration dès le départ

La structure des dossiers ne suffit pas. Un projet devient également difficile à maintenir lorsque chaque composant lit directement une variable d'environnement ou une clé de configuration écrite en dur.

ASP.NET Core fournit déjà une chaîne de configuration adaptée aux différents environnements :

  • appsettings.json contient les valeurs communes et non sensibles ;
  • appsettings.Development.json adapte le poste de développement ;
  • les variables d'environnement remplacent les valeurs au déploiement ;
  • dotnet user-secrets conserve les secrets locaux hors du dépôt Git ;
  • le gestionnaire de secrets de la plateforme héberge les secrets de production.

Le code applicatif ne devrait pas connaître les noms de clés dispersés dans la configuration. Des options typées rendent le contrat visible et vérifiable au démarrage :

public sealed class PaymentOptions
{
    public const string SectionName = "Payments";

    public required Uri BaseUrl { get; init; }
    public required string ApiKey { get; init; }
    public int TimeoutSeconds { get; init; } = 10;
}

builder.Services
    .AddOptions<PaymentOptions>()
    .BindConfiguration(PaymentOptions.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart();

Cette validation évite de découvrir une configuration manquante lors de la première requête en production. Elle fait échouer le démarrage avec une cause identifiable.

Une convention simple aide à garder le système lisible : la couche Api compose la configuration et les dépendances, tandis que les couches Application et Domain reçoivent des contrats typés sans dépendre du fournisseur de configuration.

Règle 1: les contrôleurs et endpoints doivent rester minces

Un endpoint ASP.NET Core ne devrait pas prendre de décisions métier. Il traduit une requête HTTP en action applicative.

app.MapPost("/orders", async (
    CreateOrderRequest request,
    CreateOrderHandler handler,
    CancellationToken ct) =>
{
    var result = await handler.HandleAsync(new CreateOrderCommand(
        request.CustomerId,
        request.Items), ct);

    return Results.Created($"/orders/{result.OrderId}", result);
});

Si tu vois de la logique de pricing, de validation complexe, de transactions ou d'appels externes ici, la structure s'écarte déjà de la maintenabilité.

Règle 2: les noms doivent décrire une responsabilité

Le mot "service" n'explique rien. Une base maintenable préfère des noms orientés action ou domaine:

  • CreateOrderHandler
  • InvoiceNumberGenerator
  • PaymentRetryPolicy
  • CustomerEligibilityService

Ces noms disent ce que fait le composant. Cela réduit le besoin de documentation implicite.

Règle 3: un cas d'usage = une entrée claire

Pour rester lisible, chaque besoin métier important devrait correspondre à une commande ou une query clairement identifiée.

public sealed record PublishInvoiceCommand(Guid InvoiceId);

public sealed class PublishInvoiceHandler
{
    public async Task HandleAsync(PublishInvoiceCommand command, CancellationToken ct)
    {
        // orchestration du cas d'usage
    }
}

Cette approche rend les flux applicatifs plus évidents, facilite les tests et évite les gros services fourre-tout.

Règle 4: centraliser les conventions de validation et d'erreur

Une application ASP.NET Core vieillit mal quand chaque endpoint gère ses erreurs différemment. La maintenabilité passe par des conventions:

  • middleware global de gestion des exceptions
  • objets de réponse cohérents
  • validation centralisée
  • journalisation standardisée

Si chaque contrôleur invente sa propre façon de renvoyer une erreur, la dette augmente vite.

Règle 5: isoler l'infrastructure sans la rendre invisible

L'infrastructure doit être claire dans la solution. Base de données, cache, file de messages, fournisseurs externes: tout cela doit avoir une place évidente.

Exemple de dossier Infrastructure/Persistence:

Persistence/
  AppDbContext.cs
  Configurations/
  Repositories/
  Migrations/

Exemple de dossier Infrastructure/ExternalServices:

ExternalServices/
  OpenAiClient.cs
  StripeClient.cs
  StorageClient.cs

Le but est simple: un nouveau développeur doit comprendre où vivent les détails techniques en quelques minutes.

Règle 6: éviter les projets "utilitaires" sans frontière claire

Les projets Common, Shared, Utils deviennent souvent des zones de contamination. Du code de tous types finit par y vivre, sans responsabilité claire.

Il vaut mieux:

  • garder un petit noyau de primitives techniques bien délimitées
  • placer le code partagé dans le domaine ou l'application quand il a un contexte métier
  • dupliquer légèrement si cela evite un coupling artificiel

La duplication locale est parfois moins coûteuse qu'une mauvaise abstraction globale.

Règle 7: structurer aussi les tests

Une application maintainable ne se limite pas au code de production. Les tests doivent épouser la structure du projet.

Par exemple:

  • tests de domaine pour les invariants
  • tests d'application pour les cas d'usage
  • tests d'intégration pour la persistance et les API externes
  • tests d'API pour les contrats HTTP

Cette séparation evite les suites de tests lentes et peu fiables.

Exemple de dossier par fonctionnalité

Pour certains projets, une structure par fonctionnalité fonctionne encore mieux:

Application/
  Orders/
    Create/
      CreateOrderCommand.cs
      CreateOrderHandler.cs
      CreateOrderValidator.cs
    Cancel/
      CancelOrderCommand.cs
      CancelOrderHandler.cs

Cette approche est très efficace quand le produit grossit et que les fonctionnalités ont des comportements riches.

Les signaux d'alerte

Tu peux anticiper les problèmes en observant ces signaux:

  • tu dois ouvrir cinq dossiers pour comprendre un cas d'usage simple
  • une modification HTTP casse du code métier
  • des services ont trop de dépendances injectées
  • des objets EF Core circulent partout dans l'application
  • les nouveaux développeurs hésitent constamment sur l'emplacement du code

Ce ne sont pas des détails. Ce sont des symptômes structurels.

Une checklist simple de maintenabilité

Avant d'ajouter une nouvelle fonctionnalité, vérifie:

  • où vit le cas d'usage ?
  • où vivent les règles métier ?
  • où vivent les détails d'infrastructure ?
  • le nom des classes est-il explicite ?
  • le test correspondant est-il simple à écrire ?

Si la réponse est floue, la structure doit être clarifiée avant d'ajouter plus de code.

Conclusion

Structurer un projet ASP.NET Core pour rester maintenable n'est pas une question de mode. C'est une discipline de conception.

Une bonne structure rend les décisions visibles, réduit les effets de bord et permet à l'équipe de faire évoluer l'application sans multiplier les régressions. C'est exactement ce qu'on attend d'un projet qui doit durer.

Mots-clés :ASP.NET CoreMaintainabilityArchitecture.NETBackend
Y

Yva Hajatiana

Articles techniques sur l'ingénierie logicielle, .NET, le cloud et l'intelligence artificielle appliquée aux applications.