# Basic Configuration

Configuration is the most labor-intensive part of using Brighter. Once you have configured Brighter, using its model of requests and handlers is straightforward.

## **Using .NET Core Dependency Injection**

This section covers using .NET Core Dependency Injection to configure Brighter. If you want to use an alternative DI container then see the section [How Configuration Works](https://github.com/BrighterCommand/Docs/blob/master/contents/HowConfigurationWorks.md)

We divide configuration into two sections, depending on your requirements:

* [**Configuring The Command Processor**](#configuring-the-command-processor): This section covers configuring the **Command Processor**. Use this if you want to dispatch requests to handlers, or publish messages from your application on an external bus
* [**Configuring The Dispatcher**](#configuring-the-dispatcher): This section covers configuring the **Dispatcher** (message consumer). Use this if you want to read messages from a transport (and then dispatch to handlers).

## **Configuring The Command Processor**

### **Command Processor Service Collection Extensions**

Brighter's package:

* **Paramore.Brighter.Extensions.DependencyInjection**

provides extension methods for **ServiceCollection** that can be used to add Brighter to the .NET Core DI Framework.

By adding the package you can call the **AddBrighter()** extension method.

If you are using a **Startup** class's **ConfigureServices** method call the following:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(...)
}

```

if you are using .NET 6 you can make the call directly on your **HostBuilder**'s Services property.

The **AddBrighter()** method takes an **`Action<BrighterOptions>`** delegate. The extension method supplies the delegate with a **BrighterOptions** object that allows you to configure how Brighter runs.

The **AddBrighter()** method returns an **IBrighterBuilder** interface. **IBrighterBuilder** is a [fluent interface](https://en.wikipedia.org/wiki/Fluent_interface) that you can use to configure additional Brighter properties (see [Brighter Builder Fluent Interface](#brighter-builder-fluent-interface)).

#### **Adding Polly Resilience Pipelines**

**V10**: Brighter supports Polly v8 resilience pipelines for both internal reliability and custom resilience strategies in your handlers.

To use Polly with Brighter, register resilience pipelines with a **ResiliencePipelineRegistry**:

```csharp
using Polly;
using Polly.Registry;
using Polly.Retry;
using Polly.CircuitBreaker;

var resiliencePipelineRegistry = new ResiliencePipelineRegistry<string>();

resiliencePipelineRegistry.TryAddBuilder("RetryPipeline",
    (builder, context) => builder.AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay = TimeSpan.FromMilliseconds(50),
        BackoffType = DelayBackoffType.Linear
    }));

resiliencePipelineRegistry.TryAddBuilder("CircuitBreakerPipeline",
    (builder, context) => builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions
    {
        FailureRatio = 0.5,
        MinimumThroughput = 10,
        BreakDuration = TimeSpan.FromSeconds(30)
    }));
```

And use them in your handler like this:

```csharp
internal class MyQoSProtectedHandler : RequestHandler<MyCommand>
{
    [UseResiliencePipeline(policy: "RetryPipeline", step: 1)]
    public override MyCommand Handle(MyCommand command)
    {
        // Do work that could throw errors due to distributed computing reliability
        return base.Handle(command);
    }
}
```

See the section [Policy Retry and Circuit Breaker](https://brightercommand.gitbook.io/paramore-brighter-documentation/brighter-request-handlers-and-middleware-pipelines/policyretryandcircuitbreaker) for more on using Polly resilience pipelines with handlers.

With the resilience pipeline registry configured, you need to tell Brighter where to find it:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(options =>
        options.PolicyRegistry = new PolicyRegistry() // Optional: for legacy Polly v7 policies
    )
    .ConfigureResiliencePipelines(registry =>
    {
        registry.TryAddBuilder("RetryPipeline", /* ... */);
        registry.TryAddBuilder("CircuitBreakerPipeline", /* ... */);
    });
}
```

