DEV Community

Cover image for Brighter: Migrando Postgres outbox para a V10
Rafael Andrade
Rafael Andrade

Posted on

Brighter: Migrando Postgres outbox para a V10

Uma das mudanças mais significativas na versão 10 do Brighter envolve uma reformulação completa da implementação do padrão "outbox". Neste artigo, explicarei as principais diferenças entre as versões 9 e 10, por que essas mudanças foram necessárias e fornecerei um guia claro sobre como configurar o novo padrão "outbox" com PostgreSQL.

O que é o padrão outbox?

O padrão "outbox" é uma técnica crítica em sistemas distribuídos que garante a entrega confiável de mensagens enquanto mantém a consistência dos dados. Em vez de publicar mensagens diretamente em um sistema de mensageria, os aplicativos armazenam as mensagens em uma tabela de banco de dados (a "outbox"). Um processo em segundo plano separado (o "sweeper") então entrega essas mensagens de forma confiável ao sistema de mensageria.

Requisitos

Recapitulação sobre o Brighter

Antes de continuar falando sobre a configuração do "outbox" com Postgres, vamos recapitular o que já sabemos sobre o Brighter.

Request (Command/Event)

Defina mensagens usando IRequest:

public class OrderPlaced() : Event(Id.Random())
{
    public string OrderId { get; set; } = string.Empty;
    public decimal Value { get; set; }
}
Enter fullscreen mode Exit fullscreen mode
  • Commands: Operações para um único destinatário (ex: SendEmail).
  • Events: Notificações de broadcast (ex: OrderShipped).

Message Mapper (Opcional)

Traduz entre mensagens do Brighter e seus objetos de aplicação, por padrão o Brighter usará um Serializador JSON

public class OrderPlacedMapper : IAmAMessageMapper<OrderPlaced>, IAmAMessageMapperAsync<OrderPlaced>
{ ... }
Enter fullscreen mode Exit fullscreen mode

Request Handler

Processa mensagens recebidas:

public class OrderPlaceHandler(ILogger<OrderPlaceHandler> logger) : RequestHandler<OrderPlaced>
{
    public override Greeting Handle(Greeting command)
    {
        logger.LogInformation("{OrderId} placed with value {OrderValue}", command.OrderId, command.Value);
        return base.Handle(command);

    }
}
Enter fullscreen mode Exit fullscreen mode

Como usar o outbox no Brighter

Antes de configurar o "outbox", é importante entender como interagir com ele

Publicando Mensagens Através do Outbox

O Brighter fornece o método DepositPostAsync para armazenar mensagens no "outbox"

await commandProcessor.DepositPostAsync(
    new OrderPlaced { OrderId = "ORD-123", Value = 99.99m }, 
    cancellationToken: cancellationToken);
Enter fullscreen mode Exit fullscreen mode

Configuração do outbox com PostgreSQL

1. Garanta que a tabela exista

O primeiro passo é criar a tabela do "outbox" em seu banco de dados PostgreSQL. Observe que o Brighter não gerenciará esta tabela para você - você é responsável por criá-la e adicionar os índices necessários:

CREATE TABLE IF NOT EXISTS "outboxmessages"
      (
        Id bigserial PRIMARY KEY, -- optional value you can make the MessageId as PK if you want
        MessageId character varying(255) UNIQUE NOT NULL,
        Topic character varying(255) NULL,
        MessageType character varying(32) NULL,
        Timestamp timestamptz NULL,
        CorrelationId character varying(255) NULL,
        ReplyTo character varying(255) NULL,
        ContentType character varying(128) NULL,
        PartitionKey character varying(128) NULL,  
        WorkflowId character varying(255) NULL,
        JobId character varying(255) NULL,
        Dispatched timestamptz NULL,
        HeaderBag text NULL,
        Body text NULL, -- It can be a blob
        Source character varying (255) NULL,
        Type character varying (255) NULL,
        DataSchema character varying (255) NULL,
        Subject character varying (255) NULL,
        TraceParent character varying (255) NULL,
        TraceState character varying (255) NULL,
        Baggage text NULL
      ); 
Enter fullscreen mode Exit fullscreen mode

2. Configuração do outbox

Configure o "outbox" em sua configuração de injeção de dependência:

var conn = new RelationalDatabaseConfiguration(connectionString, "brightertests", "outboxmessages");

services
  .AddSingleton<IAmARelationalDatabaseConfiguration>(conn)
  .AddConsumers(opt => { ... }) // Or AddBrighter
  .AddProducers(opt => 
  {
     opt.ConnectionProvider = typeof(PostgreSqlUnitOfWork);
     opt.TransactionProvider = typeof(PostgreSqlUnitOfWork);
     opt.Outbox = new PostgreSqlOutbox(outbox);
    ... 
  })
