Slo248.Net.DDD 1.0.1

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

Slo248.Net.DDD

A lightweight, production-ready Domain-Driven Design (DDD) library for .NET 10.0+ providing foundational abstractions and base classes for building domain models using DDD principles. Features strongly-typed entities, aggregate roots, value objects, and domain event management.


NuGet License: MIT


Features

  • Aggregate Root Base Class – Abstract base class for implementing aggregate roots with built-in domain event tracking and management
  • Entity Base Class – Generic, type-safe entity implementation with strong identity types
  • Value Object Base Class – Immutable value objects with value-based equality and atomic value composition
  • Domain Event Support – Flexible domain event publishing and tracking within aggregates
  • Type-Safe EntityId – Generic, reusable strongly-typed entity identifiers with implicit conversions
  • Repository Pattern – Generic repository interface for data access abstraction
  • Event Sourcing Ready – Track uncommitted events with GetUncommittedEvents() and MarkAsCommitted() methods

Installation

Install via NuGet Package Manager:

dotnet add package Slo248.Net.DDD

Or via Package Manager Console:

Install-Package Slo248.Net.DDD

Quick Start

1. Define Entity Identifiers

Create strongly-typed entity identifiers by using the generic EntityId<TId, TEntity> class:

using Slo248.Net.DDD.Models;

public class Product : BaseEntity<Guid, Product>
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    public Product(EntityId<Guid, Product> id, string name, decimal price)
        : base(id)
    {
        Name = name;
        Price = price;
    }

    protected override void RaiseDomainEvent(IDomainEvent domainEvent)
    {
        // Handle domain events in the entity
    }
}

2. Define an Aggregate Root

Create an aggregate root to manage domain entities and domain events:

using Slo248.Net.DDD.Models;

public class Order : AggregateRoot<Guid, Order>
{
    private readonly List<OrderItem> _items = [];

    public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
    public OrderStatus Status { get; private set; }

    public Order(EntityId<Guid, Order> id) : base(id)
    {
        Status = OrderStatus.Pending;
    }

    public Order() : base()  // Generates a new Guid automatically
    {
        Status = OrderStatus.Pending;
    }

    public void AddItem(OrderItem item)
    {
        _items.Add(item);
        RaiseDomainEvent(new OrderItemAddedEvent(Id.Value, item.ProductId, item.Quantity));
    }
}

public enum OrderStatus { Pending, Completed, Cancelled }

3. Define a Domain Event

using Slo248.Net.DDD.Models;

public record OrderItemAddedEvent(Guid OrderId, Guid ProductId, int Quantity) : IDomainEvent;

4. Define a Value Object

using Slo248.Net.DDD.Models;

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

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

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

5. Create a Repository Implementation

using Slo248.Net.DDD.Models;
using Slo248.Net.DDD.Services;

public class OrderRepository : IRepository<Order, Guid>
{
    private readonly IDbContext _dbContext;
    private readonly IDomainEventPublisher _eventPublisher;

    public OrderRepository(IDbContext dbContext, IDomainEventPublisher eventPublisher)
    {
        _dbContext = dbContext;
        _eventPublisher = eventPublisher;
    }

    public async Task<Order?> Get(Guid id, CancellationToken cancellationToken = default)
    {
        return await _dbContext.Orders.FindAsync(new object[] { id }, cancellationToken);
    }

    public async Task Add(Order aggregate, CancellationToken cancellationToken = default)
    {
        await _dbContext.Orders.AddAsync(aggregate, cancellationToken);
        await _dbContext.SaveChangesAsync(cancellationToken);

        // Publish domain events
        var events = aggregate.GetUncommittedEvents().ToArray();
        if (events.Length > 0)
        {
            await _eventPublisher.Publish(events, cancellationToken);
            aggregate.MarkAsCommitted();
        }
    }

