PouryaKit
ReferencePackages

PouryaKit.Domain

The plain types your aggregates are made of.

The plain types your aggregates are made of. An aggregate derives from AggregateRoot<TId> and is keyed by a typed ID, a behaviour that can fail checks its business rules and returns a Result with a value or an Error for an expected business failure, and what happened is raised as a Domain Event. The package has no dependencies, so it adds nothing to your domain but these types.

Install

dotnet add package PouryaKit.Domain --prerelease

When to use it

  • You are writing an aggregate, an entity or the typed ID that keys it.
  • A business rule decides whether a change may happen.
  • Something in the domain happened that other code reacts to: a Domain Event.
  • You are declaring the Errors of a Business Area.
  • A business rule compares with the current time, or a test fixes the time a Domain Event records.

A Module's handlers return these types through PouryaKit.Cqrs, and PouryaKit.Persistence.Postgres stores its aggregates.

Quick start

There is nothing to register. A behaviour checks its rule, returns an Error for a business failure and raises a Domain Event on success, as the Proving Sample's Product does:

public static Result<Product> Create(
    ProductId id,
    CategoryId categoryId,
    string name,
    string tenant
)
{
    ArgumentNullException.ThrowIfNull(id);
    ArgumentNullException.ThrowIfNull(categoryId);
    ArgumentException.ThrowIfNullOrWhiteSpace(tenant);
    if (string.IsNullOrWhiteSpace(name))
    {
        return ProductErrors.NameRequired;
    }

    var product = new Product
    {
        Id = id,
        Tenant = tenant,
        CategoryId = categoryId,
        Name = name.Trim(),
    };
    product.Raise(new ProductCreatedDomainEvent(id, categoryId, product.Name));
    return product;
}

What you get

  • Result and Error: a value on success, one or more named Errors on failure.
  • Typed IDs: one sealed record per aggregate, over a Guid, long, int or string, so the compiler refuses one aggregate's ID where another's belongs.
  • Domain Events raised by the aggregate, for the Unit of Work to run.
  • Opt-in markers for audit and soft delete, whose fields the Unit of Work sets, and for a tenant's aggregates.
  • DomainClock, the time a Domain Event records, which a test can fix. A business rule that compares with now takes the time as a parameter.

Return an Error for anything an Actor can cause, such as a missing name. Exceptions are for bugs and infrastructure. Never set an aggregate's Version or clear its Domain Events: the Unit of Work owns both.

Learn more

On this page