SolTechnology.Core.CQRS
1.0.0
dotnet add package SolTechnology.Core.CQRS --version 1.0.0
NuGet\Install-Package SolTechnology.Core.CQRS -Version 1.0.0
<PackageReference Include="SolTechnology.Core.CQRS" Version="1.0.0" />
<PackageVersion Include="SolTechnology.Core.CQRS" Version="1.0.0" />
<PackageReference Include="SolTechnology.Core.CQRS" />
paket add SolTechnology.Core.CQRS --version 1.0.0
#r "nuget: SolTechnology.Core.CQRS, 1.0.0"
#:package SolTechnology.Core.CQRS@1.0.0
#addin nuget:?package=SolTechnology.Core.CQRS&version=1.0.0
#tool nuget:?package=SolTechnology.Core.CQRS&version=1.0.0
Overview
The SolTechnology.Core.CQRS library provides a complete CQRS (Command Query Responsibility Segregation) implementation with an in-house mediator, Result pattern for explicit success/failure handling, automatic FluentValidation, fire-and-forget notifications, and logging pipeline behaviors.
Registration
// Configure handler discovery explicitly, one handler family at a time:
services.AddCQRS(o =>
{
o.UseFluentValidation = true;
o.RegisterCommandsFromAssembly(typeof(CreateUserCommand).Assembly);
o.RegisterQueriesFromAssembly(typeof(GetUserQuery).Assembly);
o.RegisterEventsFromAssembly(typeof(UserCreatedEvent).Assembly);
});
Defining a Command
// Command with no return data (side-effect only)
public class CreateUserCommand : ICommand
{
public string Name { get; set; }
public string Email { get; set; }
}
public class CreateUserCommandHandler : ICommandHandler<CreateUserCommand>
{
public async Task<Result> Handle(CreateUserCommand command, CancellationToken cancellationToken)
{
await _userRepository.Create(command.Name, command.Email);
return Result.Success();
}
}
// Command with return data
public class CreateOrderCommand : ICommand<int>
{
public string Product { get; set; }
}
public class CreateOrderCommandHandler : ICommandHandler<CreateOrderCommand, int>
{
public async Task<Result<int>> Handle(CreateOrderCommand command, CancellationToken cancellationToken)
{
var orderId = await _orderRepository.Create(command.Product);
return Result<int>.Success(orderId);
}
}
Defining a Query
public class GetUserQuery : IQuery<User>
{
public int UserId { get; set; }
}
public class GetUserQueryHandler : IQueryHandler<GetUserQuery, User>
{
public async Task<Result<User>> Handle(GetUserQuery query, CancellationToken cancellationToken)
{
var user = await _userRepository.GetById(query.UserId);
if (user == null)
return Result<User>.Fail(new NotFoundError { Message = "User not found" });
return user;
}
}
Validation
Register a FluentValidation validator — failures are returned as Result.Fail(ValidationError) automatically. The handler is never invoked.
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
{
public CreateUserCommandValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
RuleFor(x => x.Email).NotEmpty().EmailAddress();
}
}
Events
Fire-and-forget events dispatched to all registered handlers. Each handler runs on its own background task with a fresh DI scope. Failures are isolated and logged — they never propagate to the caller and never stop other handlers.
public class UserCreated : IEvent
{
public int UserId { get; set; }
}
public class SendWelcomeEmailHandler : IEventHandler<UserCreated>
{
public async Task Handle(UserCreated @event, CancellationToken cancellationToken)
{
await _emailService.SendWelcome(@event.UserId);
}
}
// Publishing (returns immediately)
_mediator.Publish(new UserCreated { UserId = 42 });
For durable (persistent) event dispatch backed by Hangfire, see SolTechnology.Core.Hangfire.
Result Pattern
Every handler returns a Result or Result<T> (from the foundational SolTechnology.Core package).
Inspect it explicitly:
var result = await _mediator.Send(new GetUserQuery { UserId = 1 });
if (result.IsSuccess)
{
var user = result.Data;
}
else
{
var error = result.Error;
}
For multi-step orchestration with fluent success/failure flow — chaining steps, guarding with
preconditions, recovering from failures — promote the handler to a Tale and describe its flow
with the fluent Tale builder. See Tale.md.
Pipeline Behaviors
Custom behaviors can be registered to wrap every request:
public class MyBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
// before
var response = await next();
// after
return response;
}
}
Built-in behaviors (registered automatically):
- LoggingPipelineBehavior — tracks every request as a logical operation with structured logging and OpenTelemetry spans.
- FluentValidationPipelineBehavior — runs all validators; short-circuits with
ValidationErroron failure.
Working with AI Agent
Building commands, queries, and events with an AI assistant (GitHub Copilot, Claude Code)? The repository ships a skill — a narrow, file-cited procedure your agent can read on demand:
command-query-event-tale— classify the artifact (command / query / event / Tale), lay out the files one-folder-per-use-case, returnResult<T>, wireAddCQRS, and add[LogScope]logging.
It points at the binding rules in the Coding Guide — keep these open while you write:
| Product | Versions 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. |
-
net10.0
- FluentValidation (>= 12.1.1)
- FluentValidation.DependencyInjectionExtensions (>= 11.11.0)
- Microsoft.Extensions.Configuration (>= 10.0.9)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- SolTechnology.Core (>= 1.0.0)
- SolTechnology.Core.Logging (>= 1.2.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on SolTechnology.Core.CQRS:
| Package | Downloads |
|---|---|
|
SolTechnology.Core.Flow
Package Description |
|
|
SolTechnology.Core.Hangfire
Durable, persistent event dispatch and recurring jobs for SolTechnology.Core.CQRS. Opt in with AddPersistentEvents() and your events survive process restarts via Hangfire storage; schedule recurring work with AddSolRecurringJob<TJob>(cron). |
GitHub repositories
This package is not used by any popular GitHub repositories.