Escrito por: Jean Carlos M. da Silva

E aí, galera da tecnologia! 👋Durante meus anos trabalhando com microsserviços, sistemas distribuídos e tudo que é cloud-native, tenho visto muitos padrões e anti-padrões por aí.

Hoje, quero continuar nosso papo sobre Arquitetura Orientada a Eventos (EDA), mas com um foco especial: a padronização dos contratos/schemas dos eventos e o versionamento deles.

Sabe aquela sensação de déjà vu quando você vai integrar com um novo microsserviço e o schema do evento é… digamos, criativo? Pois é! 😅 Em um mundo cada vez mais orientado a eventos, a falta de um esperanto para descrevê-los pode transformar o sonho da reatividade em um pesadelo de adaptações constantes.

Eventos estão por toda parte, mas os produtores tendem a descrevê-los de formas diferentes. Essa falta de um jeito comum de descrever eventos significa que nós, desenvolvedores, precisamos reaprender constantemente como consumir cada um deles.

Isso também limita o potencial de bibliotecas, ferramentas e infraestrutura que poderiam nos ajudar a entregar dados de eventos entre ambientes, como SDKs, roteadores de eventos ou sistemas de rastreamento. A portabilidade e a produtividade que poderíamos alcançar com dados de eventos acabam prejudicadas.

A dor de gerenciar inúmeros eventos se intensifica quando:

Essa falta de padronização não é apenas um incômodo técnico; ela se transforma em um gargalo de negócios. Pense bem: sistemas distribuídos e microsserviços são adotados buscando agilidade e escalabilidade. A comunicação via eventos é crucial nesses cenários.

Se cada evento é um dialeto diferente, o retrabalho para integração e adaptação é constante. Esse retrabalho consome um tempo precioso de desenvolvimento que poderia ser investido em novas funcionalidades.

A dificuldade em integrar serviços, sejam eles internos ou externos, atrasa a composição de funcionalidades mais complexas e, consequentemente, impacta diretamente a velocidade de inovação e o time-to-market.

Mas e se eu te dissesse que existe uma luz no fim do túnel? Uma forma elegante e padronizada de colocar ordem nessa bagunça, promovida pela mesma fundação que nos deu o Kubernetes?🤔

CloudEvents ao Resgate! 🦸 O Que São e Por Que Padronizar?

Segurem seus cafés ☕, porque vamos falar de CloudEvents! Pensem nele como o RG universal dos seus eventos.

CloudEvents é uma especificação – um conjunto de regras do jogo, se preferir – para descrever dados de eventos de uma maneira comum e padronizada. O objetivo? Simples: fazer com que sistemas diferentes conversem a mesma língua quando o assunto é evento, não importa se estão rodando na nuvem A, B ou C, ou se foram escritos em linguagens diferentes. Ele não dita o conteúdo do seu evento (o data), mas sim o envelope (os metadados) que o carrega.

A Importância e os Benefícios da Padronização:

A Jornada do CloudEvents na CNCF:

Esse projeto não nasceu ontem! Ele é cria da Cloud Native Computing Foundation (CNCF), a mesma casa de gigantes como Kubernetes, Prometheus e Fluentd. Surgiu da necessidade identificada no Serverless Working Group em maio de 2018.

A trajetória foi sólida:

Grandes players da nuvem como AWS, Google, Microsoft, IBM, SAP, Red Hat, VMware rapidamente demonstraram interesse e contribuíram, o que mostra a relevância da iniciativa.

A graduação de um projeto pela CNCF não é apenas um marco técnico; é um sinal de redução de risco de adoção para empresas. Projetos graduados demonstram estabilidade, adoção ampla e governança madura, elementos cruciais para decisões de arquitetura de longo prazo.

Isso significa que, ao adotar CloudEvents, as empresas estão investindo em um padrão com forte respaldo da indústria e menor receio de obsolescência.

Entendendo alguns atributos essenciais do CloudEvents v1.0.x:

Para entender a mágica, precisamos conhecer os atores principais: os atributos de contexto do CloudEvents. Eles são como as informações na etiqueta de uma encomenda.

A especificação v1.0.x define um conjunto de atributos obrigatórios e opcionais:

