Domain-Driven Design en C# : comment ne pas se noyer dans la complexité

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

Domain-Driven Design en C# : comment ne pas se noyer dans la complexité

\n\n

Lorsque votre projet C# dépasse le stade de la « simple application CRUD », le moment de vérité arrive. La logique métier commence à s'emmêler, les méthodes des services gonflent jusqu'à des centaines de lignes, et le mot « refactoring » provoque un frisson nerveux. Cela vous parle ? Bienvenue dans le monde où, sans Domain-Driven Design (DDD), vous risquez de transformer votre code en marécage.

\n\n

Le DDD n'est pas « un framework de plus ». C'est une philosophie qui place le domaine métier au centre de l'univers. Aujourd'hui, nous allons voir comment appliquer le Domain-Driven Design dans des projets C# pour que le code devienne non seulement fonctionnel, mais aussi compréhensible et flexible. Pas de blabla — seulement des exemples concrets et des conseils pratiques.

\n\n

Pourquoi le DDD sauve les projets C# ?

\n\n

Imaginez que vous écrivez un système de gestion des commandes pour une boutique en ligne. Si vous mettez toute la logique dans les contrôleurs ASP.NET, dans six mois vous ne pourrez plus expliquer pourquoi la méthode CreateOrder() appelle CalculateShipping(), qui, à son tour, va chercher les taux de change dans la base de données. Le DDD propose une séparation claire : il y a une couche domaine, où vit la logique métier, et une infrastructure, qui s'occupe des détails techniques.

\n\n

L'idée clé du DDD est l'Ubiquitous Language (langage omniprésent). Vous et les experts métier parlez le même langage. Dans le code, cela se traduit par des termes comme « Order », « LineItem », « DiscountPolicy », et non « OrderDto », « OrderService » ou « Helper ». Plus le code se rapproche du langage métier, plus il est facile à maintenir.

\n\n

Conception stratégique et tactique

\n\n

Le DDD se divise en deux parties : la stratégique (comment diviser le système en modules) et la tactique (comment écrire le code à l'intérieur d'un module). En C#, la conception tactique nous offre quatre blocs de construction puissants :

\n\n
    \n
  • Entity — un objet avec un identifiant unique (par exemple, OrderId).
  • \n
  • Value Object — un objet défini par ses attributs (par exemple, Money ou Address).
  • \n
  • Aggregate Root — l'entité racine qui gère un groupe d'objets.
  • \n
  • Repository — une abstraction d'accès aux données.
  • \n
\n\n

Voyons à quoi cela ressemble dans le code.

\n\n

Exemple : concevoir une commande (Order)

\n\n

Commençons simplement : nous avons une commande composée de lignes. La commande peut avoir le statut « Nouveau », « Payé » ou « Annulé ».

\n\n

1. Value Object : Money

\n\n

L'argent est un Value Object classique. Deux objets Money sont égaux s'ils ont le même montant et la même devise. Ils sont immuables.

\n\n
public sealed record Money(decimal Amount, string Currency)\n{\n    public static Money operator +(Money left, Money right)\n    {\n        if (left.Currency != right.Currency)\n            throw new InvalidOperationException("Currency mismatch");\n        return new Money(left.Amount + right.Amount, left.Currency);\n    }\n}\n
\n\n

Remarquez le record — en C# 9+, c'est le moyen idéal de créer un Value Object. Il fournit automatiquement Equals, GetHashCode et l'immuabilité.

\n\n

2. Entity : Order et LineItem

\n\n

Les entités ont un identifiant. Créons une classe de base pour Entity :

\n\n
public abstract class Entity<TId>\n{\n    public TId Id { get; protected set; }\n\n    protected Entity(TId id) => Id = id;\n\n    public override bool Equals(object? obj) =>\n        obj is Entity<TId> other && Id!.Equals(other.Id);\n\n    public override int GetHashCode() => Id!.GetHashCode();\n}\n
\n\n

Maintenant, Order et LineItem eux-mêmes :

\n\n
public class Order : Entity<Guid>\n{\n    private readonly List<LineItem> _items = new();\n    public IReadOnlyList<LineItem> Items => _items.AsReadOnly();\n    public OrderStatus Status { get; private set; }\n    public Money Total { get; privat                

Blogs

Book Recommendations