> **Note**: For legacy Polly v7 policies using `[UsePolicy]`, see the [migration guide](https://brightercommand.gitbook.io/paramore-brighter-documentation/brighter-request-handlers-and-middleware-pipelines/policyretryandcircuitbreaker#migration-guide-v9-to-v10) for updating to V10 resilience pipelines.

#### **Configuring Lifetimes**

Brighter can register your *Request Handlers* and *Message Mappers* for you (see [IBrighter Builder Fluent Interface](#ibrighterbuilder-fluent-interface)).

When we register *Request Handlers* and *Message Mappers* for you with ServiceCollection, we need to register them with a given lifetime (see [Dependency Injection Service Lifetimes](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes)).

We recommend the following lifetimes:

* If you are using *Scoped* lifetimes, for example with EF Core, make your *Request Handlers* *Scoped* as well.
* If you are not using *Scoped* lifetimes you can use *Transient* lifetimes for *Request Handlers*.
* You can use the default Transient lifetime for *Message Mappers* but as your *Message Mapper* generally does not have state that persists between invocations you can save allocations by making it a *Singleton*.

(Be cautious about using *Singleton* lifetimes for *Request Handlers*. Even if your *Request Handler* is stateless today, and so does not risk carrying state across requests, a common bug is that state is added to an existing *Request Handler* which has previously been registered as a *Singleton*.)

You configure the lifetimes for the different types that Brighter can create at run-time as follows:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(options =>
        options.HandlerLifetime = ServiceLifetime.Scoped;
        options.MapperLifetime = ServiceLifetime.Singleton;
    );
}

```

#### **Service Provider Function Overloads**

In addition to the standard `Action<BrighterOptions>` delegate, Brighter provides overloads that accept `Func<IServiceProvider, T>` delegates. These enable **deferred resolution** - your configuration code can access services from the DI container that may be registered later in the setup process.

**Why Use Service Provider Overloads?**

* **Deferred Resolution**: Access services registered elsewhere in your DI configuration
* **Runtime Service Access**: Resolve services like `IConfiguration`, custom factories, or test doubles at runtime
* **Test Isolation**: Each test can have its own isolated configuration without static state conflicts

**AddBrighter with Service Provider**

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // Register a custom factory first
    services.AddSingleton<IAmARequestContextFactory, MyCustomContextFactory>();

    // Use the Func<IServiceProvider, BrighterOptions> overload
    services.AddBrighter(sp => new BrighterOptions
    {
        HandlerLifetime = ServiceLifetime.Scoped,
        MapperLifetime = ServiceLifetime.Singleton,
        RequestContextFactory = sp.GetRequiredService<IAmARequestContextFactory>()
    })
    .AutoFromAssemblies();
}
```

**AddProducers with Service Provider**

The `AddProducers` method also supports a service provider overload for configuring your external bus producers:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(options =>
    {
        options.HandlerLifetime = ServiceLifetime.Scoped;
    })
    .AddProducers(sp =>
    {
        var configuration = sp.GetRequiredService<IConfiguration>();
        var connectionString = configuration.GetConnectionString("RabbitMQ");

        return new ProducersConfiguration
        {
            ProducerRegistry = new RmqProducerRegistryFactory(
                new RmqMessagingGatewayConnection
                {
                    AmpqUri = new AmqpUriSpecification(new Uri(connectionString)),
                    Exchange = new Exchange("paramore.brighter.exchange"),
                },
                new RmqPublication[]
                {
                    new RmqPublication
                    {
                        Topic = new RoutingKey("GreetingMade"),
                        MakeChannels = OnMissingChannel.Create
                    }
                }
            ).Create()
        };
    })
    .AutoFromAssemblies();
}
```

**AddConsumers with Service Provider**

Similarly, `AddConsumers` supports deferred resolution:

```csharp
private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
    services.AddConsumers(sp =>
    {
        var configuration = sp.GetRequiredService<IConfiguration>();

        return new ConsumersOptions
        {
            Subscriptions = GetSubscriptions(sp),
            DefaultChannelFactory = CreateChannelFactory(sp, configuration),
            HandlerLifetime = ServiceLifetime.Scoped
        };
    })
    .AutoFromAssemblies();
}
```

#### **Using the Options Pattern**

Brighter integrates with Microsoft's [Options pattern](https://docs.microsoft.com/en-us/dotnet/core/extensions/options), allowing you to use `Configure<T>` and `PostConfigure<T>` to modify options after initial registration. This is particularly useful for testing scenarios where you need to override specific settings.

**PostConfigure for Test Overrides**

The `PostConfigure<BrighterOptions>` method runs after all `Configure` calls, allowing you to override settings for tests:

```csharp
// In your test setup
public class MyIntegrationTests
{
    private ServiceProvider BuildTestServiceProvider()
    {
        var services = new ServiceCollection();

        // Standard Brighter configuration (could be shared with production)
        services.AddBrighter(options =>
        {
            options.HandlerLifetime = ServiceLifetime.Scoped;
        })
        .AutoFromAssemblies();

        // Test-specific overrides using PostConfigure
        services.PostConfigure<BrighterOptions>(options =>
        {
            options.RequestContextFactory = new TestRequestContextFactory();
        });

        return services.BuildServiceProvider();
    }
}
```

**Combining with Standard .NET Options Patterns**

You can combine Brighter's configuration with other Options pattern features:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    // Bind from configuration
    services.Configure<BrighterOptions>(Configuration.GetSection("Brighter"));

    // Add Brighter with defaults
    services.AddBrighter(options =>
    {
        options.HandlerLifetime = ServiceLifetime.Scoped;
    })
    .AutoFromAssemblies();

    // Environment-specific overrides
    if (Environment.IsDevelopment())
    {
        services.PostConfigure<BrighterOptions>(options =>
        {
            options.RequestContextFactory = new DebugRequestContextFactory();
        });
    }
}
```

**Benefits for Parallel Test Execution**

The combination of service provider overloads and the Options pattern enables parallel test execution without the need for test serialization:

* Each test can build its own `ServiceProvider` with isolated configuration
* No static state conflicts between tests running in parallel
* Test-specific overrides don't affect other tests

```csharp
public class ParallelTestsA
{
    [Fact]
    public async Task Test_With_Custom_Context()
    {
        var services = new ServiceCollection();
        services.AddBrighter(options => { })
            .AutoFromAssemblies();

        services.PostConfigure<BrighterOptions>(o =>
            o.RequestContextFactory = new TestContextFactoryA());

        await using var provider = services.BuildServiceProvider();
        var processor = provider.GetRequiredService<IAmACommandProcessor>();
        // Test runs isolated from other parallel tests
    }
}

public class ParallelTestsB
{
    [Fact]
    public async Task Test_With_Different_Context()
    {
        var services = new ServiceCollection();
        services.AddBrighter(options => { })
            .AutoFromAssemblies();

        services.PostConfigure<BrighterOptions>(o =>
            o.RequestContextFactory = new TestContextFactoryB());

        await using var provider = services.BuildServiceProvider();
        var processor = provider.GetRequiredService<IAmACommandProcessor>();
        // Runs in parallel with TestsA without conflicts
    }
}
```

### **Validating Your Configuration**

We recommend enabling pipeline validation and diagnostics as part of your standard configuration. This catches common misconfiguration errors — such as sync/async mismatches, incorrect attribute ordering, and missing handlers — at startup rather than at runtime.

```csharp
builder.Services.AddBrighter(options =>
    {
        options.HandlerLifetime = ServiceLifetime.Scoped;
    })
    .AutoFromAssemblies()
    .ValidatePipelines()
    .DescribePipelines();
```

**ValidatePipelines** checks your configuration and throws a `PipelineValidationException` if errors are found. **DescribePipelines** logs a structured report of your configured pipelines to `ILogger`. Both are opt-in and independent of each other.

For full details on what gets checked, how to configure validation flags, and how to interpret the diagnostic report, see [Pipeline Validation and Diagnostics](https://brightercommand.gitbook.io/paramore-brighter-documentation/brighter-configuration/pipelinevalidation).

### **Brighter Builder Fluent Interface**

#### **Type Registration**

The **IBrighterBuilder** fluent interface can scan your assemblies for your *Request Handlers* (inherit from **IHandleRequests<>** or **IHandleRequestsAsync<>**) and *Message Mappers* (inherit from **IAmAMessageMapper<>**) and register then with the **ServiceCollection**. This is the most common way to register your code.

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(...)
        .AutoFromAssemblies();
}

```

The code scans any loaded assemblies. If you need to register types from assemblies that are not yet loaded, you can provide a list of additional assemblies to scan as an argument to the call to **AutoFromAssemblies()**.

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(...)
        .AutoFromAssemblies(typeof(MyRequestHandlerAsync).Assembly);
}

```

Instead of using **AutoFromAssemblies** you can exert more fine-grained control over the registration, by explicitly registering your *Request Handlers* and *Message Mappers*. We recommend this for cases where the automatic registration does not meet your needs, such as using Brighter's support for an Agreement Dispatcher:

* **MapperRegistryFromAssemblies()**, **HandlersFromAssemblies()** and **AsyncHandlersFromAssemblies** are the methods called by **AutoFromAssemblies()** and can be called explicitly.
* **Handlers()**, **AsyncHandlers()** and **MapperRegistry()** accept an **Action<>** delegate that respectively provide you with **IAmASubscriberRegistry** or **IAmAnAsyncSubscriberRegistry** to register your RequestHandlers explicitly or a **ServiceCollectionMapperRegistry** to register your mappers. This gives you explicit control over what you register.

#### **Using an External Bus**

Using an *External Bus* allows you to send messages between processes using a message-oriented middleware transport (such as RabbitMQ or Kafka). (For symmetry, we refer to the usage of the *Command Processor* without an external bus as using an *Internal Bus*).

When raising a message on the *Internal Bus*, you use one of the following methods on the *Command Processor*:

* **Send()** and **SendAsync()** - Sends a *Command* to one *Request Handler*.
* **Publish()** and **PublishAsync()** - Broadcasts an *Event* to zero or more *Request Handlers*.

When raising a message on an *External Bus*, you use the following methods on the *CommandProcessor*:

* **Post()** and **PostAsync()** - Immediately posts a *Command* or *Event* to another process via the external Bus
* **DepositPost()** and **DepositPostAsync()** - Puts one or many *Command*(s) or *Event*(s) in the *Outbox* for later delivery
* **ClearOutbox()** and **ClearOutboxAsync()** - Clears the *Outbox*, posting un-dispatched messages to another process via the *External Bus*.
* **ClearAsyncOutbox()** - Implicitly clears the **Outbox**, similar to above however allows bulk dispatching of messages onto a **Transport**.