A ênfase do CloudEvents em metadados padronizados, separando o envelope do conteúdo, não só facilita a interoperabilidade técnica, mas também abre caminho para uma governança de eventos mais inteligente e automatizada.

Roteadores, políticas de segurança e ferramentas de observabilidade podem operar sobre esses metadados (como type, source, subject) sem precisar entender a complexidade de cada payload de evento. Isso permite que as ferramentas de governança operem de forma mais eficiente e genérica, simplificando a construção e manutenção de uma plataforma de eventos robusta.

Mão na Massa: Implementando CloudEvents com C#/.NET 9 💻✨

Ok, teoria entendida! Mas como isso funciona na prática, especialmente no nosso querido .NET? Vamos ver algumas estratégias de como podemos usar CloudEvents, sem mergulhar muito fundo nos detalhes de implementação ainda (guardem a ansiedade para o exemplo prático! 😉).

1. Identificando e Propagando Eventos de Negócio:

No coração do DDD, temos os eventos de domínio. Quando algo relevante acontece no seu negócio (um PedidoCriado, um EstoqueAtualizado), isso é um evento de negócio.

A ideia aqui é: capture esse evento e, ao publicá-lo para outros serviços, envolva-o em um CloudEvent. O type do CloudEvent pode ser algo como com.minhaempresa.pedido.criado.v1, e o data conteria os detalhes específicos desse pedido. Isso garante que todos os interessados nesse evento de negócio recebam uma mensagem com um envelope padronizado, facilitando o consumo.

2. Interceptando e Padronizando Eventos (Ex: de Sistemas Legados ou para Auditoria):

Tem um sistema legado cuspindo eventos num formato… exótico? 🦖 Você pode ter um adaptador/serviço que intercepta esses eventos, os converte para o formato CloudEvents (preenchendo os atributos de contexto de forma apropriada) e os republica.

A especificação CloudEvents menciona CloudEvents Adapters justamente para esses cenários onde nem todos os produtores geram CloudEvents nativamente.

Essa estratégia é crucial para a modernização de sistemas legados, permitindo que o legado continue operando enquanto seus eventos são gradualmente integrados ao ecossistema moderno.

Funciona como uma camada de anticorrupção para os formatos de evento, desacoplando o legado dos consumidores modernos e facilitando uma modernização incremental.

Outro caso: auditoria. Você pode querer que todos os eventos que passam por um determinado ponto do seu sistema sejam logados ou processados para fins de auditoria.

Se eles estiverem em formato CloudEvents, sua lógica de auditoria se torna muito mais simples e genérica.

3. Publicação de Eventos Padronizados para Consumidores Diversos:

Seu serviço precisa notificar outros N serviços sobre uma mudança. Ao invés de cada consumidor ter que entender um formato customizado, você publica um CloudEvent.

Isso é especialmente útil se os consumidores são de times diferentes, ou até mesmo externos (parceiros). O C#/.NET SDK para CloudEvents, como o CloudNative.CloudEvents (o SDK oficial da CNCF) e o Azure.Messaging.CloudEvent (comum no ecossistema Azure), facilitam a criação desses eventos. No nosso exemplo prático, focaremos no CloudNative.

CloudEvents pela sua generalidade; Com o .NET 9 chegando com mais melhorias para cloud-native e performance, a integração com padrões como CloudEvents se torna ainda mais fluida. As bibliotecas estão aí para nos ajudar a construir esses eventos de forma programática e correta.

Chega de papo, vamos codar! 🧑💻 Vou mostrar um exemplo simples usando C#/.NET9 para uma API REST que, ao receber um comando de criação de pedidos de compra, gera um evento de negócio, de pedido criado, o empacota como CloudEvent e o publica no Kafka.

Crie um projeto ASP.NET Core Web API e adicione os pacotes NuGet:

1. Vamos definir primeiro o schema do Order

public class Order
(Guid id, Guid customerId, Guid productId, int quantity, decimal amount)
{
    public Guid Id { get; init; } = id;
    public Guid CustomerId { get; init;} = customerId;
    public Guid ProductId { get; init;} = productId;
    public int Quantity { get; init;} = quantity;
    public decimal Amount { get; init;} = amount;
}

2. Agora vamos construir uma “minimal api” que irá receber uma requisição, gerar um “order” fake e transforma-lo em um cloud event e publicar no KAFKA.

app.MapPost("/v1/orders", async (
    IProducer<string, byte[]> kafkaProducer,
    CloudEventFormatter formatter,
    CancellationToken cancellationToken) =>
    {

        // Fake Order
        var order = new Order(
            Guid.NewGuid(), // oderId
            Guid.NewGuid(), // customerId
            Guid.NewGuid(), // productId
            10, //quantity
            1500.20M // amount
        );

        // Simulate order processing...

        // Aqui, vamos criar um CloudEvent para representar o evento de    criação do pedido.
        var time = DateTimeOffset.UtcNow; // timestamp for the CloudEvent
        var cloudEvent = new CloudEvent()
        {
            Id = Guid.CreateVersion7(time).ToString(), // ID do CloudEvent
            Source = new Uri("https://api.cloudnativeararaquara.com/v1/orders"), // Origem
            Type = $"{typeof(Order)}.v1", // Tipo do evento + versão (boa prática)
            DataContentType = MediaTypeNames.Application.Json, // "application/json"
            Data = order,
            Time = time,
            Subject = order.Id.ToString(),
            // Idealmente, termos um "repo" de schemas para especificar no evento. 
            DataSchema = new Uri("https://schemas.cloudnativeararaquara.com/v1/orders.json")

        };

        // Vamos serializar o cloud event para um formato que o kafka entenda !!
        // O ToKafkaMessage faz a mágica de converter para o formato do Kafka,
        // usando o formatter para serializar o CloudEvent para JSON (no modo estruturado).
        var message = cloudEvent.ToKafkaMessage(ContentMode.Structured, formatter);

        // Se quiséssemos usar o modo binário:
        // var kafkaMessageBinary = cloudEvent.ToKafkaMessage(ContentMode.Binary, _cloudEventFormatter);
        // No modo binário, os atributos do CloudEvent vão para os Headers da mensagem Kafka,
        // e o 'Data' (orderData serializado como JSON pelo formatter) vai para o Value.


        await kafkaProducer.ProduceAsync("order-created", message!, cancellationToken);
        return Results.Created($"/v1/orders/{order.Id}", order);
    })
    .WithName("Orders");

3. Agora vamos construir nosso consumer

public class Worker(ILogger<Worker> logger, IConsumer<string, byte[]> consumer, CloudEventFormatter formatter) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

        // Se inscrever no tópico "order-created"
        consumer.Subscribe("order-created");

        while (!stoppingToken.IsCancellationRequested)
        {
            // Consumir mensagens do Kafka
            var result = consumer.Consume(stoppingToken);
            if(result is null) continue;

            // capturar a mensagem e convertê-la para um CloudEvent
            var message = result.Message;
            var cloudEvent = message!.ToCloudEvent(formatter);
            // Deserializar o CloudEvent para um objeto Order
            var order = JsonSerializer.Deserialize<Order>(cloudEvent.Data?.ToString() ?? string.Empty);

            logger.LogInformation("Received Order: {orderId}",order!.Id);

            await Task.Delay(1000, stoppingToken);
        }
    }
}

Notas Importantes no Exemplo:

Este exemplo prático mostra que a adoção de CloudEvents com .NET e Kafka não exige uma reescrita massiva do código existente. O SDK do CloudEvents age como uma camada fina e bem definida para a padronização do envelope do evento, enquanto a lógica de negócio e a interação com o Kafka permanecem familiares.

A facilidade de adicionar atributos de extensão customizados aos CloudEvents, combinada com a padronização dos atributos principais, oferece um equilíbrio poderoso entre conformidade com o padrão e necessidades específicas de cada aplicação/organização. Isso permite que as empresas se beneficiem da interoperabilidade global enquanto ainda podem enriquecer os eventos com metadados contextuais próprios.

Nem Tudo São Flores: Os Trade-offs dos CloudEvents 🧐

Como tudo na vida de engenharia de software, não existe bala de prata! 🧙♂️ Adotar CloudEvents traz muitos benefícios, mas é bom estar ciente de algumas considerações e desafios:

1. Curva de Aprendizagem e Adoção Inicial

