For the complete documentation index, see llms.txt. This page is also available as Markdown.

V10 Migration Guide

How-to · Applies to Brighter V10

V10 Migration Overview

Brighter V10 introduces significant improvements and new features while maintaining a clear migration path from V9. This guide provides step-by-step instructions for upgrading your application to V10, addressing breaking changes, and adopting new features.

Key Changes in V10:

  • Cloud Events support

  • OpenTelemetry Semantic Conventions

  • Default Message Mappers

  • Dynamic Message Deserialization

  • Nullable Reference Types (breaking)

  • Simplified Configuration (breaking)

  • Reactor and Proactor terminology (breaking)

  • Polly Resilience Pipeline v8 (breaking)

  • Request Context enhancements

  • InMemory options for testing

  • Transport improvements (PostgreSQL, RabbitMQ, Kafka, AWS)

  • Request ID is now an Id type

Migration Effort: Most applications can be migrated in 1-4 hours, depending on complexity.

Before You Start

Prerequisites

  1. Backup your code: Commit all changes to version control

  2. Review the release notes: Read Release Notes for V10

  3. Update test suite: Ensure your tests are passing on V9

  4. Check dependencies: Review third-party package compatibility

  1. Upgrade in a feature branch: Don't upgrade directly in main/master

  2. Address breaking changes first: Fix compilation errors before adopting new features

  3. Test thoroughly: Run your full test suite after each step

  4. Deploy to staging: Validate in a non-production environment

  5. Monitor production: Watch for issues after deployment

Step 1: Update Package References

Update NuGet Packages

Update all Brighter packages to V10:

Check for Package Conflicts

Step 2: Address Breaking Changes

1. Nullable Reference Types

Breaking Change: Nullable reference types are now enabled across all Brighter projects.

Migration Steps:

  1. Enable nullable reference types in your project (if not already enabled):

  1. Address compiler warnings in Commands, Events, and Handlers:

Before (V9):

After (V10):

  1. Update Message Mappers to handle nullable warnings:

See also: Nullable Reference Types Documentation

2. Simplified Configuration

Breaking Change: Builder methods renamed for clarity.

Before (V9):

After (V10):

AddConsumers extends IServiceCollection and AddProducers extends the Brighter builder it returns, so the consumer registration comes first and the producer registration chains off it. AddBrighter is the entry point when there are no consumers.

Migration Steps:

  1. Replace UseExternalBus with AddProducers

  2. Replace AddServiceActivator with AddConsumers, and put it first in the chain

  3. Update property names: ProducerRegistry instead of passing directly, DefaultChannelFactory instead of ChannelFactory, and InboxConfiguration instead of Inbox

V9 Method
V10 Method
Purpose

UseExternalBus()

AddProducers()

Configure message producers (send/publish to external bus)

AddServiceActivator()

AddConsumers()

Configure message consumers (receive from external bus)

options.ChannelFactory

options.DefaultChannelFactory

Supply the channel factory for every subscription that does not name its own

options.Inbox

options.InboxConfiguration

Configure the Inbox used by the consumer pipeline

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.

3. Reactor and Proactor Terminology

Breaking Change: The runAsync flag on Subscription has been renamed to MessagePumpType.

Before (V9):

After (V10):

Migration Table:

V9 Parameter
V10 Parameter
Description

runAsync: false

messagePumpType: MessagePumpType.Reactor

Synchronous, blocking I/O

runAsync: true

messagePumpType: MessagePumpType.Proactor

Asynchronous, non-blocking I/O

See also: Reactor and Proactor Documentation

4. Polly Resilience Pipeline

Breaking Change: TimeoutPolicyAttribute is obsolete. Use UseResiliencePipeline on a synchronous handler and UseResiliencePipelineAsync on an async one.

Before (V9):

After (V10):

  1. Define a Resilience Pipeline:

AddBrighterDefault adds the pipelines Brighter itself requires. Assigning your own registry means Brighter does not add them for you, and without them the command processor throws ConfigurationException, so start from it.

  1. Use the new attribute:

  1. Register the pipeline with Brighter:

PolicyRegistry is the obsolete Polly v7 IPolicyRegistry<string>, not this registry.

See also: Policy Retry and Circuit Breaker Documentation

5. Request Context Interface Changes

Breaking Change: IRequestContext has changed shape.

Changed Properties:

  • Bag is a ConcurrentDictionary<string, object>; in V9 it was a Dictionary<string, object>

  • Policies, the Polly v7 registry, is obsolete; use ResiliencePipeline

  • Policies and FeatureSwitches are nullable

New Members:

Migration: Most code should not be affected unless you implement IRequestContext directly.

If you implement IRequestContext (rare):

Partition keys and custom headers are not properties. IRequestContext has no PartitionKey and no CustomHeaders. Put them in the Bag under the well-known keys RequestContextBagNames.PartitionKey and RequestContextBagNames.Headers, and pass the context with the request. Brighter's default message mappers read both:

A custom mapper reads them only if it asks: Context.GetPartitionKey() and Context.GetHeaders(), in Paramore.Brighter.Extensions. See Partition Key and Custom Headers.

InstrumentationOptions (after 10.7.0)

Not in a released package yet. IRequestContext.InstrumentationOptions ships after Brighter 10.7.0, which is the current release. It is on Brighter's development branch and in no version you can install today, so an implementation built against 10.7.0 compiles without it; treat what follows as the change as it will ship.

Breaking Change: IRequestContext gains a required InstrumentationOptions member.

It carries the instrumentation options configured for the pipeline that created the context, so middleware handlers can gate their own telemetry — on InstrumentationOptions.Brighter, for example — without needing to know how the Command Processor was configured.

This is source-breaking for direct implementors of IRequestContext only. Paramore.Brighter targets netstandard2.0, which has no default interface members, so the member is plain and abstract: any third-party or test type implementing the interface fails to compile until it adds one line.

The shipped RequestContext implements it on the development branch, defaulting to InstrumentationOptions.All, so code that uses the shipped context needs no change.

6. Request Id and CorrelationId type change

In V9 the Request type used a Guid to represent the identity of a Command or Event. In V10, as part of a move away from primitives, we have changed this to be a type Id. An Id can be constructed from a string using its ToString() method. If you have used a Guid then you will need to turn the Guid into a string.

Within IRequest both Id and CorrelationId both use the type Id in V10.

If you were using a Guid to create a random identity, you can just use Id.Random() instead which has the same behavior.

Before:

After:

Step 3: Adopt New Features (Optional)

1. Cloud Events Support

V10 adds full Cloud Events specification support.

Benefits:

  • Standardized event metadata

  • Better interoperability with other systems

  • Rich event context information

Adoption Steps:

  1. Update Publication to include Cloud Events properties:

  1. Use Cloud Events headers in your mapper (optional):

See also: Cloud Events Documentation

2. Default Message Mappers

V10 allows you to omit message mappers for simple JSON serialization.

Benefits:

  • Less boilerplate code

  • Faster development

  • Still supports custom mappers for complex scenarios

Before (V9) - Required Mapper:

After (V10) - No Mapper Needed:

When to use custom mappers:

  • Complex transformations

  • Non-JSON formats (XML, Protobuf, etc.)

  • Transform pipelines (encryption, compression, claim check)

  • Custom header mapping

See also: Message Mappers Documentation

3. Dynamic Message Deserialization

V10 supports multiple message types on the same channel.

Benefits:

  • Content-based routing

  • Flexible message processing

  • Better use of infrastructure

Example:

See also: Dynamic Deserialization Documentation

4. OpenTelemetry Semantic Conventions

V10 adopts OpenTelemetry Semantic Conventions for messaging.

Impact: Trace spans will have different names and attributes than V9.

Benefits:

  • Standard messaging conventions

  • Better integration with APM tools

  • Consistent telemetry across systems

Migration:

  1. Update dashboards and alerts to use new span names

  2. Review trace queries for compatibility

  3. Test observability in staging environment

V9 Span Names:

  • Paramore.Brighter.CommandProcessor.Send

  • Paramore.Brighter.MessagePump.Receive

V10 Span Names (OTel Semantic Conventions):

  • messaging.send

  • messaging.receive

  • messaging.process

See also: Telemetry Documentation

5. InMemory Options for Testing

