Skip to content

Desolate1998/PostOffice

Repository files navigation

PostOffice

NuGet

Installation

dotnet add package CQRS.PostOffice

Quick Start

Basic Setup

services.AddPostOffice();

public record HelloRequest(string Name) : IMail<string>;

public class HelloHandler : DeliveryAsync<HelloRequest, string>
{
    public override Task<string> HandleAsync(HelloRequest request)
    {
        return Task.FromResult($"Hello, {request.Name}!");
    }
}

var poster = services.GetRequiredService<Poster>();
var result = await poster.Send(new HelloRequest("World"));

Validation

Validators are automatically discovered and registered. Create validators that inherit from AbstractValidator<T>:

Exception-Based Validation

services.AddPostOffice()
        .AddValidation();

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);
    }
}

try
{
    var result = await poster.Send(new CreateUserRequest("John", "[email protected]", 25));
}
catch (ValidationException ex)
{
    foreach (var error in ex.Errors)
    {
        Console.WriteLine($"- {error.ErrorMessage}");
    }
}

Custom Response Validation

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);
    }
}

services.AddPostOffice();
services.AddTransient(typeof(IPostageMiddleware<,>), typeof(ErrorOrValidationBehavior<,>));

public record CreateUserCommand(string Name, string Email) : IMail<ErrorOr<string>>;

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);
    }
}

License

MIT License

Links

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages