Command Query Responsibility Segregation (CQRS) framework based on mediator pattern using Mongo DB as the data store handler
$ dotnet add package Arbiter.CommandQuery.MongoDBA powerful, lightweight, and extensible implementation of the Mediator pattern and Command Query Responsibility Segregation (CQRS) for .NET applications.
Arbiter is designed for building clean, modular architectures like Vertical Slice Architecture and CQRS. It provides a comprehensive suite of libraries that work together to simplify complex application patterns while maintaining high performance and flexibility.
Get started with Arbiter in just a few steps:
dotnet add package Arbiter.Mediation// Define your request
public class GetUserQuery : IRequest<User>
{
public int UserId { get; set; }
}
// Implement the handler
public class GetUserHandler : IRequestHandler<GetUserQuery, User>
{
public async ValueTask<User> Handle(GetUserQuery request, CancellationToken cancellationToken)
{
// Your business logic here
return await GetUserFromDatabase(request.UserId);
}
}services.AddMediator();
services.AddTransient<IRequestHandler<GetUserQuery, User>, GetUserHandler>();public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator) => _mediator = mediator;
[HttpGet("{id}")]
public async Task<User> GetUser(int id)
{
return await _mediator.Send(new GetUserQuery { UserId = id });
}
}| Library | Package | Description |
|---|---|---|
| Arbiter.Mediation | Lightweight and extensible implementation of the Mediator pattern | |
| Arbiter.CommandQuery | Base package for Commands, Queries and Behaviors | |
| Arbiter.Communication | Message template communication for email and SMS services |
| Library | Package | Description |
|---|---|---|
| Arbiter.CommandQuery.EntityFramework | Entity Framework Core handlers for the base Commands and Queries | |
| Arbiter.CommandQuery.MongoDB | MongoDB handlers for the base Commands and Queries |
| Library | Package | Description |
|---|---|---|
| Arbiter.CommandQuery.Endpoints | Minimal API endpoints for base Commands and Queries | |
| Arbiter.CommandQuery.Mvc | MVC Controllers for base Commands and Queries |
| Library | Package | Description |
|---|---|---|
| Arbiter.Mediation.OpenTelemetry | OpenTelemetry support for Arbiter.Mediation library |
| Library | Package | Description |
|---|---|---|
| Arbiter.Communication.Azure | Communication implementation for Azure Communication Services | |
| Arbiter.Communication.Twilio | Communication implementation for SendGrid and Twilio |
A lightweight and extensible implementation of the Mediator pattern for .NET applications, designed for clean, modular architectures like Vertical Slice Architecture and CQRS.
IRequest<TResponse> and IRequestHandler<TRequest, TResponse>INotification and INotificationHandler<TNotification>IPipelineBehavior<TRequest, TResponse>dotnet add package Arbiter.Mediation1. Define Request and Response
public class Ping : IRequest<Pong>
{
public string? Message { get; set; }
}
public class Pong
{
public string? Message { get; set; }
}2. Implement Handler
public class PingHandler : IRequestHandler<Ping, Pong>
{
public async ValueTask<Pong> Handle(
Ping request,
CancellationToken cancellationToken = default)
{
// Simulate async work
await Task.Delay(100, cancellationToken);
return new Pong { Message = $"{request.Message} Pong" };
}
}3. Define Pipeline Behavior (Optional)
public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;
public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
{
_logger = logger;
}
public async ValueTask<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken = default)
{
_logger.LogInformation("Handling {RequestType}", typeof(TRequest).Name);
var response = await next(cancellationToken);
_logger.LogInformation("Handled {RequestType}", typeof(TRequest).Name);
return response;
}
}4. Register Services
// Register Mediator services
services.AddMediator();
// Register handlers
services.AddTransient<IRequestHandler<Ping, Pong>, PingHandler>();
// Register pipeline behaviors (optional)
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));5. Use in Controllers
[ApiController]
[Route("api/[controller]")]
public class PingController : ControllerBase
{
private readonly IMediator _mediator;
public PingController(IMediator mediator) => _mediator = mediator;
[HttpGet]
public async Task<ActionResult<Pong>> Get(
[FromQuery] string? message = null,
CancellationToken cancellationToken = default)
{
var request = new Ping { Message = message ?? "Hello" };
var response = await _mediator.Send(request, cancellationToken);
return Ok(response);
}
}Comprehensive observability support for Arbiter.Mediation with OpenTelemetry integration.
dotnet add package Arbiter.Mediation.OpenTelemetry// Register diagnostics
services.AddMediatorDiagnostics();
// Configure OpenTelemetry
services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddMediatorInstrumentation()
.AddConsoleExporter()
)
.WithMetrics(metrics => metrics
.AddMediatorInstrumentation()
.AddConsoleExporter()
);A comprehensive Command Query Responsibility Segregation (CQRS) framework built on top of the mediator pattern.
dotnet add package Arbiter.CommandQueryservices.AddCommandQuery();The library provides several pre-built commands and queries for common operations:
Entity Queries:
EntityIdentifierQuery<TKey, TReadModel> - Get entity by IDEntitySelectQuery<TReadModel> - Query entities with filtering and sortingEntityPagedQuery<TReadModel> - Paginated entity queriesEntity Commands:
EntityCreateCommand<TKey, TCreateModel, TReadModel> - Create new entitiesEntityUpdateCommand<TKey, TUpdateModel, TReadModel> - Update existing entitiesEntityUpsertCommand<TKey, TUpsertModel, TReadModel> - Create or update entitiesEntityDeleteCommand<TKey, TReadModel> - Delete entitiesQuery by ID:
var principal = new ClaimsPrincipal(new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, "JohnDoe")
}));
var query = new EntityIdentifierQuery<int, ProductReadModel>(principal, 123);
var result = await mediator.Send(query);Query with Filtering:
var filter = new EntityFilter { Name = "Status", Operator = "eq", Value = "Active" };
var sort = new EntitySort { Name = "Name", Direction = "asc" };
var query = new EntitySelectQuery<ProductReadModel>(principal, filter, sort);
var result = await mediator.Send(query);Update Command:
var updateModel = new ProductUpdateModel
{
Name = "Updated Product",
Description = "Updated description",
Price = 29.99m
};
var command = new EntityUpdateCommand<int, ProductUpdateModel, ProductReadModel>(principal, 123, updateModel);
var result = await mediator.Send(command);Entity Framework Core integration providing ready-to-use handlers for all base commands and queries.
dotnet add package Arbiter.CommandQuery.EntityFramework// Add Entity Framework Core services
services.AddDbContext<TrackerContext>(options =>
options.UseSqlServer(connectionString)
);
// Register Command Query services
services.AddCommandQuery();
// Register mappers and validators
services.AddSingleton<IMapper, MyMapper>();
services.AddSingleton<IValidator, MyValidator>();
// Register Entity Framework handlers for each entity
services.AddEntityQueries<TrackerContext, Product, int, ProductReadModel>();
services.AddEntityCommands<TrackerContext, Product, int, ProductReadModel, ProductCreateModel, ProductUpdateModel>();MongoDB integration providing handlers for all base commands and queries with document database optimizations.
dotnet add package Arbiter.CommandQuery.MongoDB// Add MongoDB Repository services
services.AddMongoRepository("Tracker");
services.AddCommandQuery();
// Register mappers and validators
services.AddSingleton<IMapper, MyMapper>();
services.AddSingleton<IValidator, MyValidator>();
// Register MongoDB handlers for each entity
services.AddEntityQueries<IMongoEntityRepository<Product>, Product, string, ProductReadModel>();
services.AddEntityCommands<IMongoEntityRepository<Product>, Product, string, ProductReadModel, ProductCreateModel, ProductUpdateModel>();Minimal API endpoints that automatically expose your commands and queries as REST APIs.
dotnet add package Arbiter.CommandQuery.Endpointsvar builder = WebApplication.CreateBuilder(args);
// Add endpoint services
builder.Services.AddEndpointRoutes();
var app = builder.Build();
// Map endpoint routes
app.MapEndpointRoutes();
app.Run();Custom Endpoint:
public class ProductEndpoint : EntityCommandEndpointBase<int, ProductReadModel, ProductReadModel, ProductCreateModel, ProductUpdateModel>
{
public ProductEndpoint(ILoggerFactory loggerFactory)
: base(loggerFactory, "Product")
{
}
}
// Register the endpoint
builder.Services.AddSingleton<IEndpointRoute, ProductEndpoint>();MVC Controllers for base Commands and Queries with full ASP.NET Core integration.
dotnet add package Arbiter.CommandQuery.MvcA flexible message templating system for email and SMS communications with support for multiple providers.
dotnet add package Arbiter.CommunicationAzure Communication Services:
dotnet add package Arbiter.Communication.AzureSendGrid and Twilio:
dotnet add package Arbiter.Communication.TwilioExplore practical examples in the samples directory:
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this project useful, please consider: