DEV Community

Cover image for Migration Guide: Upgrading from Brighter V9 to V10
Rafael Andrade
Rafael Andrade

Posted on

Migration Guide: Upgrading from Brighter V9 to V10

In my previous article, I discussed Brighter V10 and its new features. This follow-up focuses on the essential migration path from V9 to V10, highlighting breaking changes and providing updated code examples.

Dependency Injection Registration

One of the first and most significant changes you'll encounter is how Brighter is registered with the Microsoft DI container. The AddServiceActivator method is gone, replaced by more specific methods for consumers and producers.

Messaging Consumer and Producer Setup

In V9, you typically used AddServiceActivator for consumers. In V10, this registration is now explicitly split into AddConsumers and AddProducers, giving you more granular control.

Notice also that opt.ChannelFactory has been renamed to opt.DefaultChannelFactory.

V9:

services
    .AddServiceActivator(opt => 
    {
        opt.Subscriptions = [...];
        opt.ChannelFactory = new ChannelFactory(...);
    })
    .UseExternalBus(...)
Enter fullscreen mode Exit fullscreen mode

V10:

services
    .AddConsumers(opt => 
    {
        opt.Subscriptions = [...];
        opt.DefaultChannelFactory = new ChannelFactory(...);
    })
    .AddProducers(opt =>
    {
        opt.ProducerRegistry = ....
    });
Enter fullscreen mode Exit fullscreen mode

Inbox and Outbox Configuration

Previously, you configured the Inbox and Outbox by chaining methods like UseInMemoryInbox() after AddServiceActivator. In V10, this configuration is moved directly into the AddConsumers (for the Inbox) and AddProducers (for the Outbox) options.

When configuring an Outbox in V10, you must also now explicitly configure the ConnectionProvider and TransactionProvider.

V9:

services
    .AddServiceActivator(opt => 
    {
        opt.Subscriptions = [...];
        opt.ChannelFactory = new ChannelFactory(...);
    })
    .UseInMemoryInbox()
    .UseInMemoryOutbox()
    .UseExternalBus(...)
Enter fullscreen mode Exit fullscreen mode

V10:

services
    .AddConsumers(opt => 
    {
        opt.Subscriptions = [...];
        opt.InboxConfiguration = new InboxConfiguration(...);
        opt.DefaultChannelFactory = new ChannelFactory(...);
    })
    .AddProducers(opt =>
    {
        opt.ProducerRegistry = ....;
        opt.Outbox = ...;
        opt.ConnectionProvider = typeof(...);
        opt.TransactionProvider = typeof(...);
    });
Enter fullscreen mode Exit fullscreen mode

Component Lifetime

With V10, it is strongly recommended to register your Message Mappers, Transformers, and Request Handlers as Scoped or Transient.

This is because these components now often include a RequestContext property. This property is updated by the framework at runtime (before methods are called), and using a Singleton lifetime could lead to concurrency issues and stale data.

Message Mapper Interface

The IAmAMessageMapper interface has two key breaking changes.

  1. MapToMessage Signature: The method now includes a Publication parameter: MapToMessage(Greeting request, Publication publication). This is required to support new default mapper functionality.
  2. RequestContext Property: The interface now requires a public IRequestContext? Context { get; set; } property. This allows the framework or the requester to pass additional context and data at runtime.

V9

public class GreetingMapper : IAmAMessageMapper<Greeting>
{
    public Message MapToMessage(Greeting request)
    {
        var header = new MessageHeader();
        header.Id = request.Id;
        header.TimeStamp = DateTime.UtcNow;
        header.Topic = "greeting.topic";
        header.MessageType = MessageType.MT_EVENT;

        var body = new MessageBody(JsonSerializer.Serialize(request));
        return new Message(header, body);
    }

    public Greeting MapToRequest(Message message)
    {
        return JsonSerializer.Deserialize<Greeting>(message.Body.Bytes)!;
    }
}
Enter fullscreen mode Exit fullscreen mode

V10

public class GreetingMapper : IAmAMessageMapper<Greeting>
{
    public IRequestContext? Context { get; set; }

    public Message MapToMessage(Greeting request, Publication publication)
    {
        var header = new MessageHeader();
        header.Id = request.Id;
        header.TimeStamp = DateTime.UtcNow;
        header.Topic = "greeting.topic";
        header.MessageType = MessageType.MT_EVENT;

        var body = new MessageBody(JsonSerializer.Serialize(request));
        return new Message(header, body);
    }

    public Greeting MapToRequest(Message message)
    {
        return JsonSerializer.Deserialize<Greeting>(message.Body.Bytes)!;
    }
}
Enter fullscreen mode Exit fullscreen mode

Async Mappers

A subtle but important related change: If you are using RequestHandlerAsync (for asynchronous handlers), you must now implement the corresponding IAmAMessageMapperAsync interface instead of the synchronous IAmAMessageMapper.

Outbox Breaking Changes

If you are using the Outbox from Brighter V10, especially with a relational database (like PostgreSQL, MySQL, or Microsoft SQL Server), there are several breaking schema changes.

You must add the following columns to your Inbox/Outbox tables to support enhanced context and tracing:

  • Source: VARCHAR(255)
  • DataSchema: VARCHAR(255)
  • Subject: VARCHAR(255)
  • TraceParent: VARCHAR(255)
  • TraceState: VARCHAR(255)
  • Baggage: TEXT (or VARCHAR(MAX) in SQL Server)

MessageId Column Changes

The MessageId column type has been updated in several implementations.

  • PostgreSQL: The MessageId type must be changed from UUID to VARCHAR(255).

Optional (Recommended) Changes: For better performance and native type usage, you can make the following platform-specific changes:

  • MySQL: Change MessageId from CHAR(36) to VARCHAR(255).
  • Microsoft SQL Server: Change MessageId from UNIQUEIDENTIFIER to VARCHAR(255).

Migration Strategy and Best Practices

When migrating from V9 to V10, consider this approach:

  1. Start with Registration: Update your DI configuration first, as this affects everything else
  2. Configure Inbox/Outbox: Move your inbox/outbox configuration to the appropriate registration blocks
  3. Update Mappers: Refactor all message mappers to implement the new interface requirements
  4. Review Lifetimes: Audit all Brighter-related components and ensure they're registered as Scoped or Transient
  5. Test Thoroughly: Pay special attention to transaction management and async operations

Conclusion

Migrating to Brighter V10 involves several key changes, primarily focused on clearer DI registration, explicit Inbox/Outbox setup, and enhanced message mapping with request context. By updating your service registration and mapper implementations as shown above, you can smoothly transition your application to the new version.

Top comments (0)