Flowsy.Mediation
7.0.0
See the version list below for details.
dotnet add package Flowsy.Mediation --version 7.0.0
NuGet\Install-Package Flowsy.Mediation -Version 7.0.0
<PackageReference Include="Flowsy.Mediation" Version="7.0.0" />
paket add Flowsy.Mediation --version 7.0.0
#r "nuget: Flowsy.Mediation, 7.0.0"
// Install Flowsy.Mediation as a Cake Addin #addin nuget:?package=Flowsy.Mediation&version=7.0.0 // Install Flowsy.Mediation as a Cake Tool #tool nuget:?package=Flowsy.Mediation&version=7.0.0
Flowsy Mediation
Concepts
The mediation pattern allows us to separate the core of our application from the infrastructure that supports it. A good way to achieve this separation is to think of our application as a model of the business processes from the real world, in which users ask for something to be done and expect some kind of result from the requested operation.
This way, a common application flow would be:
- User requests some action to be executed.
- The application validates the request and returns some error information to the user in case of invalid input.
- The application executes the requested operation.
- The application returns to the user the result of the operation or some error information if something went wrong during the process.
To go even further, we can split those requests in two types:
- Query: A request for reading data with no alteration of the application's current state.
- Command: A request for creating, updating or removing data, thus altering the application's current state.
Dependencies
This package relies on other packages to set the foundation for infrastructure-decoupled applications:
Usage
1. Define Some Queries
// Queries/CustomersByRegion/CustomersByRegionQuery.cs
// using ...
using Flowsy.Mediation;
// using ...
public class CustomersByRegionQuery : Request<IEnumerable<CustomerDto>>
{
public string CountryId { get; set; } = string.Empty;
public string? StateId { get; set; }
}
2. Define Some Query Validators
// Queries/CustomersByRegion/CustomersByRegionQueryValidator.cs
// using ...
using Flowsy.Mediation;
using FluentValidation;
// using ...
public class CustomersByRegionQueryValidator : AbstractValidator<CustomersByRegionQuery>
{
public CustomersByRegionQueryValidator()
{
RuleFor(query => query.CountryId)
.NotEmpty()
.WithMessage("Country identifier is required.");
}
}
3. Define Some Query Handlers
// Queries/CustomersByRegion/CustomersByRegionQueryHandler.cs
// using ...
using Flowsy.Mediation;
// using ...
public class CustomersByRegionQueryHandler : RequestHandler<CustomerByIdQuery, IEnumerable<CustomerDto>>
{
private readonly ICustomerRepository _customerRepository;
public CustomersByRegionQueryHandler(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
protected override async Task<IEnumerable<CustomerDto>> HandleAsync(CustomersByRegionQuery request, CancellationToken cancellationToken)
{
var customers = await _customerRepository.GetManyAsync<Customer>(request.CountryId, request.StateId, cancellationToken);
return customers.Select(c => {
var customerDto = new CustomerDto();
// Copy properties from c to customerDto
return customerDto;
});
}
}
4. Define Some Commands
// Commands/CreateCustomer/CreateCustomerCommand.cs
// using ...
using Flowsy.Mediation;
// using ...
public class CreateCustomerCommand : Request<CreateCustomerCommandResult>
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
5. Define Some Command Validators
// Commands/CreateCustomer/CreateCustomerCommandValidator.cs
// using ...
using Flowsy.Mediation;
using FluentValidation;
// using ...
public class CreateCustomerCommandValidator : AbstractValidator<CreateCustomerCommand>
{
public CreateCustomerCommandValidator()
{
RuleFor(command => command.FirstName)
.NotEmpty()
.WithMessage("First name is required.");
RuleFor(command => command.Email)
.EmailAddress()
.WithMessage("Invalid email address")
.DependentRules(() =>
{
RuleFor(command => command.Email)
.NotEmpty()
.WithMessage("Email is required.")
.MaximumLength(320)
.WithMessage("Up to 320 characters.");
});
}
}
6. Define Some Command Results
// Commands/CreateCustomer/CreateCustomerCommandResult.cs
public class CreateCustomerCommandResult
{
public int CustomerId { get; set; }
}
7. Define Some Command Handlers
// Commands/CreateCustomer/CreateCustomerCommandHandler.cs
// using ...
using Flowsy.Mediation;
// using ...
public class CreateCustomerCommandHandler : RequestHandler<CreateCustomerCommand, CreateCustomerCommandResult>
{
private readonly ICustomerRepository _customerRepository;
public CreateCustomerCommandHandler(ICustomerRepository customerRepository)
{
_customerRepository = customerRepository;
}
protected override async Task<CreateCustomerCommandResult> HandleAsync(CreateCustomerCommand request, CancellationToken cancellationToken)
{
var customer = new Customer();
// Copy properties from request to customer
await _customerRepository.CreateAsync(customer, cancellationToken);
return new CreateCustomerCommandResult
{
CustomerId = customer.CustomerId
};
}
}
8. Resolve the Current User
The current user must be resolved from the context of every request being executed.
The following example shows the common use case for a web application.
// HttpRequestUserResolver.cs
// using ...
using Flowsy.Mediation;
// using ...
public class HttpRequestUserResolver : IRequestUserResolver
{
private readonly IHttpContextAccessor _httpContextAccessor;
public HttpRequestUserResolver(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Task<ClaimsPrincipal?> GetUserAsync<TRequest, TResult>(TRequest request, CancellationToken cancellationToken)
where TRequest : Request<TResult>, IRequest<TResult>
{
return Task.Run<ClaimsPrincipal>((Func<ClaimsPrincipal>) (() => _httpContextAccessor.HttpContext?.User), cancellationToken);
}
}
9. Register Queries and Commands
Add a reference to the assemblies containing the application logic and place this code in the Program.cs file.
// Program.cs
// using ...
using Flowsy.Mediation;
// using ...
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<IRequestUserResolver, HttpRequestUserResolver>();
builder.Services
.AddMediation(
typeof(CustomersByRegionQuery).Assembly, // Register queries and commands from this assembly
typeof(CreateCustomerCommand).Assembly // Register queries and commands from this assembly
// Register queries and commands from others assemblies
)
// Registers RequestUserResolutionBehavior to set the current user for every request
.AddRequestUser()
// Registers RequestValidationBehavior to validate every request
.AddValidation()
// Registers RequestLoggingBehavior to log information for every request and its result
.AddLogging();
// Add other services
var app = builder.Build();
// Use services
app.Run();
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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. |
-
net6.0
- Flowsy.Core (>= 2.0.0)
- Flowsy.DependencyInjection (>= 1.0.1)
- Flowsy.Localization (>= 2.0.1)
- FluentValidation (>= 11.5.2)
- MediatR (>= 12.0.1)
- Serilog (>= 2.12.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Flowsy.Mediation:
Package | Downloads |
---|---|
Flowsy.Web.Api
Foundation components for Web APIs. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
11.0.4 | 179 | 1/17/2024 |
11.0.3 | 106 | 1/17/2024 |
11.0.2 | 104 | 1/17/2024 |
11.0.1 | 108 | 1/16/2024 |
11.0.0 | 111 | 1/16/2024 |
10.0.0 | 116 | 1/16/2024 |
9.0.2 | 159 | 12/14/2023 |
9.0.1 | 104 | 12/14/2023 |
9.0.0 | 105 | 12/14/2023 |
8.0.1 | 124 | 12/6/2023 |
8.0.0 | 131 | 12/5/2023 |
7.3.2 | 229 | 10/5/2023 |
7.3.1 | 152 | 10/5/2023 |
7.3.0 | 140 | 10/5/2023 |
7.2.1 | 168 | 10/4/2023 |
7.2.0 | 144 | 10/4/2023 |
7.1.0 | 143 | 10/4/2023 |
7.0.0 | 264 | 8/29/2023 |
6.1.7 | 368 | 6/2/2023 |
6.1.6 | 187 | 5/24/2023 |
6.1.5 | 136 | 5/24/2023 |
6.1.4 | 331 | 3/25/2023 |
6.1.3 | 386 | 3/10/2023 |
6.1.2 | 282 | 3/10/2023 |
6.1.1 | 304 | 3/10/2023 |
6.1.0 | 229 | 3/10/2023 |
6.0.3 | 273 | 3/9/2023 |
6.0.2 | 295 | 3/9/2023 |
6.0.1 | 354 | 3/9/2023 |
6.0.0 | 299 | 3/9/2023 |
5.1.1 | 340 | 2/27/2023 |
5.1.0 | 321 | 2/24/2023 |
5.0.0 | 316 | 2/23/2023 |
4.1.0 | 310 | 2/22/2023 |
4.0.0 | 376 | 2/21/2023 |
3.4.0 | 305 | 2/21/2023 |
3.3.0 | 224 | 2/21/2023 |
3.2.0 | 425 | 2/21/2023 |
3.1.0 | 246 | 2/21/2023 |
3.0.1 | 1,737 | 11/6/2022 |
3.0.0 | 642 | 11/5/2022 |
2.0.0 | 746 | 11/3/2022 |
1.1.2 | 341 | 11/2/2022 |
1.1.1 | 541 | 11/2/2022 |
1.1.0 | 344 | 11/2/2022 |
1.0.0 | 355 | 11/2/2022 |