Brokly 1.1.0
dotnet add package Brokly --version 1.1.0
NuGet\Install-Package Brokly -Version 1.1.0
<PackageReference Include="Brokly" Version="1.1.0" />
<PackageVersion Include="Brokly" Version="1.1.0" />
<PackageReference Include="Brokly" />
paket add Brokly --version 1.1.0
#r "nuget: Brokly, 1.1.0"
#:package Brokly@1.1.0
#addin nuget:?package=Brokly&version=1.1.0
#tool nuget:?package=Brokly&version=1.1.0
Brokly
Elevate your application messaging with Brokly – a streamlined implementation of the mediator pattern that brings structure to your CQRS flows while maintaining the flexibility you need.
Features
- Request/Response via
IRequest<T>/IRequestHandler<TRequest, TResult>andIRequestSender - Event Handling via
IEvent/IEventHandler<TEvent>andIEventBuswith in‑memory, bounded, channel‑based queue - Request Pipeline: Global middleware via
IRequestPipeline<TRequest, TResult>/IRequestPipeline<TRequest> - Processors: Handler-specific middleware via
IProcessor<TRequest, TResult>/IProcessor<TRequest> - Customizable queue size, concurrency (consumer count), and event‑handling toggle via
BroklyOptions
Installation
Install Brokly from NuGet:
dotnet add package Brokly --version <version_number>
This will automatically pull in the Brokly.Contracts package.
Quick Start
Register with Dependency Injection
In your Program.cs or Startup.cs:
using Brokly.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Default registration (scans calling assembly, no events/pipelines/processors handling)
builder.Services.AddBrokly();
// OR with customization:
builder.Services.AddBrokly(options =>
{
options
.AddHandlersFromAssembly(typeof(Program).Assembly)
.UseEventHandling(true)
.UseRequestPipelines()
.SetMaxQueueSize(1000)
.AddConsumers(3);
});
var app = builder.Build();
Define a Request & Handler
using Brokly.Contracts.RequestsHandling;
// Define the request
public record GetUserByIdQuery(Guid UserId) : IRequest<UserDto>;
// Implement the handler
public class GetUserByIdHandler : IRequestHandler<GetUserByIdQuery, UserDto>
{
public Task<UserDto> HandleAsync(GetUserByIdQuery request, CancellationToken ct)
{
// ... load user ...
return Task.FromResult(fetchedUser);
}
}
Send a Request
Inject IRequestSender and send:
public class UsersController : ControllerBase
{
private readonly IRequestSender _sender;
public UsersController(IRequestSender sender)
=> _sender = sender;
[HttpGet("{id}")]
public async Task<UserDto> Get(Guid id, CancellationToken ct)
=> await _sender.Send<GetUserByIdQuery, UserDto>(new(id), ct);
}
Define an Event & Handler
using Brokly.Contracts.EventsHandling;
// Define the event
public record UserCreatedEvent(UserDto User) : IEvent;
// Implement the handler
public class SendWelcomeEmailHandler : IEventHandler<UserCreatedEvent>
{
public Task HandleAsync(UserCreatedEvent @event, CancellationToken ct)
{
// ... send email ...
return Task.CompletedTask;
}
}
Publish an Event
Inject IEventBus and publish:
public class UserService
{
private readonly IEventBus _events;
public UserService(IEventBus events) => _events = events;
public async Task CreateUserAsync(UserDto dto, CancellationToken ct)
{
// ... create user ...
await _events.PublishEventAsync(new UserCreatedEvent(dto), ct);
}
}
Request Pipeline
Brokly’s request pipelines let you apply global middleware to all requests, with or without return values.
Pipeline with Result
Implement IRequestPipeline<TRequest, TResult>:
using Brokly.Contracts.Pipeline;
using Brokly.Contracts.RequestsHandling;
public class LoggingPipeline<TRequest, TResult> : IRequestPipeline<TRequest, TResult>
where TRequest : IRequest<TResult>
{
public async Task<TResult> ExecuteAsync(
TRequest request,
PipelineDelegate<TResult> complete,
CancellationToken ct)
{
Console.WriteLine($"[Pipeline] Handling {typeof(TRequest).Name}");
var result = await complete(ct);
Console.WriteLine($"[Pipeline] Handled {typeof(TRequest).Name}");
return result;
}
}
Pipeline without Result
Implement IRequestPipeline<TRequest>:
using Brokly.Contracts.Pipeline;
using Brokly.Contracts.RequestsHandling;
public class AuditPipeline<TRequest> : IRequestPipeline<TRequest>
where TRequest : IRequest
{
public async Task ExecuteAsync(
TRequest request,
PipelineDelegate complete,
CancellationToken ct)
{
Console.WriteLine($"[Pipeline] Auditing {typeof(TRequest).Name}");
await complete(ct);
Console.WriteLine($"[Pipeline] Audit complete for {typeof(TRequest).Name}");
}
}
Register Pipelines
Register in DI:
builder.Services.AddTransient(
typeof(IRequestPipeline<,>),
typeof(LoggingPipeline<,>));
builder.Services.Transient(
typeof(IRequestPipeline<>),
typeof(AuditPipeline<>));
Execution Flow
- All registered pipelines execute before the handler (in registration order)
- The handler’s
HandleAsyncruns - Pipelines execute after the handler (in reverse order)
Processors
Processors provide per-handler middleware via attributes, for commands and queries (with or without result).
Processor without Result
Inherit from ProcessorBase<TRequest>:
using Brokly.Application.Pipeline.Processors;
using Brokly.Contracts.RequestsHandling;
public class ValidationProcessor<TRequest> : ProcessorBase<TRequest>
where TRequest : IRequest
{
protected override Task PreProcess(TRequest request, CancellationToken ct)
{
Console.WriteLine($"[Processor] Validating {typeof(TRequest).Name}");
// throw if invalid
return Task.CompletedTask;
}
protected override Task PostProcess(CancellationToken ct)
{
Console.WriteLine($"[Processor] Validation complete");
return Task.CompletedTask;
}
}
Processor with Result
Inherit from ProcessorBase<TRequest, TResult>:
using Brokly.Application.Pipeline.Processors;
using Brokly.Contracts.RequestsHandling;
public class MetricsProcessor<TRequest, TResult> : ProcessorBase<TRequest, TResult>
where TRequest : IRequest<TResult>
{
protected override Task PreProcess(TRequest request, CancellationToken ct)
{
Console.WriteLine($"[Processor] Starting metrics for {typeof(TRequest).Name}");
return Task.CompletedTask;
}
protected override Task PostProcess(TResult response, CancellationToken ct)
{
Console.WriteLine($"[Processor] Metrics recorded for {typeof(TRequest).Name}");
return Task.CompletedTask;
}
}
Apply to Handlers
Decorate handlers with [UseProcessor] (multiple supported):
using Brokly.Application.Pipeline.Processors;
[UseProcessor(typeof(MetricsProcessor<CreateOrderCommand, OrderResult>))]
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand, OrderResult>
{
public Task<OrderResult> HandleAsync(CreateOrderCommand request, CancellationToken ct)
{
// handle order creation
}
}
OR
using Brokly.Application.Pipeline.Processors;
[UseProcessor(typeof(ValidationProcessor<CreateOrderCommand>))]
public class CreateOrderHandler : IRequestHandler<CreateOrderCommand>
{
public Task HandleAsync(CreateOrderCommand request, CancellationToken ct)
{
// handle order creation
}
}
Register Processors
Register explicitly or bulk-scan:
builder.Services.AddTransient<
IProcessor<CreateOrderCommand>,
ValidationProcessor<CreateOrderCommand>>();
builder.Services.AddTransient<
IProcessor<CreateOrderCommand, OrderResult>,
MetricsProcessor<CreateOrderCommand, OrderResult>>();
Execution Flow per Handler
PreProcesson each processor (in attribute/declaration order)- Handler’s
HandleAsync PostProcesson each processor (in reverse order)
Configuration Options
Configure BroklyOptions in the AddBrokly lambda:
AddHandlersFromAssembly(Assembly)/AddHandlersFromAssemblies(Assembly[])UseEventHandling(bool)– enable or disable event busUseRequestPipelines(bool)– toggle request pipelinesSetMaxQueueSize(int)– maximum in‑flight events in the channel queueAddConsumers(int)– number of parallel event‑processing workers Ifconsumers count is 1, FIFO approach for events handling is guaranteed, else it's not. The more consumers there are, the faster events are handled
See the full BroklyOptions API in the Domain project.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/YourFeature) - Commit your changes (
git commit -m 'Add some feature') - Push to the branch (
git push origin feature/YourFeature) - Open a Pull Request
Please follow the existing code style and include tests where appropriate.
License
This project is licensed under the MIT License. See the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. 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. |
-
net9.0
- Brokly.Contracts (>= 1.1.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.4)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.