V10 provides comprehensive InMemory implementations for testing.

Benefits:

  • Fast test execution

  • No external dependencies

  • Easy CI/CD integration

Example:

See also: InMemory Options Documentation

6. Kafka KIP-848 Consumer Group Protocol

V10 adds support for KIP-848, Kafka's next-generation consumer group protocol with broker-driven partition assignment (requires Apache Kafka 4.0 or later).

Not a breaking change: Existing subscriptions continue to use the classic consumer group protocol by default.

Obsoleted properties: KafkaSubscription.SessionTimeout and KafkaSubscription.PartitionAssignmentStrategy are now marked [Obsolete] and will be removed in a future version. They still work (their values are used as back-fill defaults), but new code should set these values on a ClassicGroupProtocol:

Before:

After:

Opting into KIP-848:

See also: Kafka Configuration — Consumer Group Protocol (KIP-848)

Step 4: Test Your Migration

Unit Tests

  1. Run existing unit tests:

  1. Address test failures:

    • Update mocks for IRequestContext new properties

    • Update assertions for OpenTelemetry span names

    • Fix nullable reference warnings

Integration Tests

  1. Test with InMemory components (fast):

  1. Test with real transports (slower, more complete):

Performance Testing

Compare V9 and V10 performance:

Expected: V10 should have similar or better performance due to optimizations.

Step 5: Deploy to Staging

Pre-Deployment Checklist

Deployment Steps

  1. Deploy to staging environment

  2. Run smoke tests

  3. Monitor metrics:

    • Message processing latency

    • Error rates

    • Resource usage (CPU, memory)

  4. Check logs for warnings or errors

  5. Validate telemetry (traces, metrics)

Monitoring

Watch for:

  • Increased error rates

  • Performance degradation

  • OpenTelemetry trace issues

  • Null reference exceptions

Step 6: Deploy to Production

Production Deployment

  1. Deploy during low-traffic period

  2. Use blue-green or canary deployment if possible

  3. Monitor closely for first 24 hours

  4. Have rollback plan ready

Post-Deployment

  • Review logs for issues

  • Check application metrics

  • Validate message processing

  • Monitor OpenTelemetry traces

Common Migration Issues

Issue 1: Nullable Reference Warnings

Symptom: CS8618, CS8600, CS8602 warnings

Solution: See Nullable Reference Types Documentation

Issue 2: Method Not Found

Symptom: UseExternalBus method not found

Solution: Replace with AddProducers

Issue 3: Property Not Found on Subscription

Symptom: runAsync property does not exist

Solution: Use messagePumpType: MessagePumpType.Proactor or MessagePumpType.Reactor

Issue 4: TimeoutPolicy Not Working

Symptom: TimeoutPolicyAttribute marked as obsolete

Solution: Migrate to UseResiliencePipeline with Polly v8

Issue 5: Telemetry Spans Changed

Symptom: Dashboard queries not finding spans

Solution: Update queries to use OpenTelemetry Semantic Convention names

Rollback Plan

If you need to roll back to V9:

  1. Revert package versions:

  1. Revert code changes:

  1. Redeploy V9 version

  2. Investigate issues before attempting V10 migration again

Getting Help

Resources

Reporting Issues

If you encounter issues during migration:

  1. Check existing issues: Search GitHub Issues

  2. Create a new issue with:

    • V9 and V10 versions

    • Minimal reproduction example

    • Error messages and stack traces

    • Environment details (.NET version, OS, transport)

V10 Migration Summary

Migrating to Brighter V10 involves:

  1. Update packages to V10

  2. Address breaking changes:

    • Nullable reference types

    • Simplified configuration API

    • Reactor/Proactor terminology

    • Polly Resilience Pipeline

  3. Adopt new features (optional):

    • Cloud Events

    • Default message mappers

    • Dynamic deserialization

    • OpenTelemetry conventions

    • InMemory testing options

  4. Test thoroughly

  5. Deploy to staging, then production

  6. Monitor and address any issues

Most migrations can be completed in 1-4 hours. The breaking changes are straightforward, and V10 provides significant improvements in features, performance, and developer experience.

Good luck with your migration! 🚀

Last updated

Was this helpful?