The Principal Dev – Masterclass for Tech Leads

The Principal Dev – Masterclass for Tech Leads28-29 May

Join

Brighter

canon

NuGet Version NuGet Downloads CI Coverity Scan Build Status CodeScene Code Health CodeScene System Mastery

Brighter is a Command Dispatcher and Command Processor framework for .NET. It enables you to build loosely coupled, maintainable applications using the Command pattern and supports both in-process and out-of-process messaging for microservices architectures. Its companion project, Darker, provides the query side for CQRS architectures.

Why Brighter?

Prerequisites

Quick Start

Installation

dotnet add package Paramore.Brighter.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Hosting

Basic Usage - Command Dispatcher

1. Define a Command

public class GreetingCommand(string name) : Command(Id.Random())
{
    public string Name { get; } = name;
}

2. Create a Handler

public class GreetingCommandHandler : RequestHandler<GreetingCommand>
{
    public override GreetingCommand Handle(GreetingCommand command)
    {
        Console.WriteLine($"Hello {command.Name}");
        return base.Handle(command);
    }
}

3. Configure and Send

// Setup
var builder = Host.CreateApplicationBuilder();
builder.Services.AddBrighter().AutoFromAssemblies();
var host = builder.Build();

var commandProcessor = host.Services.GetRequiredService<IAmACommandProcessor>();

// Send command (in-process)
commandProcessor.Send(new GreetingCommand("World"));

Note: For async operations, use RequestHandlerAsync<T> and override HandleAsync() instead, then call SendAsync().

Out-of-Process Messaging

For microservices communication, Brighter can send and receive events via external message brokers. This typically involves two separate applications: a sender that posts events and a consumer that processes them.

First, install a transport package (pick one for your broker):

dotnet add package Paramore.Brighter.MessagingGateway.RMQ.Async  # RabbitMQ
dotnet add package Paramore.Brighter.MessagingGateway.AWSSQS.V4  # AWS SQS (uses AWS SDK v4; use AWSSQS for v3)
dotnet add package Paramore.Brighter.MessagingGateway.Kafka  # Kafka

For the consumer application, also install:

dotnet add package Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection
dotnet add package Paramore.Brighter.ServiceActivator.Extensions.Hosting

1. Define an Event

public class GreetingEvent(string name) : Event(Id.Random())
{
    public string Name { get; } = name;
}

2. Configure Producers and Post (Sender App)

var builder = Host.CreateApplicationBuilder();

var rmqConnection = new RmqMessagingGatewayConnection
{
    AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
    Exchange = new Exchange("paramore.brighter.exchange"),
};

var producerRegistry = new RmqProducerRegistryFactory(
    rmqConnection,
    [
        new() { Topic = new RoutingKey("greeting.event"), RequestType = typeof(GreetingEvent) }
    ]).Create();

builder.Services.AddBrighter()
    .AutoFromAssemblies()
    .AddProducers(configure =>
    {
        configure.ProducerRegistry = producerRegistry;
    });

var host = builder.Build();
var commandProcessor = host.Services.GetRequiredService<IAmACommandProcessor>();

// Post to external message broker
commandProcessor.Post(new GreetingEvent("World"));

3. Create an Event Handler (Consumer App)

public class GreetingEventHandler : RequestHandler<GreetingEvent>
{
    public override GreetingEvent Handle(GreetingEvent @event)
    {
        Console.WriteLine($"Received greeting for {@event.Name}");
        return base.Handle(@event);
    }
}

4. Configure Consumer Subscriptions (Consumer App)

var builder = Host.CreateApplicationBuilder();

var rmqConnection = new RmqMessagingGatewayConnection
{
    AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
    Exchange = new Exchange("paramore.brighter.exchange"),
};

var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(rmqConnection);

builder.Services.AddConsumers(options =>
{
    options.Subscriptions =
    [
        new RmqSubscription<GreetingEvent>(
            new SubscriptionName("greeting-subscription"),
            new ChannelName("greeting.event"),
            new RoutingKey("greeting.event"),
            messagePumpType: MessagePumpType.Reactor,
            makeChannels: OnMissingChannel.Create)
    ];
    options.DefaultChannelFactory = new ChannelFactory(rmqMessageConsumerFactory);
}).AutoFromAssemblies();

builder.Services.AddHostedService<ServiceActivatorHostedService>();

var host = builder.Build();
await host.RunAsync();

Key Features

Middleware Pipeline

Add cross-cutting concerns via attributes on your handlers:

public class GreetingCommandHandler : RequestHandler<GreetingCommand>
{
    [RequestLogging(step: 1, timing: HandlerTiming.Before)]
    [UseResiliencePipeline("MyRetryPolicy", step: 2)]
    public override GreetingCommand Handle(GreetingCommand command)
    {
        Console.WriteLine($"Hello {command.Name}");
        return base.Handle(command);
    }
}

Register resilience pipelines using Polly's ResiliencePipelineRegistry and pass them to AddBrighter():

var resiliencePipelineRegistry = new ResiliencePipelineRegistry<string>();

// Add Brighter's required internal pipelines
resiliencePipelineRegistry.AddBrighterDefault();

// Add your own pipelines
resiliencePipelineRegistry.TryAddBuilder("MyRetryPolicy",
    (builder, _) => builder.AddRetry(new RetryStrategyOptions
    {
        BackoffType = DelayBackoffType.Linear,
        Delay = TimeSpan.FromSeconds(1),
        MaxRetryAttempts = 3
    }));

builder.Services.AddBrighter(options =>
{
    options.ResiliencePipelineRegistry = resiliencePipelineRegistry;
}).AutoFromAssemblies();

Available Middleware:

Outbox Pattern for Reliable Messaging

Brighter implements the Outbox pattern to guarantee message delivery in distributed systems. Instead of calling Post directly, use DepositPost to write messages to the Outbox within your database transaction, then ClearOutbox to dispatch them after the transaction commits. This ensures consistency between your database state and published messages.

Supported Outbox Stores: PostgreSQL, MySQL, MSSQL, SQLite, DynamoDB, MongoDB. See the full documentation for configuration details.

Multiple Messaging Patterns

Supported Transports

Use Cases

Clean Architecture / Hexagonal Architecture

Use Brighter as your application's port layer. Commands and Events represent your use cases, and handlers implement the application logic.

Microservices Integration

Abstract away messaging infrastructure. Developers write domain code (commands/events and handlers) while Brighter handles message routing, serialization, and transport.

Task Queue / Job Processing

Use Brighter's Service Activator to consume messages from queues and process them with retry, circuit breaker, and monitoring built-in.

Learn More

📚 Full Documentation - Comprehensive guides, tutorials, and API reference

Topics covered in the documentation:

Community & Support

NuGet Packages

Latest stable releases are available on NuGet.

Core Packages:

Transport Packages:

Outbox/Inbox Packages:

For bleeding-edge builds from the master branch, see GitHub Packages.

Contributing

We welcome contributions! See our Contributing Guide for details on:

License

Brighter is licensed under the MIT licence. See LICENCE for details.


Acknowledgements

Portions of this code are based on Stephen Cleary's AsyncEx's AsyncContext.

Join libs.tech

...and unlock some superpowers

GitHub

We won't share your data with anyone else.