Temp.DDDToolkit.FluentValidation 3.2.0-preview.6

This is a prerelease version of Temp.DDDToolkit.FluentValidation.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Temp.DDDToolkit.FluentValidation --version 3.2.0-preview.6
                    
NuGet\Install-Package Temp.DDDToolkit.FluentValidation -Version 3.2.0-preview.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="Temp.DDDToolkit.FluentValidation" Version="3.2.0-preview.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Temp.DDDToolkit.FluentValidation" Version="3.2.0-preview.6" />
                    
Directory.Packages.props
<PackageReference Include="Temp.DDDToolkit.FluentValidation" />
                    
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 Temp.DDDToolkit.FluentValidation --version 3.2.0-preview.6
                    
#r "nuget: Temp.DDDToolkit.FluentValidation, 3.2.0-preview.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 Temp.DDDToolkit.FluentValidation@3.2.0-preview.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=Temp.DDDToolkit.FluentValidation&version=3.2.0-preview.6&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Temp.DDDToolkit.FluentValidation&version=3.2.0-preview.6&prerelease
                    
Install as a Cake Tool

DDDToolkit

Source generators that remove the repetitive parts of domain driven design in .NET. You declare the intent with an attribute; the generator writes the base type, the equality members, the identifier plumbing, the persistence mapping and the API conversions.

Documentation: dylansnel.github.io/DDDToolkit, the docs/ folder as a site.

[AggregateRoot<Guid>("ORD")]
public partial class Order
{
    public Order(OrderId id, CustomerId customer) : base(id)
    {
        Customer = customer;
        RaiseDomainEvent(new OrderPlaced(id, customer));
    }

    public CustomerId Customer { get; private set; }

    public partial IReadOnlyList<OrderLine> Lines { get; }

    public void AddLine(OrderLine line) => _lines.Add(line);
}

That is the whole declaration. OrderId did not have to be written at all: naming the raw value generates it as an allocation-free identifier with parsing, comparison and JSON support. Order gets its base class, an optimistic concurrency version, a domain event list only it can write to, and a _lines backing field that Entity Framework maps directly while the outside world sees a read-only list.

Declare the identifier yourself when other aggregates, DTOs or API contracts refer to it, which gives it a file of its own to navigate to:

[EntityId<Guid>("ORD")]
public readonly partial record struct OrderId;

[AggregateRoot<OrderId>]
public partial class Order { }

Documentation

