A 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.
- Clean Architecture: Perfect for Vertical Slice Architecture and CQRS patterns
- High Performance: Minimal overhead with efficient mediator implementation
- Extensible: Pipeline behaviors, custom handlers, and extensive customization options
- Observable: Built-in OpenTelemetry support for tracing and metrics
- Database Agnostic: Support for Entity Framework Core, MongoDB, and more
- Web Ready: Minimal API endpoints and MVC controller support
- Communication: Integrated email and SMS messaging capabilities
- Quick Start
- Packages
- Core Libraries
- Data Providers
- Web Integration
- Documentation
- Samples
- Contributing
- License
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.
- Request/Response Pattern: Handle requests with typed responses using
IRequest<TResponse>
andIRequestHandler<TRequest, TResponse>
- Notifications/Events: Publish events using
INotification
andINotificationHandler<TNotification>
- Pipeline Behaviors: Middleware-like cross-cutting concerns using
IPipelineBehavior<TRequest, TResponse>
- Dependency Injection: Seamless integration with .NET's DI container
- High Performance: Minimal allocations and efficient execution
- OpenTelemetry Ready: Built-in observability support
dotnet add package Arbiter.Mediation
1. 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
- Distributed Tracing: Automatic tracing of all mediator operations
- Metrics: Built-in metrics for request duration, throughput, and errors
- Activity Enrichment: Rich contextual information in traces
- Zero Configuration: Works out of the box with minimal setup
// 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.
- CQRS Implementation: Clear separation between commands and queries
- Pre-built Operations: Common CRUD operations out of the box
- Generic Handlers: Reusable handlers for typical data operations
- Smart Behaviors: Caching, auditing, validation, and soft delete support
- Auto Mapping: Built-in view model to data model mapping
- Advanced Querying: Filter, sort, and pagination support
- Multi-tenancy Ready: Built-in tenant isolation support
dotnet add package Arbiter.CommandQuery
services.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 queries
Entity Commands:
EntityCreateCommand<TKey, TCreateModel, TReadModel>
- Create new entitiesEntityUpdateCommand<TKey, TUpdateModel, TReadModel>
- Update existing entitiesEntityUpsertCommand<TKey, TUpsertModel, TReadModel>
- Create or update entitiesEntityDeleteCommand<TKey, TReadModel>
- Delete entities
Query 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
- Complete CRUD Operations: Pre-built handlers for all entity operations
- Change Tracking: Automatic audit fields and soft delete support
- Optimized Queries: Efficient EF Core query patterns
- Transaction Support: Proper transaction management
- Bulk Operations: Support for bulk insert/update operations
// 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.Endpoints
var 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.Mvc
A flexible message templating system for email and SMS communications with support for multiple providers.
dotnet add package Arbiter.Communication
Azure Communication Services:
dotnet add package Arbiter.Communication.Azure
SendGrid and Twilio:
dotnet add package Arbiter.Communication.Twilio
- Complete Documentation - Comprehensive guides and API reference
- Quick Start Guide - Get up and running quickly
- Architecture Patterns - Best practices and patterns
Explore practical examples in the samples directory:
- Entity Framework Sample - Complete CRUD operations with EF Core
- MongoDB Sample - Document database implementation
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.
- Fork the repository
- Create your feature branch (git checkout -b feature/amazing-feature)
- Commit your changes (git commit -m 'Add amazing feature')
- Push to the branch (git push origin feature/amazing-feature)
- Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
If you find this project useful, please consider:
- Starring the repository
- Reporting issues
- Contributing improvements
- Spreading the word