Azka.CQRS 10.0.0-alpha3

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

Azka.CQRS

NuGet .NET

A lightweight CQRS library for .NET that provides command/query pipelines with validation, before/after hooks, and dependency injection integration.

Features

  • CQRS Handler - Single entry point for commands and queries.
  • Validation Pipeline - Validate commands before handlers run.
  • Before/After Hooks - Intercept before and after the handler.
  • Standardized Responses - Error collection and IsValid status.
  • Auto Registration - Assembly scanning for handlers/validators/hooks.
  • Pagination Helper - QueryResponse<T> supports paging.

Installation

dotnet add package Azka.CQRS

Quick Start

1. Register in DI

using System.Reflection;
using Azka.CQRS;

var builder = WebApplication.CreateBuilder(args);

// Register all handlers/validators/hooks in this assembly
builder.Services.AddCQRS(Assembly.GetExecutingAssembly());

2. Create a Command

using Azka.CQRS.Abstractions.Commands;
using Azka.CQRS.Implementations.Commands;

public record CreateUserCommand(string Username, string Email)
    : ICommandRequest<CreateUserResponse>;

public class CreateUserResult
{
    public Guid Id { get; set; }
    public bool IsCustom { get; set; }
    public bool IsGenericBehaviour { get; set; }
}

public class CreateUserResponse : CommandResponse<CreateUserResult>
{
}

public class CreateUserHandler : ICommandHandler<CreateUserCommand, CreateUserResponse>
{
    public async Task<CreateUserResponse> HandleAsync(
        CreateUserCommand request,
        CancellationToken cancellationToken = default)
    {
        var result = new CreateUserResult { Id = Guid.NewGuid() };
        return await Task.FromResult(new CreateUserResponse { Data = result });
    }
}

3. Add Command Validation (Optional)

using Azka.CQRS.Implementations.Commands;

public class CreateUserValidation : CommandValidation<CreateUserCommand, CreateUserResponse>
{
    protected override Task DoValidateAsync(
        CreateUserCommand request,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(request.Username))
            AddError(nameof(request.Username), "Username is required.");

        if (string.IsNullOrWhiteSpace(request.Email))
            AddError(nameof(request.Email), "Email is required.");

        return Task.CompletedTask;
    }
}

4. Before/After Hooks (Optional)

using Azka.CQRS.Abstractions.Commands;

public class CreateUserOnBefore : ICommandOnBeforeHandler<CreateUserCommand, CreateUserResponse>
{
    public Task<CreateUserResponse> OnBeforeHandleAsync(
        CreateUserCommand request,
        CancellationToken cancellationToken = default)
    {
        // Add extra rule checks before handler
        return Task.FromResult(new CreateUserResponse());
    }
}

public class CreateUserOnAfter : ICommandOnAfterHandler<CreateUserCommand, CreateUserResponse>
{
    public Task OnAfterHandleAsync(
        CreateUserCommand request,
        CreateUserResponse response,
        CancellationToken cancellationToken = default)
    {
        // Logging or side-effects after handler
        return Task.CompletedTask;
    }
}

5. Run a Command

using Azka.CQRS.Abstractions;

public class UsersController : ControllerBase
{
    private readonly ICQRSHandler _cqrs;

    public UsersController(ICQRSHandler cqrs)
    {
        _cqrs = cqrs;
    }

    [HttpPost("/users")]
    public async Task<IActionResult> Create(CreateUserCommand command)
    {
        var response = await _cqrs.RunCommandAsync<CreateUserCommand, CreateUserResponse>(command);
        if (!response.IsValid) return BadRequest(response.Errors);
        return Ok(response.Data);
    }
}

Query Example

using Azka.CQRS.Abstractions.Queries;
using Azka.CQRS.Implementations.Queries;

public record GetUsersQuery(int Page, int PerPage)
    : IQueryRequest<GetUsersResponse>;

public class GetUsersResponse : QueryResponse<UserDto>
{
    public GetUsersResponse(IEnumerable<UserDto> data, int totalRows, int page, int perPage)
        : base(data, totalRows, page, perPage)
    {
    }
}

