Odex.AspNetCore.Clarc.Domain
0.2.0
dotnet add package Odex.AspNetCore.Clarc.Domain --version 0.2.0
NuGet\Install-Package Odex.AspNetCore.Clarc.Domain -Version 0.2.0
<PackageReference Include="Odex.AspNetCore.Clarc.Domain" Version="0.2.0" />
<PackageVersion Include="Odex.AspNetCore.Clarc.Domain" Version="0.2.0" />
<PackageReference Include="Odex.AspNetCore.Clarc.Domain" />
paket add Odex.AspNetCore.Clarc.Domain --version 0.2.0
#r "nuget: Odex.AspNetCore.Clarc.Domain, 0.2.0"
#:package Odex.AspNetCore.Clarc.Domain@0.2.0
#addin nuget:?package=Odex.AspNetCore.Clarc.Domain&version=0.2.0
#tool nuget:?package=Odex.AspNetCore.Clarc.Domain&version=0.2.0
Odex.AspNetCore.Clarc.Domain
Reusable domain-layer building blocks for .NET: aggregates, domain events, specifications, guard policies, persistence-oriented repository contracts, and transaction abstractions. The public API is fully documented with XML comments for IntelliSense and NuGet. No ASP.NET Core or EF Core dependency—this assembly targets plain net9.0 so you can use it from any host or stack.
Table of contents
- About
- Installation
- Requirements
- How it fits in CLARC / clean architecture
- Capabilities
- API documentation
- Examples
- Namespaces
- Upgrading
- Contributing
- Community
- License
- Links
About
Odex.AspNetCore.Clarc.Domain is part of the CLARC family of packages. It gives you shared types and interfaces so domain code stays consistent across solutions: typed aggregate roots, optional lifecycle state, composable query predicates (Specification<T>), lightweight aggregate events (raised in the domain, dispatched in your infrastructure), guard-style checks (BasePolicy<T>), and async repository contracts you implement against your chosen store.
The package is opinionated about structure, not about frameworks. You wire dependency injection, ORMs, outboxes, and mediators in Application and Infrastructure—not in this library.
Installation
dotnet add package Odex.AspNetCore.Clarc.Domain
Pin a version when you need a reproducible build:
dotnet add package Odex.AspNetCore.Clarc.Domain --version 0.2.0
Requirements
| Item | Version |
|---|---|
| Target framework | net9.0 |
| .NET SDK | 9.x (for development and consumption aligned with the target) |
This library has no NuGet dependencies at runtime beyond the .NET runtime. The package build references Microsoft.SourceLink.GitHub (private asset) so consumers get GitHub-backed source debugging in supported IDEs. The same build emits XML API documentation for all public types and members (see API documentation). Your Infrastructure project references EF Core, Dapper, or other stacks as needed.
To build and test this repository locally, see CONTRIBUTING.md.
How it fits in CLARC / clean architecture
| Layer | Responsibility | Typical CLARC package |
|---|---|---|
| Domain | Entities, value objects, domain services, invariants, aggregate events (raised, not dispatched) | This package |
| Application | Use cases, commands/queries, handlers, orchestration | Odex.AspNetCore.Clarc.Application (your solution) |
| Infrastructure | Persistence, external APIs, event dispatch, DI registrations | Odex.AspNetCore.Clarc.Infrastructure (your solution) |
Repository interfaces (IAggregateRepository<,>) use IQueryable / Expression<> so implementations can translate predicates to SQL (for example with EF Core). That is a deliberate trade-off: the contract stays persistence-oriented while remaining store-agnostic at compile time.
Capabilities
| Area | Types (summary) |
|---|---|
| Aggregates | BaseAggregate<TId> — identity, audit timestamps, in-memory domain event list. StatefulAggregate<TId> — activation and soft-delete flags with timestamps. |
| Events | IAggregateEvent, AggregateEvent — correlation id and OccurredOn; collect with AddEvent, read with ListEvents, clear after dispatch in infrastructure. |
| Specifications | Specification<T> with And / Or / Not; ToExpression() for providers that compile LINQ expressions. |
| Policies | IBasePolicy<T>, BasePolicy<T> — RequireNotNull, RequireNotNullNorEmpty, RequireNullOrEmpty, RequireTrue / RequireFalse, etc. |
| Repositories | IBaseRepository — SaveChangesAsync, ExecuteInTransactionAsync. IAggregateRepository<TEntity,TId> — async reads/writes, includes, attach helpers. |
| Transactions | ITransactionContext — cooperative rollback signalling for unit-of-work implementations. |
| Exceptions | DomainException, PolicyViolationException, EntityNotFoundException, ConcurrencyException, InvalidEntityStateException with ExceptionType. |
| Value objects / DTO markers | BaseValueObject, BaseRequest, BaseResponse, PagedRequest, PagedResponse<T>, BaseData. |
API documentation
The public API is documented with XML Doc Comments (/// summaries, parameters, type parameters, return values, and documented exceptions where relevant). The project enables GenerateDocumentationFile, so each NuGet release ships Odex.AspNetCore.Clarc.Domain.xml next to the assembly. That file powers:
- IDE IntelliSense (Visual Studio, Rider, VS Code with C# Dev Kit)
- NuGet.org API reference and tooltips for consumers
If you extend or change public types, keep documentation in sync; see CONTRIBUTING.md.
Examples
Aggregate and policy
public class Product : StatefulAggregate<Guid>
{
public string Name { get; private set; } = string.Empty;
public void Rename(string newName)
{
var policy = new BasePolicy<string>();
policy.RequireNotNullNorEmpty(newName);
Name = newName;
MarkModified();
AddEvent(new ProductRenamedEvent(Id, newName));
}
}
Define ProductRenamedEvent as a type implementing IAggregateEvent (or inheriting AggregateEvent with extra payload properties).
Specification
public sealed class ActiveUsersSpec : Specification<User>
{
public override Expression<Func<User, bool>> ToExpression()
=> u => u.IsActive;
}
// Compose and pass the expression to your repository implementation
var spec = new ActiveUsersSpec().And(new UserNameContainsSpec("alex"));
await repository.ListAsync(spec.ToExpression(), cancellationToken);
Application service with AggregateService
AggregateService depends on IBaseRepository. Your aggregate repository implementation should implement both IAggregateRepository<T, TId> and the same unit-of-work boundary as IBaseRepository.
public sealed class ProductService(IAggregateRepository<Product, Guid> products)
: AggregateService(products)
{
public async Task<Product> CreateAsync(string name, CancellationToken cancellationToken = default)
{
var product = new Product(name);
await products.AddAsync(product, cancellationToken);
await SaveRepositoryChangesAsync(cancellationToken);
return product;
}
}
Transaction boundary
await ExecuteInRepositoryTransactionAsync(async () =>
{
await repositoryA.AddAsync(entity, cancellationToken);
await repositoryB.UpdateAsync(other, cancellationToken);
return true;
}, cancellationToken);
Commit vs rollback semantics are defined by your IBaseRepository / infrastructure implementation.
Composition root (dependency injection)
Register your implementations where the application starts (ASP.NET Core minimal API host, generic host, test fixture, etc.):
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ProductService>();
Namespaces
| Namespace | Role |
|---|---|
Odex.AspNetCore.Clarc.Domain.Aggregates |
BaseAggregate<>, StatefulAggregate<> |
Odex.AspNetCore.Clarc.Domain.Constants |
ExceptionType |
Odex.AspNetCore.Clarc.Domain.Contexts |
ITransactionContext |
Odex.AspNetCore.Clarc.Domain.DTOs |
BaseData marker |
Odex.AspNetCore.Clarc.Domain.Events |
IAggregateEvent, AggregateEvent |
Odex.AspNetCore.Clarc.Domain.Exceptions |
Domain exception hierarchy |
Odex.AspNetCore.Clarc.Domain.Policies |
IBasePolicy<>, BasePolicy<> |
Odex.AspNetCore.Clarc.Domain.Repositories |
IBaseRepository, IAggregateRepository<,> |
Odex.AspNetCore.Clarc.Domain.Services |
IBaseService, BaseService, IAggregateService, AggregateService |
Odex.AspNetCore.Clarc.Domain.Specifications |
Specification<>, combinators, ReplaceParameterVisitor |
Odex.AspNetCore.Clarc.Domain.ValueObjects |
BaseValueObject; Requests / Responses (e.g. PagedRequest, PagedResponse<>) |
Upgrading
See the changelog for version history and breaking changes. A copy also lives at the repository root as CHANGELOG.md for forks and offline docs.
0.1.x → 0.2.0 (summary):
DeleteByAccessIdAsyncwas removed fromIAggregateRepository<,>.- Read methods that accept
includeBuildergained an optional trailingCancellationToken.
Contributing
See CONTRIBUTING.md for setup, pull-request expectations, and versioning notes.
Community
- Code of Conduct — expected behavior in issues and pull requests.
- Security policy — how to report vulnerabilities responsibly.
License
This project is released under the MIT License.
Copyright (c) Asen O'Shabi.
Links
| Resource | URL |
|---|---|
| NuGet Gallery | https://www.nuget.org/packages/Odex.AspNetCore.Clarc.Domain |
| Changelog | https://github.com/o-shabi/odex-clarc-domain-aspnetcore/blob/main/CHANGELOG.md |
| Contributing | CONTRIBUTING.md (includes XML documentation for contributors) |
| Security | SECURITY.md |
| Source / issues | https://github.com/o-shabi/odex-clarc-domain-aspnetcore |
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net9.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Odex.AspNetCore.Clarc.Domain:
| Package | Downloads |
|---|---|
|
Odex.AspNetCore.Clarc.Infrastructure
Multi-purpose CLARC infrastructure for .NET 9: LINQ query builders, Domain-aligned paging, specification filters, transaction support, and typed infrastructure exceptions. Bring your own ORM or LINQ provider. |
GitHub repositories
This package is not used by any popular GitHub repositories.