The major difference here is whether or not you wish to use an *Outbox* for Transactional Messaging. (See [Outbox Pattern](https://brightercommand.gitbook.io/paramore-brighter-documentation/event-driven-architectures/outboxpattern) and [Brighter Outbox Support](https://brightercommand.gitbook.io/paramore-brighter-documentation/outbox-and-inbox/brighteroutboxsupport) for more on Brighter and the Outbox Pattern).

#### Configuring an External Bus

To use an *External Bus*, you need to supply Brighter with configuration information that tells Brighter what middleware you are using and how to find it. (You don't need to do anything to configure an *Internal Bus*, it is always available.)

The **IBrighterBuilder** interface returned from **AddBrighter** allows you to configure the properties of your external bus, by calling the **AddProducers** extension method. The AddProducers extension method takes a lambda function, whose only parameter is an **ExternalBusConfiguration**.

```csharp
private void ConfigureBrighter(IServiceCollection services)
{
    services.AddBrighter(options =>
        {
            ...
        })
        .AddProducers((configure) =>
        {
        })
        .AutoFromAssemblies();

```

#### Transports

*Transports* are how Brighter supports specific Message-Oriented-Middleware (MoM). *Transports* are provided in separate NuGet packages so that you can take a dependency only on the transport that you need. Brighter supports a number of different *transports*.

We use the naming convention **Paramore.Brighter.MessagingGateway.{TRANSPORT}** for *transports* where {TRANSPORT} is the name of the middleware.

In this example we will use the transport for RabbitMQ, provided by the NuGet package:

* **Paramore.Brighter.MessagingGateway.RMQ**

See the documentation for detail on specific *transports* on how to configure them for use with Brighter, for now it is enough to know that you need to provide a *Messaging Gateway* which tells us how to reach the middleware and a *Publication* which tells us how to configure the middleware.

#### **Publications**

A *Publication* configures a transport for sending a message to it's associated MoM. So an **RmqPublication** configures how we publish a message to RabbitMQ. There are a number of common properties to all publications.

* **MakeChannels**: Do you want Brighter to create the infrastructure? Brighter can create infrastructure that it needs, and is aware of: **OnMissingChannel.Create**. So a publication can create the topic to send messages to. Alternatively if you create the channel by another method, such as IaaC, we can verify the infrastructure on startup: **OnMissingChannel.Validate**. Finally, you can avoid the performance cost of runtime checks by assuming your infrastructure exists: **OnMissingChannel.Assume**.
* **MaxOutstandingMessages**: How large can the number of messages in the Outbox grow before we stop allowing new messages to be published and raise an **OutboxLimitReachedException**.
* **MaxOutStandingCheckIntervalMilliSeconds**: How often do we check to see if the Outbox is full.
* **Topic**: A Topic is the key used within the MoM to route messages. Publishers publish to a topic and subscribers, subscribe to it. We use a class **RoutingKey** to encapsulate the identifier used for a topic. The name the MoM uses for a topic may vary. Kafka & SNS use *topic* whilst RMQ uses *routingkey*

#### Producer Registry

In order to provide Brighter with the means to send a message via the transport, we need to provide it with an **IAmAProducerRegistry** for the transport you intend to use for the *External Bus*.

A **producer** is the transport specific code that you need to send messages; it implements **IAmAMessageProducer**.

We register a **producer** with a **producer registry**; it needs to implement **IAmAProducerRegistry** but usually you will use the provided **ProducerRegistry**. At runtime, we lookup the producer to use in the registry by **routing key** (aka topic).

Typically for a transport we implement a **producer registry factory**; it needs to implement **IAmAProducerRegistryFactory**. For example, for the RMQ transport, we provide **RmqProducerRegistryFactory**. A **producer registry factory** typically takes a **connection** to the broker and a collection of **publications**, and it iterates over the **publications** creating a producer for each one and registering it in a **producer registry**. It then returns a configured **producer registry**.

The following code shows an application using the RMQ transport support to create its **producer registry**.

```csharp
var producerRegistry = new RmqProducerRegistryFactory(
    new RmqMessagingGatewayConnection
    {
        AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
        Exchange = new Exchange("paramore.brighter.exchange"),
    },
    new RmqPublication[]
    {
        new RmqPublication
        {
            Topic = new RoutingKey("GreetingMade"),
            MaxOutStandingMessages = 5,
            MaxOutStandingCheckIntervalMilliSeconds = 500,
            WaitForConfirmsTimeOutInMilliseconds = 1000,
            MakeChannels = OnMissingChannel.Create
        }
    }
).Create();
```

#### **Bus Example**

Putting this together, an example configuration for an External Bus for a local RabbitMQ instance could look like this:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(...)
        .AddProducers((configure) =>
        {
            configure.ProducerRegistry = new RmqProducerRegistryFactory(
                new RmqMessagingGatewayConnection
                {
                    AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
                    Exchange = new Exchange("paramore.brighter.exchange"),
                },
                new RmqPublication[]{
                    new RmqPublication
                {
                    Topic = new RoutingKey("GreetingMade"),
                    MaxOutStandingMessages = 5,
                    MaxOutStandingCheckIntervalMilliSeconds = 500,
                    WaitForConfirmsTimeOutInMilliseconds = 1000,
                    MakeChannels = OnMissingChannel.Create
                }}
            ).Create();
        })
        .AutoFromAssemblies()

            ...
}
```

#### **Outbox Support**

If you intend to use Brighter's *Outbox* support for Transactional Messaging then you need to provide us with details of your *Outbox*.

Brighter provides a number of *Outbox* implementations for common Dbs (and you can write your own for a Db that we do not support). For this discussion we will look at Brighter's support for working with EF Core. See the documentation for working with specific *Outbox* implementations.

EF Core supports a number of databases and you should pick the packages that match the Dy you want to use with EF Core. In this case we will choose MySQL.

For this we will need the *Outbox* packages for the MySQL *Outbox*.

* **Paramore.Brighter.MySql**
* **Paramore.Brighter.Outbox.MySql**

For a given backing store the pattern should be Paramore.Brighter.{DATABASE} and Paramore.Brighter.Outbox.{DATABASE} where {DATABASE} is the name of the Db that you are using.

In addition for an ORM you will need to add the package that supports the ORM, in this case EF Core:

* **Paramore.Brighter.MySql.EntityFrameworkCore**

For a given ORM the pattern should be Paramore.Brighter.{ORM}.{DATABASE} where {ORM} is the ORM you are choosing and {DATABASE} is the Db you are using with the ORM.

#### Configuring the Outbox

To configure our *Outbox* we need to use the **ExternalBusConfiguration**.

An Outbox has three pieces:

* The *Outbox*, which implements **IAmAnOutbox**. Brighter provides implementations for a range of common Dbs.
* The *Connection Provider* which tells Brighter how to connect to the *Outbox*
* The *Transaction Provider* which allows Brighter to participate in the same transaction that you update an entity with.

In this example, we want to use EF Core with an MySQL Outbox. See the documentation for Outboxes for specific configuration options.

```csharp
public void ConfigureServices(IServiceCollection services)
{
            
    var outboxConfiguration = new RelationalDatabaseConfiguration(DbConnectionString());
    services.AddSingleton<IAmARelationalDatabaseConfiguration>(outboxConfiguration);

    services.AddBrighter(...)
        .AddProducers((configure) =>
        {
            configure.Outbox = new MySqlOutbox(outboxConfiguration);
            configure.TransactionProvider = typeof(MySqlEntityFrameworkConnectionProvider<GreetingsEntityGateway>);
            configure.ConnectionProvider = typeof(MySqlConnectionProvider);
        })
        .AutoFromAssemblies();

        ...
}

```

Typically **DbConnectionString** would obtain the connection string for the Db from configuration.

#### Outbox Sweeper

Finally, if we want the *Outbox* to use a background thread to clear un-dispatched items from the *Outbox*, and we do in most circumstances, we need to run an *Outbox Sweeper* to do this work. (You can force an immediate clear within the code that produces the outgoing message using **ClearOutbox**, but you should still have a sweeper to guarantee it is sent if that call fails).

Typically you run one sweeper. Brighter provides a variety of distributed lock implementations to help you run a single sweeper at a time, with other sweepers available as a hot standby. Typically, you run the Sweeper in a stand-alone console project. The outbox documentation looks at your strategies for ensuring only one sweeper runs.

For development purposes though, you may wish to add a sweeper to the instance that you are currently running.

To add the *Outbox Sweeper* you will need to take a dependency on another NuGet package:

* **Paramore.Brighter.Outbox.Hosting**

You can then add a sweeper using "UseOutboxSweeper"

For a development version, using an internal sweeper, this results in:

```csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddBrighter(...)
        .AddProducers(...)
         .UseOutboxSweeper()
         .AutoFromAssemblies();
        ...
}

```

#### Request-Reply

(**AddProducers()** has optional parameters for use with Request-Reply support for some transports. We don't cover that here, instead see [Direct Messaging](https://brightercommand.gitbook.io/paramore-brighter-documentation/using-an-external-bus/routing#direct-messaging) for more).

#### **Configuring JSON Serialization**

Brighter defines a set of serialization options for use when it needs to serialize messages to JSON. Internally we use these options in our transports, when serializing messages to an external bus and deserializing from an external bus. We also use them in default JSON message mappers. You may wish to use these options in your own [*Message Mapper*](https://brightercommand.gitbook.io/paramore-brighter-documentation/using-an-external-bus/messagemappers) implementation.

By default our JSONSerialization Options are configured as follows:

```csharp
static JsonSerialisationOptions()
{
    var opts = new JsonSerializerOptions
    {
        PropertyNameCaseInsensitive = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        NumberHandling = JsonNumberHandling.AllowReadingFromString,
        AllowTrailingCommas = true
    };

    opts.Converters.Add(new JsonStringConverter());
    opts.Converters.Add(new DictionaryStringObjectJsonConverter());
    opts.Converters.Add(new ObjectToInferredTypesConverter());
    opts.Converters.Add(new JsonStringEnumConverter());

    Options = opts;
}
```

You can use the **IBrighterBuilder** extension **ConfigureJsonSerialisation** to override these values. The method takes an **Action\<JsonSerialisationOptions>** lambda expression that allows you to override these defaults. For example:

```csharp

.ConfigureJsonSerialisation((options) =>
{
    options.PropertyNameCaseInsensitive = true;
})

```

If you want to use this configured set of JSON Serialization options in your own code, you can, by using the static property JsonSerialisationOptions.Options. For example:

```csharp
public GreetingMade MapToRequest(Message message)
{
    return JsonSerializer.Deserialize<GreetingMade>(message.Body.Value, JsonSerialisationOptions.Options);
}
```

#### Retry and Circuit Breaker with an External Bus

When sending a request via the External Bus we use a Polly policy internally to control Retry and Circuit Breaker in case the External Bus is not available. These policies have defaults but you can configure the behavior using the policy keys:

* **Paramore.RETRYPOLICY**
* **Paramore.CIRCUITBREAKER**

### **Putting It All Together**

Putting all this together, a typical configuration might looks as follows:

```csharp
public void ConfigureServices(IServiceCollection services)
{

    var outboxConfiguration = new RelationalDatabaseConfiguration(DbConnectionString());
    services.AddSingleton<IAmARelationalDatabaseConfiguration>(outboxConfiguration);

    services.AddBrighter(options =>
        {
            options.HandlerLifetime = ServiceLifetime.Scoped;
            options.MapperLifetime = ServiceLifetime.Singleton;
            options.PolicyRegistry = policyRegistry;
        })
        .ConfigureJsonSerialisation((options) =>
        {
            options.PropertyNameCaseInsensitive = true;
        })
       .AddProducers((configure) =>
        {
            configure.ProducerRegistry = new RmqProducerRegistryFactory(
                new RmqMessagingGatewayConnection
                {
                    AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
                    Exchange = new Exchange("paramore.brighter.exchange"),
                },
                new RmqPublication[]{
                    new RmqPublication
                {
                    Topic = new RoutingKey("GreetingMade"),
                    MaxOutStandingMessages = 5,
                    MaxOutStandingCheckIntervalMilliSeconds = 500,
                    WaitForConfirmsTimeOutInMilliseconds = 1000,
                    MakeChannels = OnMissingChannel.Create
                }}
            ).Create();
           configure.Outbox = new MySqlOutbox(outboxConfiguration);
           configure.TransactionProvider = typeof(MySqlEntityFrameworkConnectionProvider<GreetingsEntityGateway>);
           configure.ConnectionProvider = typeof(MySqlConnectionProvider);
        })
        .UseOutboxSweeper()
        .AutoFromAssemblies();
}

```

## **Configuring The Dispatcher**

A *consumer* reads messages from Message-Oriented Middleware (MoM), and a *producer* puts messages onto the MoM for the *consumer* to read.

A *consumer* waits for messages to appear on the queue, reads them, and then calls your *Request Handler* code to react. The component that listens for messages and dispatches them to handlers is called a **Dispatcher**. (In Enterprise Integration Patterns terminology, this is called a [*Service Activator*](https://www.enterpriseintegrationpatterns.com/patterns/messaging/MessagingAdapter.html), and the assembly name reflects this, but we use "Dispatcher" for simplicity.)

To use Brighter's Dispatcher you will need to take a dependency on the NuGet package:

* **Paramore.Brighter.ServiceActivator** (assembly name, the component is called Dispatcher)

### **Dispatcher Service Collection Extensions**

We provide support for configuring .NET Core's **HostBuilder** as a message consumer (Dispatcher) for use with MoM. We use Brighter's Command Processor to dispatch the messages read by the Dispatcher. If you are not using **HostBuilder** then you will need to configure the Dispatcher yourself. See [How Configuring the Dispatcher Works](https://brightercommand.gitbook.io/paramore-brighter-documentation/brighter-configuration/howconfiguringthedispatcherworks) for more.

To use Brighter's Dispatcher with **HostBuilder** you will need to take a dependency on the following NuGet packages:

* **Paramore.Brighter.ServiceActivator.Extensions.Hosting**
* **Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection**

These provide an extension method **AddConsumers()** that can be used to add Brighter to the .NET Core DI Framework.

By adding the package you can call the **AddConsumers()** extension method.

If you are using a **HostBuilder** class's **ConfigureServices** method call the following:

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            services.AddConsumers(...)
        }

```

if you are using .NET 6 you can make the call direction on your **HostBuilder**'s Services property.

The **AddConsumers()** method takes an **`Action<ServiceActivatorOptions>`** delegate. The extension method supplies the delegate with a **ServiceActivatorOptions** object that allows you to configure how Brighter runs.

The **AddConsumers()** method returns an **IBrighterBuilder** interface. **IBrighterBuilder** is a [fluent interface](https://en.wikipedia.org/wiki/Fluent_interface) that you can use to configure Brighter *Command Processor* properties. It is discussed above at [Brighter Builder Fluent Interface](#brighter-builder-fluent-interface)) and the same options apply. We discuss one additional option that becomes important when receiving requests the *Inbox* in [Additional Brighter Builder Options](https://github.com/BrighterCommand/Docs/blob/master/contents/BasicConfiguration.md#the-inbox).

#### **Subscriptions**

When configuring your application's Dispatcher (message consumer), your *Subscriptions* configure how your application will receive messages from the associated MoM queues or streams.

All *Subscriptions* lets you configure the following common properties.

* **Buffer Size**: The number of messages to hold in memory. Where the buffer is not shared, a single thread or Performer can access these; where the buffer is shared, multiple threads can access the same buffer of work. Work in a buffer is locked on queue based middleware, and thus not available to other consumers (threads or process depending if the buffer is shared or not) until *Acknowledged* or *Rejected*.
* **Channel Factory**: Creates or finds the necessary infrastructure for messaging on the MoM and wraps it in an object.
* \**Channel Name*: If queues are primitives in the MoM this names the queue, otherwise just used for diagnostics.
* **Channel Failure Delay**: How long should we delay if a channel fails before trying again, to give problems time to clear.
* **Data Type**: We use a [Datatype Channel](https://www.enterpriseintegrationpatterns.com/DatatypeChannel.html). What is the type of this channel?
* **Empty Channel Delay**: If there are no messages in the queue or stream when we read, how long should we pause before reading again?
* **MakeChannels**: Do you want Brighter to create the infrastructure? Brighter can create infrastructure that it needs, and is aware of: **OnMissingChannel.Create**. So a subscription can create the topic to send messages to, and any subscription to that topic required by the MoM, including a queue (which uses the *Channel Name*). Alternatively if you create the channel by another method, such as IaaC, we can verify the infrastructure on startup: **OnMissingChannel.Validate**. Finally, you can avoid the performance cost of runtime checks by assuming your infrastructure exists: **OnMissingChannel.Assume**.
* **Name**: What do we call this subscription for diagnostic purposes.
* **NoOfPerformers**: Effectively, how many threads do we use to read messages from the queue. As Brighter uses a Single-Threaded Apartment model, each thread has it's own message pump and is thus an in-process implementation of the [Competing Consumers](https://www.enterpriseintegrationpatterns.com/CompetingConsumers.html) pattern.
* **RequeueCount**: How many times can you retry a message before we declare it a poison pill message?
* **RequeueDelayInMilliseconds**: When we requeue a message how long should we delay it by?
* **RoutingKey**: The identifier used to routed messages to subscribers on MoM. You publish to this, and subscriber from this. This has different names; in Kafka or SNS this is a Topic, in RMQ this is the routing key.
* **MessagePumpType**: Chooses the concurrency model: **MessagePumpType.Reactor** (blocking I/O, lower latency) or **MessagePumpType.Proactor** (non-blocking I/O, higher throughput). The Proactor pattern can increase throughput where a handler is I/O bound by allowing the message pump to yield the thread whilst awaiting I/O completion. The cost is that strict ordering of messages may be lost as processing of I/O bound requests may complete out-of-sequence. Brighter provides its own synchronization context for async operations. We recommend scaling via increasing the number of performers, unless you know that I/O is your bottleneck. See [Reactor and Proactor](https://brightercommand.gitbook.io/paramore-brighter-documentation/under-the-hood/reactorandproactor) for details.
* **TimeOut**: How long does a read 'wait' before assuming there are no pending messages.
* **UnaceptableMessageLimit**: Brighter will ack a message that throws an unhandled exception, thus removing it from a queue.

For a more detailed discussion of using Requeue (with Delay) for Handler failure, (**RequeueCount** and **RequeueDelayInMilliseconds**) along with termination of a consumer due to message failure (**UnacceptableMessageLimit**) see [Handler Failure](https://brightercommand.gitbook.io/paramore-brighter-documentation/using-an-external-bus/handlerfailure)

In addition, individual transports that provide access to specific MoM sub-class *Subscription* to provide properties unique to the chosen middleware. We discuss those under a section for that transport.

For RabbitMQ for example, this would look like this:

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
    var subscriptions = new Subscription[]
    {
        new RmqSubscription<GreetingMade>(
            new SubscriptionName("paramore.sample.salutationanalytics"),
            new ChannelName("SalutationAnalytics"),
            new RoutingKey("GreetingMade"),
            messagePumpType: MessagePumpType.Proactor,
            timeOut: TimeSpan.FromMilliseconds(200),
            isDurable: true,
            makeChannels: OnMissingChannel.Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere
    };

    services.AddConsumers(options =>
        {
            options.Subscriptions = subscriptions;
        })
}

...

```

#### **Gateway Connections & Channel Factories**

A *Gateway Connection* tells Brighter how to connect to MoM for a particular transport. The transport package will contain a *Gateway Connection*, you need to provide the information to connect to your middleware (URIs, ports, credentials etc.) Your transport package provides a *Gateway Connection*

A *Channel Factory* connects Brighter to MoM. Depending on the configuration settings for your *Subscription* it may create the required primitives (topics/routing keys, queues, streams) on MoM or simply attach to ones that you have created via Infrastructure as Code (IaC). Your transport provides a *Channel Factory* and you need to pass it a *Gateway Connection*.

For RabbitMQ, this would look like:

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{

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

    var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(rmqConnection);

    services.AddConsumers(options =>
        {
             options.DefaultChannelFactory = new ChannelFactory(rmqMessageConsumerFactory);
        })
}

...

```

#### **Configuring Dispatcher Lifetimes**

Under the hood your Dispatcher uses a *Command Processor* and you will need to configure lifetimes [as discussed above](#configuring-lifetimes).

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
    services.AddConsumers(options =>
        {
            options.HandlerLifetime = ServiceLifetime.Scoped;
            options.MapperLifetime = ServiceLifetime.Singleton;
        })
}

...

```

### **Dispatcher Brighter Builder Fluent Interface**

The call to **AddConsumers()** returns an **IBrighterBuilder** fluent interface. This means that you can use any of the options described in [Brighter Build Fluent Interfaces](#brighter-builder-fluent-interface) to configure the associated *Command Processor* such as scanning assemblies for *Request Handlers* and adding an *External Bus* and *Outbox*.

An option intended for the context of a Dispatcher (message consumer) is described below.

#### **Inbox**

As described in the [Outbox Pattern](https://brightercommand.gitbook.io/paramore-brighter-documentation/event-driven-architectures/outboxpattern) an *Outbox* offers **Guaranteed, At Least Once** delivery. It explicitly may result in you sending duplicate messages. In addition, MoM tends to offer "At Least Once" guarantees only, further creating the risk that you will receive a duplicate message.

If the request is not idempotent, you can use an Inbox to de-duplicate it. See [Inbox Support](https://brightercommand.gitbook.io/paramore-brighter-documentation/outbox-and-inbox/brighterinboxsupport) for more.

Configuring an *Inbox* has two elements. The first is the type of *Inbox*, the second configuration for the *Inbox* behavior.

Brighter provides a number of *Inbox* implementations for common Dbs (and you can write your own for a Db that we do not support). For this discussion we will look at Brighter's support for working with MySQL. See the documentation for working with specific *Inbox* implementations.

For this we will need the *Inbox* packages for the MySQL *Inbox*.

* **Paramore.Brighter.Inbox.MySql**

For a given backing store the pattern should be Paramore.Brighter.Inbox.{DATABASE} where {DATABASE} is the name of the Db that you are using.

To configure our *Inbox* we then need to use the UseExternalInbox method call and pass in an instance of a class that implements **IAmAnInbox**, taken from our package, and an instance of **InboxConfiguration** that tells Brighter how we want to use the Inbox.

For *Inbox Configuration* you set the following properties:

* **ActionOnExists**: What do we do if the request has been handled? The default,**OnceOnlyAction.Throw** is to throw a **OnceOnlyException**. If you take no other action this will cause the message to be rejected and sent to a DLQ if one is configured (See [Handler Failure](https://brightercommand.gitbook.io/paramore-brighter-documentation/using-an-external-bus/handlerfailure)). The alternative is **OnceOnlyAction.Warn** simply logs that the request is a duplicate, but takes no other action.
* **OnceOnly**: This defaults to *true* and will check for a duplicate and take the action indicated by **ActionOnExists**. If *false* the *Inbox* will record the request, but will take no further action. (This tends to be set to *false* if you are using the *Inbox* to record what requests caused current state only and not de-duplicate).
* **Scope**: This indicates the type of request (*Command* or *Event*) to store in the *Inbox*. By default this is set to **InboxScope.All** and captures everything but you can be explicit and just capture **InboxScope.Commands** or **InboxScope.Events**. (This tends to be set to **InboxScope.Commands** when only commands cause changes to state that are not idempotent).
* **Context**: Used to uniquely identify receipt of this request via this handler. If you are recording *Events* and have multiple handlers, then the first event handler to receive the message will block the others from doing so, unless you disambiguate the handler identity by supplying a context method.

A typical *Inbox* configuration for MySQL would be:

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
    services.AddConsumers(options =>
        {   
            options.InboxConfiguration =  new InboxConfiguration(
                    inbox: new MySqlInbox(new RelationalDatabaseConfiguration(DbConnectionString()))
                    scope: InboxScope.Commands,
                    onceOnly: true,
                    actionOnExists: OnceOnlyAction.Throw
            ...
        });
 }
 
 ...
```

Typically **DbConnectionString** would obtain the connection string for the Db from configuration.

### Running the Dispatcher

To run the Dispatcher we add it as a [Hosted Service](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0\&tabs=visual-studio).

We provide the class **ServiceActivatorHostedService** for this in the NuGet package:

* **Paramore.Brighter.ServiceActivator.Extensions.Hosting**

The **ServiceActivatorHostedService** calls the **Dispatcher.Receive** method which starts message pumps for the configured *Subscriptions*.

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
    ...
    services.AddHostedService<ServiceActivatorHostedService>();
}

```

On shutdown Brighter will allow the current *Request Handler* to complete, then end the message pump loop and exit. If you have long-running handlers it is possible that they will not complete in the default 5s for graceful shutdown of the MS Generic Host. In this case, you need to [increase the timeout](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-6.0#shutdowntimeout) of the host shutdown.

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            services.Configure<HostOptions>(options =>
            {
                options.ShutdownTimeout = TimeSpan.FromSeconds(20);
            });
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
    ...

    services.AddHostedService<ServiceActivatorHostedService>();
}

```

### A Complete Dispatcher Example

When all of the relevant configuration sections are added together, your code will look something like this, with variations for your transport and stores.

```csharp
private static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices(hostContext, services) =>
        {
            services.Configure<HostOptions>(options =>
            {
                options.ShutdownTimeout = TimeSpan.FromSeconds(20);
            });
            ConfigureBrighter(hostContext, services);
        }

private static void ConfigureBrighter(HostBuilderContext hostContext, IServiceCollection services)
{
   var subscriptions = new Subscription[]
    {
        new RmqSubscription<GreetingMade>(
            new SubscriptionName("paramore.sample.salutationanalytics"),
            new ChannelName("SalutationAnalytics"),
            new RoutingKey("GreetingMade"),
            messagePumpType: MessagePumpType.Proactor,
            timeOut: TimeSpan.FromMilliseconds(200),
            isDurable: true,
            makeChannels: OnMissingChannel.Create), //change to OnMissingChannel.Validate if you have infrastructure declared elsewhere
    };

    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);

    services.AddConsumers(options =>
    {
        options.Subscriptions = subscriptions;
        options.DefaultChannelFactory = new ChannelFactory(rmqMessageConsumerFactory);
        options.UseScoped = true;
        options.HandlerLifetime = ServiceLifetime.Scoped;
        options.MapperLifetime = ServiceLifetime.Singleton;
        options.CommandProcessorLifetime = ServiceLifetime.Scoped;
        options.PolicyRegistry = new SalutationPolicy();
        options.InboxConfiguration =  new InboxConfiguration(
            inbox: new MySqlInbox(new RelationalDatabaseConfiguration(DbConnectionString()))
            scope: InboxScope.Commands,
            onceOnly: true,
            actionOnExists: OnceOnlyAction.Throw
        );
    })
    .AddProducers((configure) =>
    {
        configure.ProducerRegistry = producerRegistry;
        configure.Outbox = outbox;
        configure.TransactionProvider = transactionProvider;
        configure.ConnectionProvider = connectionProvider;
    })
    .AutoFromAssemblies();
    
    services.AddHostedService<ServiceActivatorHostedService>();

}

```

## Samples

Brighter includes a comprehensive set of [Samples](https://github.com/BrighterCommand/Brighter/tree/master/samples) in its main repo that you can review for clarity on how Brighter works and should be configured.

## **V10 Configuration Changes**

**V10 introduces simplified configuration methods with clearer naming:**

| V9 Method               | V10 Method       | Purpose                                                    |
| ----------------------- | ---------------- | ---------------------------------------------------------- |
| `UseExternalBus()`      | `AddProducers()` | Configure message producers (send/publish to external bus) |
| `AddServiceActivator()` | `AddConsumers()` | Configure message consumers (receive from external bus)    |

**Terminology**: In V10, we use **"Dispatcher"** to refer to the component that dispatches messages to handlers. The assembly name remains `Paramore.Brighter.ServiceActivator` for backward compatibility, but documentation and APIs now use "Dispatcher" for clarity.

### Quick Migration Guide

**V9**:

```csharp
services.AddBrighter()
    .UseExternalBus(...)
    .AddServiceActivator(...);
```

**V10**:

```csharp
services.AddBrighter()
    .AddProducers(...)
    .AddConsumers(...);
```

***
