CleanArch.DevKit.Domain 1.1.1

dotnet add package CleanArch.DevKit.Domain --version 1.1.1
                    
NuGet\Install-Package CleanArch.DevKit.Domain -Version 1.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CleanArch.DevKit.Domain" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CleanArch.DevKit.Domain" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="CleanArch.DevKit.Domain" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CleanArch.DevKit.Domain --version 1.1.1
                    
#r "nuget: CleanArch.DevKit.Domain, 1.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CleanArch.DevKit.Domain@1.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CleanArch.DevKit.Domain&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=CleanArch.DevKit.Domain&version=1.1.1
                    
Install as a Cake Tool

CleanArch.DevKit.Domain

Primitives DDD pures : entité, racine d'agrégat, événement de domaine. Aucune dépendance externe.

Rôle

Fournit les classes de base nécessaires pour modéliser le domaine selon Domain-Driven Design. Le package est volontairement minimaliste : pas de couplage à EF Core, au médiateur, ou à un framework de DI. Une racine d'agrégat sait bufferiser ses événements ; un autre composant (par exemple le pont CleanArch.DevKit.Mediator.Domain) est responsable de les dispatcher.

Installation

dotnet add package CleanArch.DevKit.Domain

Aucune dépendance NuGet.

Fonctionnalités

  • Entity<TId> — identité, égalité par Id, entité transiente
  • ValueObject — égalité structurelle pour les value objects avec comportement
  • AggregateRoot<TId> — buffer d'événements de domaine
  • IDomainEvent — marqueur pur (aucune dépendance dispatcher)
  • IEntity<TId> / IAggregateRoot — interfaces pour l'introspection / unit of work

Entity<TId> — identité et égalité

Deux entités sont égales si elles ont le même type runtime et le même Id non-défaut. Les entités transientes (Id à default) ne sont égales qu'à elles-mêmes par référence.

public sealed class Product : Entity<int>
{
    public string Name { get; private set; }

    private Product() { } // pour EF Core / ORM

    public Product(int id, string name) : base(id)
    {
        Name = name;
    }
}

var a = new Product(1, "Cup");
var b = new Product(1, "Mug");
Console.WriteLine(a == b);          // True (même Id)
Console.WriteLine(a.GetHashCode());  // combiné de typeof + Id

L'API publique :

Membre Rôle
Id L'identifiant (init protégé)
IsTransient() true quand Id == default
Equals, ==, !=, GetHashCode Cohérents avec l'égalité par Id

Le paramètre TId doit implémenter IEquatable<TId>. Pour les IDs strongly-typed :

public sealed record ProductId(Guid Value) : IEquatable<ProductId>;

public sealed class Product : Entity<ProductId> { /* ... */ }

ValueObject — égalité structurelle

ValueObject est la base des value objects avec comportement — quand un simple record ne suffit pas (méthodes de validation, factories statiques, invariants vérifiés à la construction). L'égalité est définie par la séquence retournée par GetEqualityComponents().

public sealed class Money : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }

    private Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public static Money Of(decimal amount, string currency)
    {
        if (amount < 0) throw new ArgumentException("Negative amount", nameof(amount));
        if (string.IsNullOrWhiteSpace(currency)) throw new ArgumentException("Currency required", nameof(currency));
        return new Money(amount, currency.ToUpperInvariant());
    }

    public Money Add(Money other)
    {
        if (Currency != other.Currency) throw new InvalidOperationException("Currency mismatch");
        return new Money(Amount + other.Amount, Currency);
    }

    protected override IEnumerable<object?> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }
}

var a = Money.Of(10, "EUR");
var b = Money.Of(10, "eur");
Console.WriteLine(a == b);  // True (mêmes composants après normalisation)

Règles :

Aspect Comportement
Égalité Même type runtime et GetEqualityComponents() séquences égales élément par élément
null dans les composants Géré (deux null à la même position sont égaux)
GetHashCode Combine GetType() + tous les composants — stable avec Equals
Type runtime Strict : un Money n'est jamais égal à un Weight même avec les mêmes composants

Quand préférer record : pour des value objects purement composés (sans validation, sans normalisation, sans méthodes de domaine). record génère l'égalité structurelle automatiquement et c'est plus concis. Utilise ValueObject quand tu as besoin de cacher le constructeur derrière une factory, de normaliser des composants, ou de porter du comportement métier.


AggregateRoot<TId> — buffer d'événements

AggregateRoot<TId> étend Entity<TId> avec un buffer interne de IDomainEvent. Les méthodes du domaine appellent Raise(...) pour signaler ce qui s'est passé ; un composant externe drain le buffer après le commit.

public sealed class User : AggregateRoot<UserId>
{
    public string Email { get; private set; }

    private User() { }
    private User(UserId id, string email) : base(id) { Email = email; }

    public static User Create(string email)
    {
        var user = new User(UserId.New(), email);
        user.Raise(new UserCreated(user.Id, email));
        return user;
    }

    public void ChangeEmail(string newEmail)
    {
        var old = Email;
        Email = newEmail;
        Raise(new UserEmailChanged(Id, old, newEmail));
    }
}

L'API publique (héritée de IAggregateRoot) :

Membre Rôle
Raise(IDomainEvent) Ajoute un événement au buffer (protected)
DomainEvents Liste read-only des événements bufferisés
ClearDomainEvents() Vide le buffer après publication

Un unit of work typique :

foreach (var aggregate in changeTracker.GetAggregates())
{
    foreach (var evt in aggregate.DomainEvents)
        // dispatch...
    aggregate.ClearDomainEvents();
}

IDomainEvent — marqueur pur

public interface IDomainEvent { }

Aucune méthode, aucune dépendance — c'est volontaire. Le but est que la couche domaine n'ait rien à savoir sur le dispatch. Pour dispatcher via le médiateur, voir le package CleanArch.DevKit.Mediator.Domain qui fournit l'interface IDomainEventNotification (qui combine IDomainEvent et INotification) et l'extension PublishDomainEventsAsync.

Un événement de domaine est typiquement un record :

public sealed record UserCreated(UserId Id, string Email) : IDomainEvent;
public sealed record UserEmailChanged(UserId Id, string Old, string New) : IDomainEvent;

IEntity<TId> et IAggregateRoot — interfaces

Pour les composants génériques qui n'ont pas besoin de connaître le type d'Id (ex. unit of work, change tracker) :

public interface IEntity<out TId>
{
    TId Id { get; }
}

public interface IAggregateRoot
{
    IReadOnlyList<IDomainEvent> DomainEvents { get; }
    void ClearDomainEvents();
}

IAggregateRoot est non-générique pour qu'un dispatcher puisse itérer sur tous les agrégats trackés sans connaître leurs types d'Id.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on CleanArch.DevKit.Domain:

Package Downloads
CleanArch.DevKit.Mediator.Domain

Bridge between CleanArch.DevKit.Domain and CleanArch.DevKit.Mediator: IDomainEventNotification (combines IDomainEvent + INotification) and a typed PublishDomainEventsAsync extension on IMediator. Install this only if you use both Domain and Mediator together. Part of the CleanArch.DevKit set.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 103 5/17/2026
1.1.0 96 5/17/2026
1.0.0 107 5/15/2026
0.1.0-preview.1 51 5/14/2026