Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion contents/BrighterBasicConfiguration.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ using Paramore.Brighter;
using Paramore.Brighter.Extensions.DependencyInjection;
using Paramore.Brighter.MessagingGateway.RMQ.Async;
using Paramore.Brighter.MySql;
using Paramore.Brighter.MySql.EntityFrameworkCore;
using Paramore.Brighter.Outbox.Hosting;
using Paramore.Brighter.Outbox.MySql;

Expand Down Expand Up @@ -100,7 +101,7 @@ public void ConfigureServices(IServiceCollection services)
configure.MaxOutStandingMessages = 5;
configure.MaxOutStandingCheckInterval = TimeSpan.FromMilliseconds(500);
configure.Outbox = new MySqlOutbox(outboxConfiguration);
configure.TransactionProvider = typeof(MySqlEntityFrameworkConnectionProvider<GreetingsEntityGateway>);
configure.TransactionProvider = typeof(MySqlEntityFrameworkTransactionProvider<GreetingsEntityGateway>);
configure.ConnectionProvider = typeof(MySqlConnectionProvider);
})
.UseOutboxSweeper()
Expand Down
14 changes: 8 additions & 6 deletions contents/CQRSWithBrighterAndDarker.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,9 @@ Configure both Brighter and Darker in your `Program.cs` or `Startup.cs`:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Paramore.Brighter;
using Paramore.Brighter.Extensions;
using Paramore.Brighter.Extensions.DependencyInjection;
using Polly.Registry;
using Paramore.Darker;
using Paramore.Darker.AspNetCore;
using Paramore.Darker.Policies;
Expand All @@ -370,15 +372,15 @@ builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// Configure Brighter (Command Side)
var resiliencePipelineRegistry = new ResiliencePipelineRegistry<string>()
.AddBrighterDefault();
// Configure retry and circuit breaker pipelines for commands with TryAddBuilder here

builder.Services.AddBrighter(options =>
{
// Configure Brighter options
options.ResiliencePipelineRegistry = resiliencePipelineRegistry;
})
.AutoFromAssemblies(typeof(PlaceOrderCommandHandler).Assembly)
.ConfigureResiliencePipelines(registry =>
{
// Configure retry and circuit breaker policies for commands
});
.AutoFromAssemblies(typeof(PlaceOrderCommandHandler).Assembly);