Page What it covers
Getting started One module built step by step: an id, a value object, an aggregate, a test, a database, a second module
What the generator writes The generated code for one small aggregate, file by file, and why it is generated
Identifiers [EntityId<T>], struct versus record ids, parsing, prefixes
Value objects [ValueObject], [SingleValueObject<T>], validation and the always-valid twin
Entities and aggregates [Entity<T>], [AggregateRoot<T>], child entities, read-only collections, referencing by id
Invariants Named IInvariant<T> rules and the CheckInvariants() seam, the two stages, and the save that runs them
Domain events Declaring, raising and draining events, and their stable names
Designing aggregates The four rules of aggregate design, and how the toolkit holds each one
Testing The aggregate testing kit: acting on an aggregate and asserting on what it raised
Failure handling Validating without exceptions, and when to throw anyway
Entity Framework Wiring, the generated converters, mapping, concurrency and migrations
Composite keys [KeyPart]: keying a table on more than the id, and carrying that into every owned table
Delivering domain events In-process dispatch or the outbox, and how to choose
Row level security Running a context's queries as the caller, and row access rules written in C# as Postgres policies
Supabase Exporting each module's migrations for supabase db push, as part of the build
Start-up checks What the registrations check before the host serves anything, run with one call, in a fixed order, and turned off by name
Modules DDD_Module, [assembly: Module] and the boundary the analyzer checks
Module contracts What a module publishes, why, and where to keep it
Access requirements What a command or a query requires of its caller, the checks that hold it to that before the handler, and the behavior written for Mediator
How a request is checked One request followed from the attribute on it to the rows it touches: the generated requirement, the module's checks, the statement the access check sends, the caller on the connection and the policies, with a walk-through to click through
Integration events Contracts between modules, the outbox and the inbox, versioning
Transports Carrying integration events out of the process: pgmq, Wolverine, MassTransit, or a sink of your own
Tenancy The first supporting domain: what it is for, what it does and does not do, and how its pieces connect
Tenancy: the tutorial Tenancy built into an application step by step: why each step, the whole files, and what they do
Tenancy: your module The module that holds Tenancy: its packages, the model, your own classes and ids, the context and the registration, and calling its managers
Tenancy: roles and permissions The permission catalogue, the starter roles a new tenant gets and how they follow your changes, who may give a role, and containment
Tenancy: who may do what Who acts and Tenancy's queries about the caller, what a request requires of its caller, and what a module reads of Tenancy
Tenancy: keeping tenants apart The query filter and the save check that keep every read and save in the caller's tenant, and system work
Tenancy: names and languages Turning the ids a module answers into names, how a seat is shown, and refusals and role names in the reader's language
Tenancy: invitations Inviting a person without an account yet into a tenant, with a unit and a role waiting
Tenancy: operators and history The application's operators across tenants, who wrote and changed each row, and the access history
Tenancy on Postgres Tenancy's rules as row level security policies and functions in Postgres, how to set them up, and what they stop
Tenancy: the sample The Tenancy sample: what is in it, the people and units of its seed, and its parts
Tenancy sample: who may do what The sample's modules in layers, a request from route to save, what each request declares, roles and crews, paging, operators and invitations
Tenancy sample: GraphQL One schema over the sample's three modules, what a query costs, the rules each module's schema keeps, and the administration's gateway
Tenancy sample: try it Running the sample, its UI, one call followed to its refusal, and the presets that each break one rule
Tenancy sample: design choices Every Tenancy design choice with its code, something to try in the sample, and the test that holds it
Tenancy: design and limits Why Tenancy is shaped the way it is, what it does not do, and upgrading from key to permission
Tenancy API: aggregates and entities Generated from the code: the templates of tenant, organization, unit, seat, role and invitation, with their value objects, invariants and events
Tenancy API: managers Generated from the code: TenantManager, OrganizationManager, SeatManager, RoleManager and InvitationManager, the stores a storage package implements, and the registration a host calls
Tenancy API: catalogue and starter roles Generated from the code: permissions, the catalogue, starter roles and their sync
Tenancy API: access manager and users Generated from the code: TenancyAccessManager, the Tenancy user, actors, tenant selection and the requirement attributes, [Operator] among them
Tenancy API: queries and row access Generated from the code: db.Tenancy() and its queries, the read model, the projection and the row access rules
Tenancy API: refusals Generated from the code: every refusal code and what it means
Tenancy API: Entity Framework Generated from the code: the public API of the Entity Framework package, with [OfTenant] and the mapping of a context
Tenancy API: Postgres Generated from the code: the public API of the Postgres package
Membership The second supporting domain: access to one thing through the people on it, what it does and does not do, how its pieces connect, and what the application decides
Membership: the tutorial Membership built into the Harbor application step by step: a board with its members, owner and roles, its database rules, and reach from above
Membership: resources and members The packages, a whole resource with members and no Tenancy, the member list written for you, names, what the member list keeps, and several kinds of resource
Membership: permissions and roles The settings of a permission, who holds one, who may give a role, and roles a customer makes: starter roles, changing a role, whose a role is
Membership: from the check to the save Requirements that combine, what holds a change from the check to the save, MembershipAccessManager and the queries inside your own statements, system work, and the expert hold
Membership on Postgres The functions your row access rules ask, who may ask them, the start-up check, and the lock on the member rows and the owner
Membership: beside an organization Who a member is, where the roles come from and reach from above, with Tenancy or an organization of your own
Membership: design and limits Why each piece of a resource with members is yours to write, and what the package does not do
Membership API: resource and members Generated from the code: the member template and its member list, periods and roles held, what the resource's class says, and the custom role template
Membership API: rules and permissions Generated from the code: MembershipRules, where the roles come from, the permissions on a resource and their settings, reach from above, and who a member is
Membership API: managers and access Generated from the code: the member manager, the role manager, who may be a member, MembershipAccessManager, the requirements and the resource access contracts
Membership API: refusals Generated from the code: every refusal code, the codes of one resource, and their texts
Membership API: Entity Framework Generated from the code: the public API of the Entity Framework package
Membership API: Postgres Generated from the code: the public API of the Postgres package
Writing your own supporting domain Shipping a domain as a package that applications extend with their own ids and classes, without losing its rules
GraphQL AddDDDToolkitTypes(), the generated scalar bindings, errors with codes, Relay node ids, one schema over the modules
FluentValidation A value object's rules as a FluentValidation validator, and value objects inside a request validator
Localization Validation errors and invariant violations in the reader's language, looked up by code
Performance The benchmarks behind the struct-versus-record advice, including where they disagree with it
Diagnostics Every DDD000xx diagnostic and how to fix it
Migrating to 3.0 Every 2.x break, with the before and the after