Embora a especificação seja bem documentada e os SDKs ajudem, há uma curva de aprendizado para o time entender os atributos, os modos de transporte (structured vs. binary) e as melhores práticas. Garantir que toda a organização adote o padrão de forma consistente pode ser um desafio de governança.

2. Overhead de Metadados e Tamanho da Mensagem

Os atributos de contexto do CloudEvents adicionam alguns bytes à sua mensagem. No modo structured (onde tudo vai no payload JSON), isso pode aumentar um pouco o tamanho total. Para cenários de altíssimo volume e latência ultra-baixa, cada byte conta.

O modo binary (metadados nos headers, payload original no corpo) pode ser mais eficiente nesses casos, mas exige que o protocolo de transporte suporte bem headers customizados (Kafka, HTTP, AMQP suportam! ). A especificação sugere um limite de 64KB para mensagens, incentivando payloads compactos ou links para dados maiores.

3. A Opcionalidade do dataschema

Como vimos, o atributo dataschema (que aponta para o schema do seu payload) é opcional. Isso dá flexibilidade, mas… se os produtores não o utilizarem consistentemente, os consumidores podem voltar à estaca zero tentando adivinhar a estrutura do payload.

Aqui, a governança interna é chave! 🔑 Definir como sua organização vai usar (ou exigir) o dataschema é crucial.

CloudEvents, por si só, não é uma solução completa para governança de schema de eventos; ele fornece um framework que precisa ser complementado por práticas internas e, idealmente, por um Schema Registry.

4. Versionamento do Payload (Data) Ainda é Responsabilidade Sua

CloudEvents padroniza o envelope. A versão da especificação CloudEvents em si é tratada pelo atributo specversion (ex: 1.0).

Mas e a versão do seu payload (data)? Isso ainda precisa ser gerenciado por você! Estratégias comuns incluem versionar o type do evento (ex: com.meudominio.pedido.criado.v1, …v2) ou usar o dataschema para apontar para diferentes versões do schema do payload.

5. Complexidade em Ambientes Muito Heterogêneos (se não houver SDKs)

Embora existam SDKs para muitas linguagens , se você precisar integrar com um sistema em uma linguagem muito obscura sem SDK, a implementação manual da especificação pode adicionar um pouco de trabalho.


Os trade-offs de CloudEvents (overhead vs. padronização, flexibilidade vs. rigor) refletem um desafio fundamental em sistemas distribuídos: encontrar o equilíbrio certo entre autonomia de equipe/serviço e consistência/interoperabilidade global. CloudEvents pende para a interoperabilidade global do envelope, mas deixa alguma autonomia no payload, que deve ser gerenciada pelas equipes de desenvolvimento e arquitetura.

Conclusão: Padronizar para Conquistar! 🏁 E Fiquem Ligados!

Ufa! Falamos bastante, né? 😅 Mas espero que tenha ficado claro: padronizar os contratos e o versionamento dos seus eventos com CloudEvents não é só “modinha”. É um passo fundamental para construir arquiteturas orientadas a eventos mais robustas, interoperáveis e fáceis de manter e evoluir.

Em um mundo de microsserviços e sistemas distribuídos, falar a mesma língua é o que nos permite escalar de verdade, integrar com parceiros e inovar mais rápido. CloudEvents é o nosso passaporte para essa comunicação fluida! 🛂

E aí, curtiram o mergulho nos CloudEvents? 🏊♂️ O que mais vocês gostariam de ver por aqui sobre EDAs, microsserviços,.NET, Kafka ou o universo cloud-native? Deixem seus comentários!

📢 ATENÇÃO, MUDANÇA DE PLANOS! Para facilitar nosso bate-papo e trazer conteúdo fresquinho com mais frequência, nossos encontros técnicos por aqui agora serão às QUINTAS-FEIRAS! 🎉 Chega de esperar o domingão ao meio-dia para aquela dose de arquitetura e código. 😉

#cloudevents #eventdrivenarchitecture #EDA #microservices #dotnet #azure #microsoft #kafka #kubernetes #systemdesign #softwarearchitecture #CNCF #cloudnative #cloudnativeararaquara #opensource

Apoio: 5by5 | Soluções em Sistemas