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 = "[email protected]" });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 = "[email protected]" });
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.