With an AI coding agent

An agent that has never seen the toolkit writes the base class the generator already writes, a settable collection, a sealed value object. Three things teach it otherwise.

A skill. skills/dddtoolkit holds the declarations, the rules the generators enforce, the wiring for Entity Framework and modules, and the fix for every DDD diagnostic. It is an Agent Skill, the format Claude Code, Codex, Cursor and GitHub Copilot read. This repository is also a Claude Code plugin marketplace, so in Claude Code:

/plugin marketplace add DylanSnel/DDDToolkit
/plugin install dddtoolkit@dddtoolkit

For another agent, npx skills add DylanSnel/DDDToolkit installs it, or copy the folder into the agent's skills directory.

The docs as text. The site serves llms.txt, an index of the pages with what each covers, and llms-full.txt, all of them in one file. Every page is also plain Markdown at its own address with .md added, such as docs/invariants.md.

The diagnostics. Every DDD diagnostic carries a link to its entry in Diagnostics, so an IDE opens it from the error list and an agent that reads the build output can follow it.

Packages

Reference DDDToolkit and add the integrations you actually use. Each integration package brings its own generator, so referencing it is all the configuration there is.

For now 3.x is published under Temp. ids: Temp.DDDToolkit, Temp.DDDToolkit.EntityFramework, and so on, each package in the table below with Temp. in front of its name. The nuget.org account that owns the DDDToolkit.* ids cannot publish at the moment, so DDDToolkit there is still 2.0.22. Only the package id is different. The assemblies and namespaces are the ones this page names, so using DDDToolkit; stays as it is.

3.2.0 is in preview. This page and the docs describe it: 3.2.0-preview.6 as they are written. Until 3.2.0 is released, add every Temp.DDDToolkit.* package with --prerelease, or with --version 3.2.0-preview.6, and keep them all at the same version. A package of this release next to 3.1.0 of another fails restore with NU1605, a package downgrade, or fails at run time with a MissingMethodException.

