Demarbit.Shared.Domain 1.0.6

dotnet add package Demarbit.Shared.Domain --version 1.0.6
                    
NuGet\Install-Package Demarbit.Shared.Domain -Version 1.0.6
                    
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="Demarbit.Shared.Domain" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Demarbit.Shared.Domain" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="Demarbit.Shared.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 Demarbit.Shared.Domain --version 1.0.6
                    
#r "nuget: Demarbit.Shared.Domain, 1.0.6"
                    
#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 Demarbit.Shared.Domain@1.0.6
                    
#: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=Demarbit.Shared.Domain&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=Demarbit.Shared.Domain&version=1.0.6
                    
Install as a Cake Tool

Demarbit.Shared.Domain

Shared domain building blocks for DDD and Clean Architecture in .NET — entities, aggregates, value objects, domain events, and guards.

CI/CD Quality Gate Status NuGet

What's Included

Entities and AggregatesEntityBase<TId> and AggregateRoot<TId> base classes with identity, equality-by-ID, built-in audit fields, and domain event collection. Guid convenience aliases (EntityBase, AggregateRoot) auto-generate IDs.

Value ObjectsValueObject base class with structural equality. Subclasses define equality through GetEqualityComponents().

Domain EventsIDomainEvent contract and DomainEventBase record with auto-generated metadata (EventId, OccurredOn, EventType, Version). Supports with expressions for deterministic testing.

ContractsIRepository<T, TId>, IUnitOfWork, IAuditableEntity, IAggregateRoot, ICurrentUserProvider, ICurrentTenantProvider, ITenantEntity, and IEventIdempotencyService. Pure abstractions — no infrastructure dependencies.

Guards — Static Guard class for argument validation and domain invariants. Uses [CallerArgumentExpression] to capture parameter names automatically.

ExceptionsDomainException for domain rule violations with an optional ErrorCode for structured error handling.

ExtensionsToSlug() string extension for URL-friendly slug generation with diacritics removal.

Quick Start

Entities

Inherit from EntityBase (Guid ID) or EntityBase<TId> (custom ID type). Audit fields and equality come built in.

public class Customer : EntityBase
{
    public string Name { get; private set; }

    public Customer(string name)
    {
        Guard.NotNullOrWhiteSpace(name);
        Name = name;
    }
}

Aggregate Roots

Inherit from AggregateRoot to get domain event support on top of entity features. Raise events inside domain methods — they queue until the unit of work dispatches them.

public class Order : AggregateRoot
{
    public string CustomerName { get; private set; }
    public OrderStatus Status { get; private set; }

    public Order(string customerName)
    {
        Guard.NotNullOrWhiteSpace(customerName);
        CustomerName = customerName;
        Status = OrderStatus.Placed;
        RaiseDomainEvent(new OrderPlaced { OrderId = Id });
    }

    public void Cancel()
    {
        Guard.Against(Status == OrderStatus.Shipped, "Cannot cancel a shipped order.");
        Status = OrderStatus.Cancelled;
        RaiseDomainEvent(new OrderCancelled { OrderId = Id });
    }
}

Domain Events

Use DomainEventBase as a record base. Metadata is auto-generated — override with with for testing.

public sealed record OrderPlaced : DomainEventBase
{
    public Guid OrderId { get; init; }
}

// In a test with deterministic time:
var evt = new OrderPlaced { OrderId = id } with { OccurredOn = fixedTime };

Value Objects

Subclass ValueObject and implement GetEqualityComponents(). Two value objects are equal when all components match.

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

    public Money(decimal amount, string currency)
    {
        Guard.GreaterThanOrEqualTo(amount, 0m, "Amount cannot be negative.");
        Guard.NotNullOrWhiteSpace(currency);
        Amount = amount;
        Currency = currency;
    }

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

Guard Clauses

Validate arguments and enforce domain invariants. Parameter names are captured automatically.

Guard.NotNull(customer);                              // throws ArgumentNullException
Guard.NotNullOrWhiteSpace(name);                      // throws ArgumentException
Guard.Against(age < 0, "Age cannot be negative.");    // throws ArgumentException
Guard.Between(quantity, 1, 100, "Quantity out of range.");
Guard.MustBe(email, e => e.Contains('@'), "Invalid email.");
Guard.NotEmpty(lineItems);                            // throws on null or empty collection