    public async Task Update(Order aggregateRoot, CancellationToken cancellationToken = default)
    {
        _dbContext.Orders.Update(aggregateRoot);
        await _dbContext.SaveChangesAsync(cancellationToken);

        var events = aggregateRoot.GetUncommittedEvents().ToArray();
        if (events.Length > 0)
        {
            await _eventPublisher.Publish(events, cancellationToken);
            aggregateRoot.MarkAsCommitted();
        }
    }

    public async Task Delete(Order aggregateRoot, CancellationToken cancellationToken = default)
    {
        _dbContext.Orders.Remove(aggregateRoot);
        await _dbContext.SaveChangesAsync(cancellationToken);
    }
}

6. Publish Domain Events

using MediatR;
using Slo248.Net.DDD.Services;

public class DomainEventPublisher : IDomainEventPublisher
{
    private readonly IMediator _mediator;

    public DomainEventPublisher(IMediator mediator)
    {
        _mediator = mediator;
    }

    public async Task Publish(IDomainEvent[] events, CancellationToken cancellationToken = default)
    {
        foreach (var domainEvent in events)
        {
            await _mediator.Publish((dynamic)domainEvent, cancellationToken);
        }
    }
}

public class OrderItemAddedEventHandler : INotificationHandler<OrderItemAddedEvent>
{
    public async Task Handle(OrderItemAddedEvent notification, CancellationToken cancellationToken)
    {
        // Handle the order item added event
        await Task.CompletedTask;
    }
}

Architecture

Core Components

  • BaseEntity<TId, TEntity> – Abstract base class for all domain entities with identity and domain event support
  • AggregateRoot<TId, TEntity> – Extends BaseEntity<TId, TEntity> for aggregate root implementation with domain event tracking
  • AggregateRoot<TEntity> – Convenience variant using Guid as the identity type
  • Entity<TId, TEntity> – Extends BaseEntity<TId, TEntity> for use within aggregates, with event propagation to the root
  • Entity<TEntity> – Convenience variant using Guid as the identity type
  • EntityId<TId, TEntity> – Strongly-typed generic identifier for entities with value-based equality
  • EntityId<TEntity> – Convenience variant for Guid-based identities
  • ValueObject – Base class for immutable value objects with atomic value composition
  • ValueObject<T> – Generic convenience variant for simple single-value value objects
  • IDomainEvent – Marker interface for domain events
  • IAggregateRoot – Interface for aggregate root contracts
  • IRepository<TAggregateRoot, TId> – Generic repository interface for data access abstraction
  • IDomainEventPublisher – Domain event publication abstraction for handling committed events

Requirements

  • .NET 10.0 or later
  • (Optional) MediatR 12.0 or later – for event handling integration (recommended but not required)

Package Metadata

Key Design Patterns

Event Sourcing

Aggregate roots track all domain events that occur within their boundary via GetUncommittedEvents(). After persisting the aggregate, call MarkAsCommitted() to clear the event list.

Value Objects

Use ValueObject for immutable objects that represent a concept and whose equality is based on atomic values, not identity.

Strong Typing

The generic EntityId<TId, TEntity> enables compile-time type safety. Entities can only reference other entities through their proper ID types.

Event-Driven Architecture

Domain events enable decoupling between aggregates. Use IDomainEventPublisher to handle events after aggregate persistence.

Usage Tips

  1. Keep Aggregates Small – Only include entities that must be modified together within a single transaction
  2. Use Value Objects – Encapsulate related values (e.g., Money, Address) as value objects for domain clarity
  3. Domain Events – Raise domain events for important state changes within aggregate roots
  4. Repository Pattern – Access aggregates only through repositories, never directly modify via ORM
  5. Event Publishing – Publish events after successful persistence to ensure consistency

Documentation

For more information on Domain-Driven Design principles, see:

License

This project is licensed under the MIT License. See the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a pull request or open an issue.

Support

If you encounter any issues or have questions, please open an issue on GitHub.

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 129 5/7/2026