Package Use it for
DDDToolkit Base types and the core generators. Start here.
DDDToolkit.Abstractions The attributes and marker interfaces alone, for projects that must not reference the runtime. With DDDToolkit.Analyzers beside it the generators run there too, which is what a contracts project wants.
DDDToolkit.EntityFramework Value converters, model conventions, domain event dispatch, invariant checks, optimistic concurrency, the outbox and the inbox.
DDDToolkit.Messaging.Postgres A pgmq sink, so the outbox enqueues inside the same Postgres transaction that writes the aggregate, and a consumer that reads a queue into the modules' inboxes.
DDDToolkit.Messaging.Wolverine Wolverine as the transport between one process's outbox and another's inbox.
DDDToolkit.Messaging.MassTransit MassTransit 8 as that transport, for those already on it.
DDDToolkit.EntityFramework.Postgres Row level security on any Postgres: each connection a context opens runs as the current user (ICurrentUser), as PostgREST's do, and [RowAccess] rules written in C# become the policies.
DDDToolkit.EntityFramework.Supabase Your Entity Framework migrations and row access rules written as Supabase migration files by the build, so supabase db push and branching apply them, and a CI build that fails when one is missing.
DDDToolkit.Auth.Supabase Supabase Auth's access tokens validated in any host, against the keys the project publishes or its JWT secret, and turned into the current user row level security runs your queries as. No Entity Framework needed. For server code, a client for Auth's admin API: invite an address, make a user under an id of your own, and find or delete one by id.
DDDToolkit.Auth.Supabase.AspNetCore The same in ASP.NET Core: a JWT bearer scheme for Supabase Auth, and each request's user as the current user.
DDDToolkit.Auth.Supabase.AzureFunctions The same in Azure Functions on the isolated worker: a worker middleware that makes each HTTP invocation's user the current user.
DDDToolkit.Mediator One call that dispatches domain events through Mediator instead of a hand-written delegate.
DDDToolkit.FluentValidation A generated validator per value object.
DDDToolkit.Localization Validation errors and invariant violations phrased in the reader's language, through IStringLocalizer.
DDDToolkit.HotChocolate GraphQL scalar bindings and converters for typed identifiers, failures as coded errors and as typed errors in mutation payloads, plus a subscription sink. Needs HotChocolate 16.6.9 or later.
DDDToolkit.HotChocolate.Fusion.InMemory One GraphQL schema over a modular monolith: each module a source schema, composed by a Fusion gateway in the process. Needs HotChocolate Fusion 16.6.9 or later.
DDDToolkit.NewtonSoft.Json Newtonsoft converters and a contract resolver that honours [Internal].
DDDToolkit.Supporting.Tenancy Tenancy, a supporting domain: tenants, their organization tree, seats and roles over a permission catalogue, a manager per aggregate, and the access manager. No database in it.
DDDToolkit.Supporting.Tenancy.Abstractions Tenancy's markers for domain classes, [OfTenant] on the property that says which tenant a row belongs to, for a module's domain project that sees only Tenancy's ids. Tenancy's package references it.
DDDToolkit.Supporting.Tenancy.EntityFramework Tenancy in a context of your own, with its queries as Entity Framework queries (db.Tenancy()), on every provider, and the convention that maps [OfTenant].
DDDToolkit.Supporting.Tenancy.Postgres Tenancy's queries as SQL functions, its row level security policies, and the start-up checks.
DDDToolkit.Supporting.Membership Membership, a supporting domain: access to a resource through its members, the roles they hold for a period, and roles a customer makes. No database in it. Its generator writes the member list on your aggregate.
DDDToolkit.Supporting.Membership.Abstractions Membership's markers for domain classes, [At] on the property that says where a resource hangs. Membership's package references it.
DDDToolkit.Supporting.Membership.EntityFramework A resource's members in a context of your own, mapped by convention, a registration named after each resource, and queries inside your own statements (db.Boards.WhereIHold(permission)). Its generator joins a resource to Tenancy where an application has both.
DDDToolkit.Supporting.Membership.Postgres A resource's access queries as SQL functions for your row access rules, and a start-up check.
DDDToolkit.Testing The aggregate testing kit. A test-only reference; it brings no test framework of its own.
dotnet add package Temp.DDDToolkit --prerelease

There are five more packages you never reference directly: DDDToolkit.Analyzers and the .EntityFramework, .EntityFramework.Supabase, .FluentValidation and .HotChocolate analyzer packages beside it. Each one carries the generators for its integration and arrives as a dependency of the package above, so adding DDDToolkit.HotChocolate is all it takes to get the GraphQL generator.

Requires .NET 10. The generators themselves target netstandard2.0 and carry no runtime dependencies, so they load in any recent SDK.