Repository Contract

Define aggregate-specific repositories by extending IRepository<T> (Guid) or IRepository<T, TId> (custom ID):

public interface IOrderRepository : IRepository<Order>
{
    Task<List<Order>> GetByCustomerAsync(string customerName, CancellationToken ct = default);
}

The base interface provides GetByIdAsync, GetAllAsync, AddAsync, AddRangeAsync, UpdateAsync, UpdateRangeAsync, RemoveAsync, RemoveRangeAsync, and RemoveByIdAsync.

Unit of Work

IUnitOfWork coordinates transactional persistence and domain event collection:

await unitOfWork.BeginTransactionAsync(ct);
await orderRepository.AddAsync(order, ct);
await unitOfWork.SaveChangesAsync(ct);
var events = unitOfWork.GetAndClearPendingEvents();
await unitOfWork.CommitTransactionAsync(ct);

// Dispatch events after commit
await dispatcher.NotifyAsync(events, ct);

Multi-Tenancy

Mark aggregates or entities with ITenantEntity and resolve the current tenant with ICurrentTenantProvider:

public class Project : AggregateRoot, ITenantEntity
{
    public Guid TenantId { get; private set; }

    public Project(Guid tenantId, string name)
    {
        TenantId = tenantId;
        Name = name;
    }
}

Domain Exceptions

Throw DomainException for domain rule violations. The optional ErrorCode enables structured error mapping:

throw new DomainException("Order has already been shipped.");
throw new DomainException("Insufficient stock.", "INSUFFICIENT_STOCK");

Event Idempotency

IEventIdempotencyService and ProcessedEvent prevent duplicate event handling:

if (await idempotencyService.HasBeenProcessedAsync(evt.EventId, nameof(MyHandler), ct))
    return;

// Handle the event...

await idempotencyService.MarkAsProcessedAsync(evt.EventId, evt.EventType, nameof(MyHandler), ct);

Slug Generation

Convert strings to URL-friendly slugs with diacritics removal:

"Café au Lait".ToSlug()   // "cafe-au-lait"
"Hello World!".ToSlug()   // "hello-world"
"Ürban Köln".ToSlug()     // "urban-koln"

Design Principles

  • Zero dependencies — depends only on the .NET base class library. No NuGet packages, no infrastructure concerns.
  • Identity by ID — entities use equality-by-ID semantics. Value objects use structural equality.
  • Audit built in — every entity tracks CreatedAt, UpdatedAt, CreatedBy, UpdatedBy through IAuditableEntity.
  • Events stay in the domain — aggregates raise events, the unit of work collects them, infrastructure dispatches them. The domain layer never depends on the dispatcher.
  • Pure contractsIRepository, IUnitOfWork, and provider interfaces define what the domain needs without dictating how it's implemented.
  • Guard clauses over exceptions in constructorsGuard keeps validation expressive and consistent across the domain.

Architecture Fit

Demarbit.Shared.Domain          ← this package (zero deps)
    ↑
Demarbit.Shared.Application     ← dispatching, pipeline, validation
    ↑
Demarbit.Shared.Infrastructure  ← EF Core, repositories, event dispatch
    ↑
[Your Application Project]      ← references what it needs

License

MIT

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 (3)

Showing the top 3 NuGet packages that depend on Demarbit.Shared.Domain:

Package Downloads
Demarbit.Shared.Application

Opinionated shared application layer for .NET projects following Clean Architecture principles. Provides a lightweight CQRS dispatcher with pipeline behaviors (logging, validation, transactions), structured exceptions, and common application models.

Demarbit.Shared.Infrastructure

A lightweight Result pattern library for .NET 10. Replace exceptions as control flow with explicit Result types — designed for CQRS pipelines and Clean Architecture.

Demarbit.Shared.Api

Shared ASP.NET Core API library for DDD / Clean Architecture projects. Provides a configurable ProblemDetails exception filter, correlation ID middleware, security headers middleware, session resolution middleware, OpenAPI transformers for Optional<T>, and DI/pipeline wiring.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.6 107 2/24/2026
1.0.4 139 2/20/2026
1.0.3 86 2/20/2026
1.0.2 85 2/20/2026
1.0.1 95 2/20/2026
1.0.0 103 2/18/2026