// Configure Darker (Query Side)
builder.Services.AddDarker(options =>
Expand Down
32 changes: 26 additions & 6 deletions contents/CommandProcessorConfigurationReference.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,40 @@ See the section [Policy Retry and Circuit Breaker](/contents/PolicyRetryAndCircu
With the resilience pipeline registry configured, you need to tell Brighter where to find it:

``` csharp
using Microsoft.Extensions.DependencyInjection;
using Paramore.Brighter;
using Paramore.Brighter.Extensions;
using Paramore.Brighter.Extensions.DependencyInjection;
using Polly;
using Polly.Registry;
using Polly.Retry;

// ...
public void ConfigureServices(IServiceCollection services)
{
var resiliencePipelineRegistry = new ResiliencePipelineRegistry<string>()
.AddBrighterDefault();

resiliencePipelineRegistry.TryAddBuilder("RetryPipeline",
(builder, _) => builder.AddRetry(new RetryStrategyOptions()));

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

**`ResiliencePipelineRegistry` is a property on `BrighterOptions`, not a fluent builder call.**
You set it inside the `AddBrighter` options delegate, as above. Brighter supplies its own
registry only when you leave the property unset — it does so with `??=` — so a registry you
build yourself needs `AddBrighterDefault()`, which backfills the pipelines Brighter requires
without touching yours. Without it, startup fails with a `ConfigurationException` naming the
missing `CommandProcessor.OutboxProducer` pipeline.

`BrighterOptions.PolicyRegistry` still exists for Polly v7 `[UsePolicy]` handlers, but it is
marked obsolete in V10 and compiling against it raises `CS0618`.

> **Note**: For legacy Polly v7 policies using `[UsePolicy]`, see the [migration guide](/contents/MigratingToPollyV8.md#polly-v8-migration-guide-v9-to-v10) for updating to V10 resilience pipelines.

### Configuring Lifetimes
Expand Down Expand Up @@ -610,7 +630,7 @@ public void ConfigureServices(IServiceCollection services)
.AddProducers((configure) =>
{
configure.Outbox = new MySqlOutbox(outboxConfiguration);
configure.TransactionProvider = typeof(MySqlEntityFrameworkConnectionProvider<GreetingsEntityGateway>);
configure.TransactionProvider = typeof(MySqlEntityFrameworkTransactionProvider<GreetingsEntityGateway>);
configure.ConnectionProvider = typeof(MySqlConnectionProvider);
})
.AutoFromAssemblies();
Expand Down
21 changes: 19 additions & 2 deletions contents/HowConfiguringTheCommandProcessorWorks.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,27 @@ You need to provide a factory to give us instances of a [Context](/contents/Usin
All these individual elements can be passed to a **Command Processor Builder** to help build a **Command Processor**. This has a fluent interface to help guide you when configuring Brighter. The result looks like this:

``` csharp
var commandProcessor = CommandProcessorBuilder.With()
using Paramore.Brighter;
using Paramore.Brighter.Extensions;
using Polly.Registry;

var commandProcessor = CommandProcessorBuilder.StartNew()
.Handlers(new HandlerConfiguration(subscriberRegistry, handlerFactory))
.Policies(policyRegistry)
.Resilience(resiliencePipelineRegistry.AddBrighterDefault())
.NoExternalBus()
.NoInstrumentation()
.RequestContextFactory(new InMemoryRequestContextFactory())
.RequestSchedulerFactory(new InMemorySchedulerFactory())
.Build();
```

**Each call returns the interface the next one is declared on**, which is what makes the chain
guide you: `StartNew()` gives you something that only offers `Handlers`, and `Handlers` gives
you something that only offers `Resilience` or `DefaultResilience`. You cannot skip a step,
and the compiler — not the documentation — is what tells you so.

`Resilience` takes a second, optional Polly v7 `IPolicyRegistry<string>` if you still have
`[UsePolicy]` handlers to carry; see the deprecated Policy Registry section below.
`AddBrighterDefault()` backfills the pipelines Brighter itself needs — without it, a registry
you built yourself makes `Resilience` throw `ConfigurationException` at startup. If you have
no pipelines of your own, `DefaultResilience()` replaces the whole call.
63 changes: 48 additions & 15 deletions contents/HowConfiguringTheDispatcherWorks.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,34 +45,67 @@ var messageMapperRegistry = new MessageMapperRegistry(messageMapperFactory)

### Channel Factory

The Channel Factory is where we take a dependency on a specific Broker. We pass the **Dispatcher** an instances of **InputChannelFactory** which in turn has a dependency on implementation of **IAmAChannelFactory**. The channel factory is used to create channels that wrap the underlying Message-Oriented Middleware that you are using.
The Channel Factory is where we take a dependency on a specific Broker. We pass the **Dispatcher** an instance of `ChannelFactory`, which in turn has a dependency on an implementation of `IAmAChannelFactory`. The channel factory is used to create channels that wrap the underlying Message-Oriented Middleware that you are using.

### Creating a Builder

This code fragment shows putting the whole thing together

``` csharp
using System;
using Paramore.Brighter;
using Paramore.Brighter.MessagingGateway.RMQ.Sync;
using Paramore.Brighter.Observability;
using Paramore.Brighter.ServiceActivator;

// create message mappers
var messageMapperRegistry = new MessageMapperRegistry(messageMapperFactory)
var messageMapperRegistry = new MessageMapperRegistry(messageMapperFactory, null);
messageMapperRegistry.Register<GreetingCommand, GreetingCommandMessageMapper>();

// create the gateway
var rmqConnection = new RmqMessagingGatewayConnection
{
{ typeof(GreetingCommand), typeof(GreetingCommandMessageMapper) }
AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672/%2f")),
Exchange = new Exchange("paramore.brighter.exchange")
};
var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(rmqConnection);

// create the gateway
var rmqMessageConsumerFactory = new RmqMessageConsumerFactory(logger);
_dispatcher = DispatchBuilder.With()
.CommandProcessor(CommandProcessorBuilder.With()
.Handlers(new HandlerConfiguration(subscriberRegistry, handlerFactory))
.Policies(policyRegistry)
.NoExternalBus()
.RequestContextFactory(new InMemoryRequestContextFactory())
.Build())
.MessageMappers(messageMapperRegistry)
.ChannelFactory(new InputChannelFactory(rmqMessageConsumerFactory))
.Subscribers(subscriptions)
var tracer = new BrighterTracer(TimeProvider.System);

var commandProcessor = CommandProcessorBuilder.StartNew()
.Handlers(new HandlerConfiguration(subscriberRegistry, handlerFactory))
.DefaultResilience()
.NoExternalBus()
.ConfigureInstrumentation(tracer, InstrumentationOptions.All)
.RequestContextFactory(new InMemoryRequestContextFactory())
.RequestSchedulerFactory(new InMemorySchedulerFactory())
.Build();

_dispatcher = DispatchBuilder.StartNew()
.CommandProcessor(commandProcessor, new InMemoryRequestContextFactory())
// four registries: sync mappers, async mappers, transforms, async transforms
.MessageMappers(messageMapperRegistry, null, null, null)
.ChannelFactory(new ChannelFactory(rmqMessageConsumerFactory))
.Subscriptions(
[
new RmqSubscription<GreetingCommand>(
new SubscriptionName("GreetingCommand"),
new ChannelName("greeting.command"),
new RoutingKey("greeting.command"),
messagePumpType: MessagePumpType.Reactor,
timeOut: TimeSpan.FromMilliseconds(200))
])
.ConfigureInstrumentation(tracer, InstrumentationOptions.All)
.Build();
```

**Two details in that block will bite you if you change them.** The subscription is typed
`RmqSubscription<T>` rather than `Subscription<T>`, because a transport's channel factory casts
to its own subscription type and throws `ConfigurationException` when the cast fails — code that
compiles perfectly and dies at `Receive()`. And `messagePumpType` is set explicitly to
`Reactor`: `Subscription<T>` defaults to `Proactor`, which needs the *async* mapper registry,
and the third and fourth arguments to `MessageMappers` here are `null`.

## Validating Consumer Configuration

When you enable pipeline validation with `.ValidatePipelines()` on the **IBrighterBuilder**, consumer-specific checks run automatically when `AddConsumers()` is used. These checks catch common mistakes before the dispatcher starts receiving messages:
Expand Down
4 changes: 2 additions & 2 deletions contents/MSSQLOutbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ services.AddSingleton<IAmARelationalDatabaseConfiguration>(dbConfig);
Next, in your `ConfigureServices` method or `Program.cs`, add the outbox configuration when calling `AddBrighter`. You need to specify the `Outbox`, `ConnectionProvider`, and `TransactionProvider`.

The `TransactionProvider` depends on how you manage your database transactions.
- Use `MsSqlUnitOfWork` for ADO.NET-based transaction management.
- Use `MsSqlEntityFrameworkConnectionProvider<T>` if you are using Entity Framework Core, where `T` is your `DbContext`.
- Use `MsSqlTransactionProvider` for ADO.NET-based transaction management.
- Use `MsSqlEntityFrameworkCoreTransactionProvider<T>` if you are using Entity Framework Core, where `T` is your `DbContext`.

### **Example with Entity Framework Core**

Expand Down
36 changes: 29 additions & 7 deletions contents/MigratingToPollyV8.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ internal class MyHandler : RequestHandler<MyCommand>

### Step 4: Update CommandProcessor Configuration

**V9**:
**V9 — superseded**

```csharp
// ...
Expand All @@ -102,18 +102,40 @@ var commandProcessor = CommandProcessorBuilder.With()
.Build();
```

**V10**:
**V10 — current**

```csharp
using Paramore.Brighter;
using Paramore.Brighter.Extensions;
using Polly.Registry;

// ...
var commandProcessor = CommandProcessorBuilder.With()
.Handlers(/* ... */)
.Policies(policyRegistry) // Optional: Keep for legacy v7 policies during migration
.ResiliencePipelines(resiliencePipelineRegistry) // New: Polly v8 pipelines
var commandProcessor = CommandProcessorBuilder.StartNew()
.Handlers(handlerConfiguration)
// One call, not two: the Polly v7 registry is Resilience's optional second argument
.Resilience(resiliencePipelineRegistry, policyRegistry)
.NoExternalBus()
.NoInstrumentation()
.RequestContextFactory(new InMemoryRequestContextFactory())
.RequestSchedulerFactory(new InMemorySchedulerFactory())
.Build();
```

> **Note**: You can use both `Policies()` and `ResiliencePipelines()` during migration to support both legacy `UsePolicy` and new `UseResiliencePipeline` attributes.
**Three names change in that chain, and one of them is the entry point.** `With()` becomes
`StartNew()`, and `Policies()` — which took a Polly v7 `IPolicyRegistry<string>` on its own —
is now the **optional second argument** to `Resilience()`. There is no separate call to make:
pass both registries to `Resilience` during migration, and drop the second argument once no
`[UsePolicy]` attributes remain. If you have nothing to carry over, `DefaultResilience()`
replaces the pair.

> **Build the registry with `AddBrighterDefault()`.** Brighter requires a pipeline registered
> under `CommandProcessor.OutboxProducer`, and `Resilience` throws `ConfigurationException` at
> startup when the registry you hand it does not have one. `AddBrighterDefault` uses
> `TryAddBuilder`, so it adds what is missing and leaves your own pipelines alone.

The V10 chain is also longer than V9's. `NoExternalBus()`, `NoInstrumentation()` and
`RequestSchedulerFactory()` are steps the fluent interface now requires — the compiler will
tell you, because each returns the interface the next call is declared on.

---

Expand Down
4 changes: 2 additions & 2 deletions contents/MySQLOutbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ services.AddSingleton<IAmARelationalDatabaseConfiguration>(dbConfig);
Next, in your `ConfigureServices` method or `Program.cs`, add the outbox configuration when calling `AddBrighter`. You need to specify the `Outbox`, `ConnectionProvider`, and `TransactionProvider`.

The `TransactionProvider` depends on how you manage your database transactions.
- Use `MySqlUnitOfWork` for ADO.NET-based transaction management.
- Use `MySqlEntityFrameworkConnectionProvider<T>` if you are using Entity Framework Core, where `T` is your `DbContext`.
- Use `MySqlTransactionProvider` for ADO.NET-based transaction management.
- Use `MySqlEntityFrameworkTransactionProvider<T>` if you are using Entity Framework Core, where `T` is your `DbContext`.

### **Example with Entity Framework Core**

Expand Down
Loading
Loading