The core has no mediator dependency and does not need one: in-process event delivery is a delegate you write, and DDDToolkit.Mediator only saves you writing it. The examples publish through Mediator rather than MediatR because MediatR is commercially licensed from version 13, and this repository prefers dependencies its users can take for free. MediatR still works perfectly well with the toolkit; Delivering domain events shows the delegate to write for it.

What the generators produce

You write You get
[EntityId<T>] on a readonly partial record struct Value, a constructor, Empty/IsEmpty, Parse/TryParse, IParsable<T>, IComparable<T>, explicit conversions, a JSON converter, and for Guid a time-ordered Create(), which implements ICreatableEntityId<T>, and CreateUnique/CreateSequential
[EntityId<T>] on a partial record A reference type identifier: Value, equality over it, Parse/TryParse, for Guid a time-ordered Create() (ICreatableEntityId<T>) and CreateUnique/CreateSequential, and a Valid twin. It has null rather than Empty, and no conversion operators
[SingleValueObject<T>] on a partial record A wrapper with value equality, a Valid twin and validation
[ValueObject] on a partial record, positional or with { get; protected init; } properties Structural equality across the properties you did not exclude, a With(...) for changed copies, and a Valid twin
[Entity<TId>] / [AggregateRoot<TId>] on a partial class The base type, a persistence constructor, a CheckInvariants() seam, GetInvariantViolations() and EnsureInvariants() over it, over any nested IInvariant<T> rules and over every child entity it holds, an EnsureOwnInvariants() / GetOwnInvariantViolations() pair that stops at this object, and an implementation for every get-only partial collection property

Add DDDToolkit.EntityFramework and the same declarations also produce value converters, [Owned] and [ComplexType] annotations, and a single Add<Module>Converters call for your DbContext. Add DDDToolkit.HotChocolate and they produce GraphQL type bindings. You never write that code.

Beyond the declarations

Three things the toolkit does that are not a generated member.

Invariants. State a rule in the generated partial void CheckInvariants() seam, or as a nested IInvariant<T> when it deserves a name, a code and a test of its own. There are two moments to ask. GetInvariantViolations() answers with a list and never throws, for the application that wants to handle "not consistent yet"; EnsureInvariants() throws, and an interceptor runs the same check before every SaveChanges that writes the entity, child entities included, so a broken rule stops the save. Asking the aggregate root asks the whole aggregate, its children too, each violation naming the entity that reported it, so a handler can act on an aggregate and ask what that broke without a DbContext taking part. State nothing and the compiler erases the seam, so an aggregate with no invariants pays nothing.

Integration events. The outbox writes one row per event in the same transaction as the aggregate. A sink delivers it afterwards: to another module in this process, to a pgmq queue, to a GraphQL subscription, or to one you wrote. Versioning and upcasting keep last year's payload readable, and an inbox keyed by message id and consumer makes the receiving side idempotent.

Modules. Declare a project's module with <DDD_Module>Ordering</DDD_Module>, once for a folder of projects in a Directory.Build.props, or with [assembly: Module("Ordering")], and name what it publishes with [ModuleContract]. An analyzer then reports where another module reaches past the contract. It says nothing at all until both sides opt in.

Status

Version 3.0 is a breaking release. Identifiers may be structs, domain events moved from Entity to AggregateRoot, IDomainEvent carries an id and a timestamp, aggregates gained an invariant seam and a concurrency version, and misapplied attributes now report a diagnostic instead of silently generating nothing.

Coming from 2.0.22, read Migrating to 3.0: it lists every break with the code you have and the code you need. The changelog has the rest.

License

MIT. See LICENSE.

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.

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
3.2.0-preview.7 0 10/29/2026
3.2.0-preview.6 0 10/29/2026
3.2.0-preview.5 51 10/26/2026
3.2.0-preview.4 49 10/26/2026
3.2.0-preview.3 57 10/9/2026
3.2.0-preview.2 70 10/5/2026
3.2.0-preview.1 69 10/4/2026
3.1.0 98 10/1/2026
3.0.1 93 10/1/2026
3.0.0 100 9/27/2026