public class GetUsersHandler : IQueryHandler<GetUsersQuery, GetUsersResponse>
{
    public async Task<GetUsersResponse> HandleAsync(
        GetUsersQuery request,
        CancellationToken cancellationToken = default)
    {
        var data = new List<UserDto>();
        var totalRows = 0;
        return await Task.FromResult(new GetUsersResponse(data, totalRows, request.Page, request.PerPage));
    }
}

Run a Query

var response = await _cqrs.RunQueryAsync<GetUsersQuery, GetUsersResponse>(
    new GetUsersQuery(page: 1, perPage: 20));

if (!response.IsValid)
{
    // handle error
}

var rows = response.Data;

Execution Pipeline

Command

  1. ICommandValidation (if any)
  2. ICommandOnBeforeHandler (if any)
  3. ICommandHandler
  4. ICommandOnAfterHandler (if any)

Query

  1. IQueryOnBeforeHandler (if any)
  2. IQueryHandler

Custom Behaviour

By default, ICommandBehaviour<TRequest, TResponse> uses CommandBehaviour<,>. You can override behaviour for a specific command or replace it globally.

Custom Behaviour for a Specific Command

using Azka.CQRS.Abstractions.Commands;
using Azka.CQRS.Implementations.Commands;

public class CustomBehaviour
    : CommandBehaviour<CreateUserCommand, CreateUserResponse>
{
    public CustomBehaviour(
        ICommandHandler<CreateUserCommand, CreateUserResponse> handler,
        ICommandValidation<CreateUserCommand, CreateUserResponse>? validation = null,
        ICommandOnBeforeHandler<CreateUserCommand, CreateUserResponse>? onBefore = null,
        ICommandOnAfterHandler<CreateUserCommand, CreateUserResponse>? onAfter = null)
        : base(handler, validation, onBefore, onAfter)
    {
    }

    public override async Task<CreateUserResponse> RunBehaviourAsync(
        CreateUserCommand request,
        CancellationToken cancellationToken = default)
    {
        var response = await base.RunBehaviourAsync(request, cancellationToken);
        if (response.Data != null)
        {
            response.Data.IsCustom = true;
        }
        return response;
    }
}

Registration:

builder.Services.AddCQRS(Assembly.GetExecutingAssembly());
builder.Services.AddTransient<ICommandBehaviour<CreateUserCommand, CreateUserResponse>, CustomBehaviour>();

Global Behaviour (for all commands)

public class GlobalBehaviour<TRequest, TResponse> : CommandBehaviour<TRequest, TResponse>
    where TRequest : ICommandRequest<TResponse>
    where TResponse : ICommandResponse
{
    public GlobalBehaviour(
        ICommandHandler<TRequest, TResponse> handler,
        ICommandValidation<TRequest, TResponse>? validation = null,
        ICommandOnBeforeHandler<TRequest, TResponse>? onBefore = null,
        ICommandOnAfterHandler<TRequest, TResponse>? onAfter = null)
        : base(handler, validation, onBefore, onAfter)
    {
    }

    public override async Task<TResponse> RunBehaviourAsync(
        TRequest request,
        CancellationToken cancellationToken = default)
    {
        var response = await base.RunBehaviourAsync(request, cancellationToken);
        if (response is CommandResponse<CreateUserResult> user && user.Data != null)
        {
            user.Data.IsGenericBehaviour = true;
        }
        return response;
    }
}

Registration:

builder.Services.AddTransient(typeof(ICommandBehaviour<,>), typeof(GlobalBehaviour<,>));

API Summary

Command

Interface Description
ICommandRequest<TResponse> Marker for command request
ICommandResponse Response contract with errors & IsValid
ICommandHandler<TRequest, TResponse> Main command handler
ICommandValidation<TRequest, TResponse> Command validation
ICommandOnBeforeHandler<TRequest, TResponse> Before hook
ICommandOnAfterHandler<TRequest, TResponse> After hook

Query

Interface Description
IQueryRequest<TResponse> Marker for query request
IQueryResponse Response contract with errors & IsValid
IQueryHandler<TRequest, TResponse> Main query handler
IQueryOnBeforeHandler<TRequest, TResponse> Before hook

Requirements

License

See LICENSE.txt for details.

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
10.0.0-alpha3 32 2/1/2026
10.0.0-alpha2 32 1/31/2026

Initial release of Azka.CQRS
     - Command and query handler pipeline
     - Validation and before/after hooks
     - Auto registration for handlers and validators
     - Standardized command/query responses