CQRS.PostOffice
1.1.0
dotnet add package CQRS.PostOffice --version 1.1.0
NuGet\Install-Package CQRS.PostOffice -Version 1.1.0
<PackageReference Include="CQRS.PostOffice" Version="1.1.0" />
<PackageVersion Include="CQRS.PostOffice" Version="1.1.0" />
<PackageReference Include="CQRS.PostOffice" />
paket add CQRS.PostOffice --version 1.1.0
#r "nuget: CQRS.PostOffice, 1.1.0"
#:package CQRS.PostOffice@1.1.0
#addin nuget:?package=CQRS.PostOffice&version=1.1.0
#tool nuget:?package=CQRS.PostOffice&version=1.1.0
PostOffice
A high-performance CQRS messaging library for .NET with middleware pipeline support and FluentValidation integration.
Installation
dotnet add package CQRS.PostOffice
Quick Start
Basic Setup
// Configure services
services.AddPostOffice();
// Create a request
public record HelloRequest(string Name) : IMail<string>;
// Create a handler
public class HelloHandler : DeliveryAsync<HelloRequest, string>
{
public override Task<string> HandleAsync(HelloRequest request)
{
return Task.FromResult($"Hello, {request.Name}!");
}
}
// Handlers are auto-discovered - no manual registration needed!
// Send the message
var poster = services.GetRequiredService<Poster>();
var result = await poster.Send(new HelloRequest("World"));
// Returns: "Hello, World!"
Message Handlers
Create handlers that inherit from DeliveryAsync<TRequest, TResponse>
. Handlers are automatically discovered and registered:
public record GetUserRequest(int UserId) : IMail<User>;
public class GetUserHandler : DeliveryAsync<GetUserRequest, User>
{
private readonly IUserRepository _userRepository;
public GetUserHandler(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override async Task<User> HandleAsync(GetUserRequest request)
{
var user = await _userRepository.GetByIdAsync(request.UserId);
if (user == null)
throw new NotFoundException($"User {request.UserId} not found");
return user;
}
}
// Handlers are auto-discovered - no manual registration needed!
var result = await poster.Send(new GetUserRequest(123));
Validation
Validators are automatically discovered and registered. Create validators that inherit from AbstractValidator<T>
:
Exception-Based Validation
// Add validation
services.AddPostOffice()
.AddValidation();
// Create validator
public class CreateUserRequestValidator : AbstractValidator<CreateUserRequest>
{
public CreateUserRequestValidator()
{
RuleFor(x => x.Name).NotEmpty();
RuleFor(x => x.Email).EmailAddress();
RuleFor(x => x.Age).GreaterThan(0).LessThan(150);
}
}
// Validators are auto-discovered - no manual registration needed!
// Usage
try
{
var result = await poster.Send(new CreateUserRequest("John", "john@example.com", 25));
}
catch (ValidationException ex)
{
foreach (var error in ex.Errors)
{
Console.WriteLine($"- {error.ErrorMessage}");
}
}
Custom Response Validation
// Create custom validation middleware
public class ErrorOrValidationBehavior<TMail, TResponse> : IPostageMiddleware<TMail, TResponse>
where TMail : IMail<TResponse>
where TResponse : IErrorOr
{
private readonly IValidator<TMail>? _validator;
public ErrorOrValidationBehavior(IValidator<TMail>? validator = null)
{
_validator = validator;
}
public async Task<(bool handled, TResponse? result)> StampAsync(TMail mail, Func<TMail, Task<TResponse>> next)
{
if (_validator is null)
{
return (false, default(TResponse));
}
ValidationResult validatorResult = await _validator.ValidateAsync(mail);
if (validatorResult.IsValid)
{
return (false, default(TResponse));
}
List<Error> errors = validatorResult.Errors.Select(x => Error.Validation(x.PropertyName, x.ErrorMessage)).ToList();
return (true, (TResponse)(dynamic)errors);
}
}
// Register custom middleware
services.AddPostOffice();
services.AddTransient(typeof(IPostageMiddleware<,>), typeof(ErrorOrValidationBehavior<,>));
// Use ErrorOr in commands
public record CreateUserCommand(string Name, string Email) : IMail<ErrorOr<string>>;
// Usage
var result = await poster.Send(new CreateUserCommand("", "invalid-email"));
if (result.IsError)
{
foreach (var error in result.Errors)
{
Console.WriteLine($"- {error.Description}");
}
}
Middleware
Adding Middleware
services.AddPostOffice()
.AddMiddleware<LoggingMiddleware<,>>()
.AddMiddleware<CachingMiddleware<,>>();
Creating Custom Middleware
public class LoggingMiddleware<TMail, TResponse> : IPostageMiddleware<TMail, TResponse>
{
private readonly ILogger<LoggingMiddleware<TMail, TResponse>> _logger;
public LoggingMiddleware(ILogger<LoggingMiddleware<TMail, TResponse>> logger)
{
_logger = logger;
}
public async Task<(bool handled, TResponse? result)> StampAsync(
TMail mail, Func<TMail, Task<TResponse>> next)
{
_logger.LogInformation("Processing {MailType}", typeof(TMail).Name);
var stopwatch = Stopwatch.StartNew();
var result = await next(mail);
stopwatch.Stop();
_logger.LogInformation("Processed {MailType} in {ElapsedMs}ms",
typeof(TMail).Name, stopwatch.ElapsedMilliseconds);
return (false, result);
}
}
Performance Logging
// Add logging
services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
// Add performance logging
services.AddPostOffice()
.AddPerformanceLogging();
// Add validation performance logging
services.AddPostOffice()
.AddValidation()
.AddValidationPerformanceLogging();
Sample log output:
🚀 Starting request: CreateUserCommand at 2024-01-15T10:30:00.000Z
🔍 Starting validation for CreateUserCommand with 1 validators
✅ Validation passed for CreateUserCommand in 2ms
✅ Request completed: CreateUserCommand in 52ms at 2024-01-15T10:30:00.052Z
Complete Example
// Program.cs
services.AddLogging(builder => builder.AddConsole());
services.AddPostOffice()
.AddValidation()
.AddPerformanceLogging();
// Request
public record CreateUserRequest(string Name, string Email, int Age) : IMail<string>;
// Validator
public class CreateUserRequestValidator : AbstractValidator<CreateUserRequest>
{
public CreateUserRequestValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
RuleFor(x => x.Email).NotEmpty().EmailAddress();
RuleFor(x => x.Age).GreaterThan(0).LessThan(150);
}
}
// Handler
public class CreateUserHandler : DeliveryAsync<CreateUserRequest, string>
{
public override async Task<string> HandleAsync(CreateUserRequest request)
{
// Business logic here
await Task.Delay(50); // Simulate work
return $"User {request.Name} created successfully!";
}
}
// Handlers and validators are auto-discovered - no manual registration needed!
// Usage
var poster = services.GetRequiredService<Poster>();
// Valid request
var result = await poster.Send(new CreateUserRequest("John", "john@example.com", 25));
Console.WriteLine(result); // "User John created successfully!"
// Invalid request
try
{
await poster.Send(new CreateUserRequest("", "invalid-email", -5));
}
catch (ValidationException ex)
{
Console.WriteLine("Validation failed:");
foreach (var error in ex.Errors)
{
Console.WriteLine($"- {error.ErrorMessage}");
}
}
Performance
PostOffice uses compiled expressions instead of reflection for fast handler invocation. Performance optimizations are enabled by default.
// Precompile handlers for common types
services.AddPostOffice()
.WarmupForTypes(typeof(CreateUserRequest), typeof(UpdateUserRequest));
License
MIT License
Links
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. 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. |
-
net8.0
- FluentValidation (>= 12.0.0)
- FluentValidation.DependencyInjectionExtensions (>= 12.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
PostOffice v1.0.6 - Deployment Guide
What's New:
- Added comprehensive deployment guide with cute emojis
- Documented missing features analysis for future development
- Created step-by-step deployment instructions
- Added troubleshooting guide for common deployment issues
- Included automated deployment options (GitHub Actions, PowerShell scripts)
- Enhanced version management documentation
Features:
- High-performance message processing with middleware pipeline
- FluentValidation integration with custom response support
- Compiled expressions for 10x faster handler invocation
- Object pooling and memory optimizations
- Fast-path validation for simple rules (25x faster!)
- Multiple performance profiles (MaxThroughput, LowLatency, LowMemory)
- Clean architecture with professional folder structure
Performance:
- Sub-microsecond response times
- 400,000+ requests/second throughput
- 50-80% less memory allocations
- Zero reflection overhead
Professional and enterprise-ready CQRS messaging library with comprehensive documentation and deployment guides.