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
<PackageReference Include="CleanArch.DevKit.Domain" Version="1.1.1" />
<PackageVersion Include="CleanArch.DevKit.Domain" Version="1.1.1" />
<PackageReference Include="CleanArch.DevKit.Domain" />
paket add CleanArch.DevKit.Domain --version 1.1.1
#r "nuget: CleanArch.DevKit.Domain, 1.1.1"
#:package CleanArch.DevKit.Domain@1.1.1
#addin nuget:?package=CleanArch.DevKit.Domain&version=1.1.1
#tool nuget:?package=CleanArch.DevKit.Domain&version=1.1.1
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é transienteValueObject— égalité structurelle pour les value objects avec comportementAggregateRoot<TId>— buffer d'événements de domaineIDomainEvent— 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 | Versions 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. |
-
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 |