0Pricing
C# Academy · Leçon

Records dans la conception pilotée par le domaine

Modélisez les objets-valeurs, les DTO et les événements de domaine sous forme de records afin de garantir l’immuabilité et d’améliorer la lisibilité du code.

Records dans la conception pilotée par le domaine est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.

DDD et objets-valeurs

En conception pilotée par le domaine (DDD), un objet-valeur est défini par ses propriétés, et non par son identité. Deux valeurs Money(100, "USD") sont interchangeables. Les enregistrements constituent la représentation C# idéale des objets-valeurs.

Les objets-valeurs sous forme d’enregistrements

Modélisez les objets-valeurs du domaine sous forme d’enregistrements. Leur égalité structurelle, leur immutabilité et leur syntaxe concise correspondent parfaitement aux principes du DDD.

// Value objects — defined by their values, not identity
public record Money(decimal Amount, string Currency);
public record Email(string Value);
public record PhoneNumber(string CountryCode, string Number);
public record Address(string Street, string City, string Country, string PostalCode);

// Two Money objects with same values are equal:
var price = new Money(99.99m, "USD");
var same  = new Money(99.99m, "USD");
Console.WriteLine(price == same); // True

Validation dans les constructeurs d’objets-valeurs

Ajoutez un constructeur compact pour valider les invariants. Les objets-valeurs ne doivent pouvoir être créés que dans un état valide : tout état invalide doit provoquer une exception au moment de la construction.

public record Email(string Value)
{
    // Compact constructor for validation
    public Email : this(Value)
    {
        if (string.IsNullOrWhiteSpace(Value) || !Value.Contains('@'))
            throw new ArgumentException("Invalid email address", nameof(Value));
        Value = Value.Trim().ToLowerInvariant();
    }
}

// Usage:
var email = new Email("  Alice@Example.COM  "); // normalized to alice@example.com
new Email("not-an-email"); // throws ArgumentException

Entités et objets-valeurs

Les entités possèdent une identité (suivie par un ID au fil du temps) ; les objets-valeurs n’ont pas d’identité et sont interchangeables lorsque leurs valeurs correspondent. Modélisez-les différemment en C#.

// ENTITY: has identity, mutable state
public class Customer
{
    public Guid Id { get; } = Guid.NewGuid();
    public string Name { get; set; } = "";
    public Email Email { get; set; } = default!; // value object
}

// VALUE OBJECT: no identity, immutable
public record Email(string Value);
public record Money(decimal Amount, string Currency);

// Two customers with same name are DIFFERENT entities
// Two Money(100, "USD") are the SAME value

Objets-valeurs riches avec des méthodes métier

Les objets-valeurs peuvent contenir de la logique métier sous forme de méthodes. Les opérations renvoient de nouveaux objets-valeurs au lieu de modifier celui qui existe.

public record Money(decimal Amount, string Currency)
{
    public Money Add(Money other)
    {
        if (Currency != other.Currency)
            throw new DomainException("Cannot add different currencies");
        return this with { Amount = Amount + other.Amount };
    }

    public Money Subtract(Money other) =>
        Amount >= other.Amount
            ? this with { Amount = Amount - other.Amount }
            : throw new DomainException("Insufficient funds");

    public Money ApplyDiscount(decimal percent) =>
        this with { Amount = Amount * (1 - percent / 100) };

    public static Money Zero(string currency) => new(0, currency);
}

Événements de domaine sous forme d’enregistrements

Les événements de domaine décrivent les faits qui se sont produits dans le domaine. Les enregistrements sont parfaits pour cela : ils sont immuables, descriptifs et utilisent l’égalité structurelle, ce qui simplifie les tests.

// Domain events as records
public abstract record DomainEvent(DateTime OccurredAt);

public record OrderPlaced(
    Guid OrderId,
    Guid CustomerId,
    Money Total,
    DateTime OccurredAt) : DomainEvent(OccurredAt);

public record OrderShipped(
    Guid OrderId,
    string TrackingNumber,
    DateTime OccurredAt) : DomainEvent(OccurredAt);

public record PaymentReceived(
    Guid OrderId,
    Money Amount,
    DateTime OccurredAt) : DomainEvent(OccurredAt);

DTO et contrats d’API sous forme d’enregistrements

Les DTO de requête et de réponse sont naturellement des objets-valeurs : ils transportent des données sans identité. Les enregistrements les rendent concis et immuables.

