Adicione o texto do seu título aqui

Eventos, Eventos Por Toda Parte… E Nenhuma Gota de Padrão? Vamos falar de CloudEvents

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:

  • Uma mudança acontece e adaptar-se à alteração dos schemas vira uma verdadeira caça ao tesouro 🕵️.
  • Faltam campos essenciais no evento, ou cada produtor inventa seu próprio “timestamp” (às vezes até como string! 😱).
  • A interoperabilidade entre sistemas de diferentes times, ou até de diferentes vendors, parece uma missão impossível. Sem padrões, a integração de eventos ou fluxos de mensagens entre sistemas, brokers e organizações se torna um desafio. Cada sistema precisa entender a estrutura do evento ou depender de soluções customizadas para versionamento ou validação.

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:

  • Interoperabilidade Turbinada: Imaginem poder conectar um produtor de eventos da AWS com um consumidor no Azure, ou um serviço legado com uma nova função serverless, sem precisar de um tradutor juramentado para cada mensagem!. CloudEvents simplifica a interoperabilidade fornecendo um schema de evento comum para publicação e consumo.
  • Adeus, Reaprendizado Constante: Chega de quebrar a cabeça para entender como cada sistema descreve seus eventos. Com CloudEvents, a estrutura dos metadados é consistente.
  • Ecossistema de Ferramentas e SDKs: A padronização abre portas para bibliotecas, SDKs (sim, temos para .NET! 💚), roteadores de eventos e sistemas de tracing que funcionam out-of-the-box. A especificação suporta SDKs em diversas linguagens, incluindo C#/.NET, Go, Java, Javascript, Python, entre outras.
  • Portabilidade e Produtividade: No final do dia, é sobre ganhar agilidade e focar no que realmente importa: a lógica de negócio!.

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:

  • 🚀 Projeto Sandbox: 15 de Maio de 2018
  • 🛠️ Projeto Incubadora: 24 de Outubro de 2019 (junto com o V1.0 da especificação!)
  • 🎓 Projeto Graduado: 25 de Janeiro de 2024! Um selo de maturidade e ampla adoção pela indústria. 🎉

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:

  • id : Identifica o evento. Produtores DEVEM garantir que source + id seja único para cada evento distinto (Obrigatório).
  • source: Identifica o contexto em que o evento aconteceu, uma URI de referência (Obrigatório).
  • specVersion: A versão da especificação CloudEvents que o evento usa por exemplo “1.0” (Obrigatório).
  • type: Descreve o tipo de evento relacionado à ocorrência original. Usado para roteamento, observabilidade, etc. Como por exemplo: com.example.order.created (Obrigatório).
  • dataContentType: Tipo de conteúdo do valor em data (payload). Como por exemplo: application/json (Opcional).
  • dataSchema: URI que identifica o schema ao qual data adere (Opicional).
  • time: Timestamp (UTC, RFC3339) de quando a ocorrência aconteceu. Se não determinável, pode ser o tempo de produção do CloudEvent (Opicional).

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:

  • CloudNative.CloudEvents.SystemTextJson (para o JsonEventFormatter)
  • CloudNative.CloudEvents.Kafka (para a extensão ToKafkaMessage)
  • Confluent.Kafka (o driver do Kafka)

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 é um exemplo simplificado! Em produção, você cuidaria de configurações mais robustas do Kafka (acks, retries, idempotência), tratamento de exceções mais elaborado, logging, etc..
  • A escolha entre ContentMode.Structured e ContentMode.Binary depende do seu cenário. Structured é mais simples de inspecionar, Binary pode ser mais performático para payloads grandes se os metadados forem suficientes para roteamento.
  • O dataschema é super importante para versionamento e validação do payload. Em um cenário real, você apontaria para um schema em um Schema Registry

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

Compartilhe este texto:

Adicione o texto do seu título aqui

Ao navegar neste site, você aceita os cookies que usamos para melhorar sua experiência. Veja mais informações.