Enter fullscreen mode Exit fullscreen mode

3. Configurar o Sweeper

O Brighter Sweeper é responsável por enviar as mensagens do "outbox" para o gateway de mensageria. Configure-o com tamanhos de lote e outras propriedades apropriadas:

services
  .AddSingleton<IAmARelationalDatabaseConfiguration>(conn)
  .AddConsumers(opt => { ... }) // Or AddBrighter
  .UseOutboxSweeper(opt => { opt.BatchSize = 10; })
Enter fullscreen mode Exit fullscreen mode

Observação: Se estiver executando em um ambiente multi-máquina, certifique-se de que apenas uma instância execute o "sweeper" ou configure o bloqueio distribuído (distributed lock) no Brighter para evitar entrega duplicada de mensagens.

4. Configurar o Arquivamento do Outbox

Se estiver executando em um ambiente multi-máquina, certifique-se de que apenas uma instância execute o "sweeper" ou implemente bloqueio distribuído para evitar entrega duplicada de mensagens.

services
  .AddSingleton<IAmARelationalDatabaseConfiguration>(conn)
  .AddConsumers(opt => { ... }) // Or AddBrighter
  .UseOutboxArchiver<DbTransaction>(new NullOutboxArchiveProvider(),
                    opt => opt.MinimumAge = TimeSpan.FromMinutes(1));
Enter fullscreen mode Exit fullscreen mode

Principais Diferenças Entre Brighter V9 e V10

Outbox como Singleton

Na Brighter V10, o "outbox" é implementado como um serviço singleton, o que significa:

  • Publicação atômica de mensagens (onde várias mensagens ou todas são bem-sucedidas ou todas falham) requer uma abordagem diferente
  • Se a publicação atômica em várias mensagens for crítica para seu aplicativo, considere abrir uma discussão no repositório GitHub do Brighter

Na Brighter V10, o "outbox" é um singleton, portanto, agora não é possível ter uma transação compartilhada (talvez exista uma solução alternativa que eu não encontrei), tornando impossível ter uma publicação atômica de mensagens, como se algo falhar, nenhuma mensagem seja enviada. Se você sentir que isso é um problema em seu aplicativo, abra uma discussão/issue no GitHub do Brighter.

Mudanças na Configuração do Banco de Dados

O construtor RelationalDatabaseConfiguration agora usa:

  • Segundo parâmetro: databaseName (anteriormente era o nome da tabela do outbox)
  • Essa mudança foi necessária para suportar telemetria aprimorada

Mudanças na tabela do banco de dados

Analisando os dois esquemas de tabela, eis as mudanças significativas entre Brighter V9 e V10:

1. Mudanças nos Tipos de Dados

  • MessageId: Alterado de UUID para character varying(255)
    • Fornece mais flexibilidade na identificação da mensagem
    • Permite diferentes formatos de ID de mensagem além de apenas UUIDs
  • CorrelationId: Alterado de UUID para character varying(255)
    • Suporta formatos mais variados de ID de correlação usados em diferentes sistemas

2. Novas Colunas na V10

A V10 introduz várias novas colunas que suportam recursos avançados de mensageria:

  • PartitionKey: Habilita o particionamento de mensagens para melhor escalabilidade
  • WorkflowId & JobId: Suporta nova funcionalidade de processamento de fluxo de trabalho e trabalhos
  • Source & Type: Fornece melhor categorização e roteamento de eventos
  • DataSchema: Permite especificar o esquema de payload para validação
  • Subject: Habilita capacidades mais granulares de filtragem de mensagens
  • TraceParent, TraceState & Baggage: Suporte integrado para rastreamento distribuído
    • Essencial para observabilidade em sistemas distribuídos complexos
    • Integra com padrões modernos de rastreamento

Conclusão

A reformulação do "outbox" na Brighter V10 representa um passo significativo em direção à padronização e interoperabilidade em arquiteturas orientadas a eventos. Ao adotar a especificação Cloud Events, o Brighter permite integração perfeita entre sistemas heterogêneos - seja você trabalhando com RabbitMQ, Kafka ou outras tecnologias de mensageria.

Embora as mudanças exijam algum esforço de migração, elas posicionam seu aplicativo para melhor manutenibilidade a longo prazo e compatibilidade com ecossistemas nativos da nuvem. A adição do suporte a Cloud Events garante que seus eventos serão compreendidos por qualquer sistema que siga este padrão emergente.

Para um exemplo completo funcional, confira a implementação completa no GitHub. Se você encontrar desafios durante a migração, a comunidade Brighter é ativa e acolhedora no GitHub - não hesite em abrir uma issue ou discussão para obter assistência.

Top comments (0)