// Request DTOs
public record CreateOrderCommand(
    Guid CustomerId,
    IReadOnlyList<OrderLineDto> Lines);

public record OrderLineDto(Guid ProductId, int Quantity);

// Response DTOs
public record OrderCreatedResponse(
    Guid OrderId,
    string Status,
    decimal Total,
    DateTime CreatedAt);

// These can be compared in tests by value:
var expected = new OrderCreatedResponse(id, "Pending", 99m, now);
Assert.Equal(expected, actual);

Stockage des objets-valeurs dans EF Core

Utilisez des entités possédées pour conserver les objets-valeurs dans EF Core. Les propriétés de chaque objet-valeur sont stockées dans la table du propriétaire (ou dans une table distincte), sans PK distincte.

public class Order
{
    public int Id { get; set; }
    public Money Total { get; set; } = new(0, "USD");
    public Address ShipTo { get; set; } = default!;
}

// EF Core config:
modelBuilder.Entity<Order>(e =>
{
    e.OwnsOne(o => o.Total, money =>
    {
        money.Property(m => m.Amount).HasColumnType("decimal(18,2)");
        money.Property(m => m.Currency).HasMaxLength(3);
    });
    e.OwnsOne(o => o.ShipTo);
});

Racine d’agrégat avec des objets-valeurs

Regroupez tous ces concepts : une racine d’agrégat Order qui utilise des enregistrements pour ses objets-valeurs et ses événements de domaine, avec toutes les règles métier appliquées dans les méthodes.

public class Order
{
    private readonly List<DomainEvent> _events = new();
    public IReadOnlyList<DomainEvent> DomainEvents => _events;

    public Guid Id { get; } = Guid.NewGuid();
    public Address ShipTo { get; private set; } = default!; // value obj
    public Money Total { get; private set; } = Money.Zero("USD");

    public void PlaceOrder(Address shipTo, IEnumerable<OrderLine> lines)
    {
        ShipTo = shipTo;
        Total  = lines.Aggregate(Money.Zero("USD"),
            (acc, l) => acc.Add(l.Price));
        _events.Add(new OrderPlaced(Id, CustomerId, Total, DateTime.UtcNow));
    }
}

Cas réel : identifiants sûrs par leur type

Utilisez des enregistrements pour encapsuler les ID primitifs et empêcher le mélange accidentel des ID de différentes entités — une technique courante du DDD appelée « identifiants fortement typés ».

// Strongly typed IDs — can't confuse CustomerId with OrderId
public record CustomerId(Guid Value)
{
    public static CustomerId New() => new(Guid.NewGuid());
    public static implicit operator Guid(CustomerId id) => id.Value;
}

public record OrderId(Guid Value)
{
    public static OrderId New() => new(Guid.NewGuid());
}

// Type safety:
void ShipOrder(OrderId orderId, CustomerId customerId) { }

ShipOrder(OrderId.New(), CustomerId.New()); // OK
ShipOrder(CustomerId.New(), OrderId.New()); // COMPILE ERROR!

Vérification rapide

Qu’est-ce qui définit un objet-valeur DDD, et pourquoi les enregistrements lui conviennent-ils naturellement ?

Récapitulatif : les enregistrements en conception pilotée par le domaine

Points clés à retenir :

  • Objets-valeurs : définis par leurs valeurs, sans identité — utilisez des enregistrements
  • Entités : possèdent une identité (ID) et un état mutable — utilisez des classes
  • Validez les invariants dans des constructeurs compacts — un état invalide ne peut ainsi pas être représenté
  • Événements de domaine sous forme d’enregistrements : immuables, comparables et explicites
  • Entités possédées d’EF Core pour conserver les objets-valeurs
  • Les ID fortement typés à l’aide d’enregistrements empêchent le mélange des ID d’entités

Questions Fréquemment Posées

La leçon « Records dans la conception pilotée par le domaine » est-elle gratuite ?

Oui — le texte complet de « Records dans la conception pilotée par le domaine » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Records dans la conception pilotée par le domaine » ?

Modélisez les objets-valeurs, les DTO et les événements de domaine sous forme de records afin de garantir l’immuabilité et d’améliorer la lisibilité du code. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer C# Academy ?

Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Records dans la conception pilotée par le domaine » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?

Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Types record : notions de base et syntaxe
  2. Immuabilité avec init et with
  3. Égalité des valeurs et déconstruction
  4. Records dans la conception pilotée par le domaine
← Retour à C# Academy