Comment ne pas se noyer dans les spaghettis : Architecture Hexagonale en C#

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Quand le projet se transforme en bouillie

\n

Avez-vous déjà ouvert un projet C# écrit il y a un an, et réalisé avec horreur que rien ne peut être modifié sans risquer de tout casser ? La logique métier est étroitement liée à Entity Framework, les contrôleurs ASP.NET cachent les secrets de la base de données, et les tests unitaires se transforment en une intégration infernale avec la vraie base de données. Cela vous dit quelque chose ?

\n

C'est un symptôme classique d'absence d'architecture. Bonne nouvelle : il existe une recette éprouvée — l'Architecture Hexagonale (également appelée Ports & Adapters). Ce modèle, inventé par Alistair Cockburn, n'est pas qu'un terme à la mode. C'est un salut pour les projets moyens et grands en C#. Voyons comment il fonctionne, et écrivons tout de suite du code.

\n\n

L'essence de l'Architecture Hexagonale : noyau et périphérie

\n

Imaginez un hexagone. Au centre se trouve votre application avec une logique métier pure, écrite en C# pur sans dépendances externes. Sur les bords se trouvent les ports (interfaces), par lesquels le noyau communique avec le monde extérieur. Les adaptateurs sont les implémentations concrètes : contrôleurs ASP.NET, repositories EF Core, clients RabbitMQ.

\n

La règle principale : le noyau ne sait rien du monde extérieur. Il ne connaît que ses ports. Cela permet de changer la base de données, l'interface utilisateur ou les files de messages sans modifier la logique métier.

\n\n

Pourquoi un développeur C# en a-t-il besoin ?

\n
    \n
  • Testabilité : La logique métier peut être testée sans base de données, HTTP ni système de fichiers.
  • \n
  • Flexibilité : Vous voulez remplacer PostgreSQL par MongoDB ? Changez uniquement l'adaptateur.
  • \n
  • Lisibilité : Le code devient semblable à une spécification de besoins, plutôt qu'à un enchevêtrement de dépendances.
  • \n
  • Respect de SOLID : En particulier le principe d'inversion des dépendances (Dependency Inversion).
  • \n
\n\n

Structure du projet en C#

\n

Dans l'Architecture Hexagonale, on distingue généralement trois couches principales. Pour C#, il est pratique de les organiser en projets séparés dans une même solution :

\n\n
MyApp.sln\n├── MyApp.Domain          // Noyau : entités, value objects, ports (interfaces)\n├── MyApp.Application     // Cas d'utilisation (services applicatifs)\n├── MyApp.Infrastructure  // Adaptateurs : EF Core, API externes, journalisation\n└── MyApp.Api             // Adaptateur d'entrée : Contrôleurs ASP.NET / Minimal API\n
\n\n

Notez que Domain et Application sont des bibliothèques de classes pures, sans références à Microsoft.AspNetCore ou EntityFramework. Uniquement System et System.Threading.Tasks.

\n\n

Écrire du code : exemple concret

\n

Supposons que nous créons un système de commandes. Il faut créer une commande et la sauvegarder.

\n\n

Étape 1. Le noyau (Domain)

\n

Ici vivent les entités et les ports — les interfaces qui définissent comment le noyau communique avec le monde.

\n\n
// MyApp.Domain/Entities/Order.cs\npublic class Order\n{\n    public Guid Id { get; private set; }\n    public string CustomerName { get; private set; }\n    public decimal TotalAmount { get; private set; }\n    public DateTime CreatedAt { get; private set; }\n\n    public Order(string customerName, decimal totalAmount)\n    {\n        Id = Guid.NewGuid();\n        CustomerName = customerName;\n        TotalAmount = totalAmount;\n        CreatedAt = DateTime.UtcNow;\n    }\n}\n\n// MyApp.Domain/Ports/IOrderRepository.cs (Port de sortie)\npublic interface IOrderRepository\n{\n    Task<Order> GetByIdAsync(Guid id);\n    Task AddAsync(Order order);\n}\n\n// MyApp.Domain/Ports/IOrderNotifier.cs (Port de sortie — notifications)\npublic interface IOrderNotifier\n{\n    Task NotifyOrderCreatedAsync(Order order);\n}\n
\n\n

Notez que IOrderRepository et IOrderNotifier sont des ports. Le noyau définit ce qu'il veut, mais pas comment c'est implémenté.

\n\n

Étape 2. Cas d'utilisation

Blogs

Book Recommendations