A lightweight, dependency-free CQRS library for .NET (net8 / net9 / net10), designed for developers who want:
- Simple Commands and Queries
- Optional Command return values
- Reflection-safe, minimal Dispatcher
- Clean Vertical Slice architecture
- Ready-to-use Console + Minimal API samples
- Full unit test coverage
- Zero dependencies (no MediatR, no pipelines)
Author: livedcode
License: MIT
public sealed class CreateLogCommand : ICommand { }
public sealed class CreateUserCommand : ICommand<int> { }public sealed class GetUserQuery : IQuery<UserDto> { }SendAsync(ICommand)SendAsync<TResult>(ICommand<TResult>)QueryAsync<TResult>(IQuery<TResult>)
- Minimal API (Vertical Slice)
- Console application
Uses xUnit
MinimalCQRS/
β
βββ README.md
βββ LICENSE
βββ CHANGELOG.md
β
βββ src/
β βββ MinimalCQRS/
β β βββ Commands/
β β β βββ ICommand.cs
β β β βββ ICommandHandler.cs
β β β
β β βββ Queries/
β β β βββ IQuery.cs
β β β βββ IQueryHandler.cs
β β β
β β βββ Execution/
β β β βββ IDispatcher.cs
β β β βββ Dispatcher.cs
β β β
β β βββ MinimalCQRS.csproj
β β
β βββ MinimalCQRS.SampleConsole/
β β βββ Program.cs
β β β
β β βββ Commands/
β β β βββ NoReturn/
β β β β βββ CreateLogCommand.cs
β β β β βββ CreateLogHandler.cs
β β β β
β β β βββ WithReturn/
β β β βββ CreateUserCommand.cs
β β β βββ CreateUserHandler.cs
β β β
β β βββ Models/UserDto.cs
β β βββ Queries/
β β β βββ GetUserQuery.cs
β β β βββ GetUserHandler.cs
β β β
β β βββ MinimalCQRS.SampleConsole.csproj
β β
β βββ MinimalCQRS.Sample.Api/
β βββ Program.cs
β β
β βββ Features/
β β βββ Logs/
β β β βββ Create/
β β β βββ CreateLogCommand.cs
β β β βββ CreateLogHandler.cs
β β β βββ Endpoint.cs
β β β
β β βββ Users/
β β βββ Create/
β β β βββ CreateUserCommand.cs
β β β βββ CreateUserHandler.cs
β β β βββ Endpoint.cs
β β β
β β βββ Get/
β β βββ GetUserQuery.cs
β β βββ GetUserHandler.cs
β β βββ UserDto.cs
β β βββ Endpoint.cs
β β
β βββ MinimalCQRS.Sample.Api.csproj
β
βββ tests/
βββ MinimalCQRS.Tests/
βββ DispatcherTests.cs
βββ MinimalCQRS.Tests.csproj
services.AddScoped<IDispatcher, Dispatcher>();
// Command (no return)
services.AddScoped<ICommandHandler<CreateLogCommand>, CreateLogHandler>();
// Command (with return)
services.AddScoped<ICommandHandler<CreateUserCommand, int>, CreateUserHandler>();
// Query
services.AddScoped<IQueryHandler<GetUserQuery, UserDto>, GetUserHandler>();public sealed class CreateLogCommand : ICommand
{
public string Message { get; init; } = string.Empty;
}public sealed class CreateLogHandler : ICommandHandler<CreateLogCommand>
{
public Task HandleAsync(CreateLogCommand cmd, CancellationToken ct = default)
{
Console.WriteLine($"LOG: {cmd.Message}");
return Task.CompletedTask;
}
}await dispatcher.SendAsync(new CreateLogCommand { Message = "Hello!" });public sealed class CreateUserCommand : ICommand<int>
{
public string Email { get; init; } = string.Empty;
}public sealed class CreateUserHandler : ICommandHandler<CreateUserCommand, int>
{
private static int _nextId = 1;
public Task<int> HandleAsync(CreateUserCommand cmd, CancellationToken ct = default)
{
return Task.FromResult(_nextId++);
}
}int id = await dispatcher.SendAsync(new CreateUserCommand { Email = "demo@test.com" });public sealed class GetUserQuery : IQuery<UserDto>
{
public int UserId { get; init; }
}public sealed class GetUserHandler : IQueryHandler<GetUserQuery, UserDto>
{
public Task<UserDto> HandleAsync(GetUserQuery query, CancellationToken ct = default)
{
return Task.FromResult(new UserDto { UserId = query.UserId });
}
}var user = await dispatcher.QueryAsync(new GetUserQuery { UserId = 10 });- Tests for void command
- Tests for return command
- Tests for query
- Tests for dispatcher behavior
Example:
[Fact]
public async Task SendAsync_CommandWithReturn_Should_Return_Id()
{
var id = await dispatcher.SendAsync(new CreateUserCommand { Email = "abc@test.com" });
Assert.True(id > 0);
}MIT License β see LICENSE file.
MinimalCQRS is:
- β Simple
- β Fast
- β Production-ready
- β No 3rd party dependencies
- β Perfect for Vertical Slice or Clean Architecture
- β Includes samples + tests
A great lightweight alternative to MediatR when you only need pure CQRS, nothing else.