Contratos de API com AsyncAPI: documentando sistemas orientados a eventos

1. Por que AsyncAPI? O Problema da Documentação em Sistemas Orientados a Eventos

1.1. A lacuna entre REST (OpenAPI) e arquiteturas orientadas a eventos

Sistemas modernos frequentemente combinam APIs síncronas (REST, GraphQL) com fluxos assíncronos baseados em eventos. Enquanto o OpenAPI se consolidou como padrão para documentar APIs REST, as arquiteturas orientadas a eventos careciam de um equivalente. Desenvolvedores recorriam a documentação ad hoc, diagramas soltos ou especificações internas inconsistentes. Essa lacuna gerava retrabalho, bugs de integração e dificuldade para novos membros entenderem o fluxo de eventos.

1.2. Desafios comuns: filas, tópicos, brokers e a falta de um contrato padronizado

Em sistemas com RabbitMQ, Kafka ou MQTT, cada time documentava de forma diferente: uns usavam wikis, outros READMEs, e muitos simplesmente não documentavam. A ausência de um contrato formal dificultava responder perguntas como: "Qual o payload esperado nesse tópico?", "Quem publica e quem consome?", "O schema mudou sem aviso?". Brokers diferentes têm semânticas distintas — filas com entrega garantida vs. tópicos com retenção limitada — e sem um padrão, a confusão era inevitável.

1.3. AsyncAPI como o "OpenAPI dos eventos": princípios e benefícios

AsyncAPI surge para preencher exatamente essa lacuna. Inspirado no OpenAPI, ele oferece um formato YAML/JSON para descrever canais de comunicação assíncrona, operações de publish/subscribe, schemas de mensagens e bindings específicos de cada protocolo. Os benefícios incluem: documentação legível por humanos e máquinas, geração automática de código, validação de mensagens e integração com ferramentas de CI/CD.

2. Estrutura Fundamental de um Documento AsyncAPI

2.1. Componentes essenciais

Um documento AsyncAPI mínimo contém:

  • asyncapi: versão da especificação (ex: "2.6.0")
  • info: metadados (título, versão, descrição)
  • channels: define canais de comunicação (tópicos, filas)
  • components: schemas reutilizáveis, security schemes, message traits

2.2. Definindo canais e operações

Cada canal pode ter duas operações:

  • subscribe: o serviço escuta mensagens naquele canal
  • publish: o serviço envia mensagens para aquele canal

2.3. Exemplo prático: um contrato mínimo

asyncapi: '2.6.0'
info:
  title: Sistema de Notificações
  version: '1.0.0'
  description: Gerencia envio de notificações push e email
channels:
  notificacoes/enviar:
    subscribe:
      message:
        payload:
          type: object
          properties:
            tipo:
              type: string
              enum: [push, email, sms]
            destinatario:
              type: string
            mensagem:
              type: string
          required: [tipo, destinatario, mensagem

3. Modelagem de Mensagens e Schemas

3.1. Payloads, headers e binding de mensagens

AsyncAPI permite descrever headers customizados, payloads complexos e bindings específicos (AMQP headers, Kafka headers, MQTT properties). Para sistemas Kafka, por exemplo, é possível definir a chave da mensagem e partições.

3.2. Reutilização de schemas com components.schemas

components:
  schemas:
    Endereco:
      type: object
      properties:
        rua:
          type: string
        cidade:
          type: string
        cep:
          type: string
    Usuario:
      type: object
      properties:
        id:
          type: integer
        nome:
          type: string
        endereco:
          $ref: '#/components/schemas/Endereco'

3.3. Correlação e IDs de mensagem

Mensagens podem incluir messageId, correlationId e replyTo para padrões request/reply assíncronos.

4. Suporte a Múltiplos Protocolos e Brokers

4.1. Protocol bindings

AsyncAPI suporta bindings para Kafka, RabbitMQ, MQTT, HTTP/SSE, WebSocket, AMQP, JMS, SQS/SNS e NATS. Cada binding adiciona configurações específicas: groupId para Kafka, exchange/routingKey para RabbitMQ, topic para MQTT.

4.2. Diferenças práticas na documentação

Para brokers baseados em fila (RabbitMQ), canais representam filas com operações subscribe/publish. Para brokers baseados em tópico (Kafka), canais representam tópicos com partições e replicação.

4.3. Exemplo: contrato suportando Kafka e RabbitMQ

channels:
  pedidos/criados:
    subscribe:
      bindings:
        kafka:
          groupId: processador-pedidos
          clientId: servico-pedidos
        amqp:
          exchange: pedidos
          routingKey: criados
      message:
        payload:
          type: object
          properties:
            pedidoId:
              type: integer
            cliente:
              type: string

5. Ferramentas do Ecossistema AsyncAPI

5.1. Geradores de código

AsyncAPI Generator produz clientes, servidores e documentação HTML para diversas linguagens (Node.js, Java, Python, Go). Exemplo de uso:

asyncapi generate fromTemplate asyncapi.yaml @asyncapi/nodejs-template

5.2. Validadores e linters

O CLI oficial valida contratos contra a especificação. Linters como spectral podem ser configurados para regras customizadas.

5.3. Integração com CI/CD

É possível validar contratos em pull requests, gerar documentação visual com AsyncAPI Studio e publicar em páginas estáticas (GitHub Pages, GitLab Pages).

6. AsyncAPI na Prática: Ciclo de Vida do Contrato

6.1. Design-first vs. code-first

No design-first, o contrato é criado antes do código, garantindo alinhamento entre times. No code-first, anotações geram o contrato automaticamente. AsyncAPI suporta ambas as abordagens.

6.2. Versionamento de contratos

Canais e mensagens evoluem. AsyncAPI permite versionamento semântico e depreciação de campos.

6.3. Estudo de caso: pipeline de e-commerce

Um sistema de e-commerce documenta eventos como pedido/criado, pagamento/aprovado, estoque/atualizado. Cada microsserviço publica e subscreve canais específicos. O contrato AsyncAPI centraliza essa documentação.

channels:
  pagamento/aprovado:
    publish:
      message:
        payload:
          type: object
          properties:
            pedidoId:
              type: integer
            valor:
              type: number
            formaPagamento:
              type: string
  envio/atualizacao:
    subscribe:
      message:
        payload:
          type: object
          properties:
            codigoRastreio:
              type: string
            status:
              type: string

7. Relação com OpenAPI e Padrões Híbridos

7.1. Coexistência em sistemas síncronos + assíncronos

AsyncAPI e OpenAPI não são concorrentes, mas complementares. Um mesmo serviço pode expor endpoints REST (OpenAPI) e publicar eventos (AsyncAPI).

7.2. Estratégias para unificar documentação

Ferramentas como Redoc e AsyncAPI Studio permitem visualização lado a lado. Repositórios podem conter ambos os contratos, com scripts para validação conjunta.

7.3. Visualização combinada

AsyncAPI Studio renderiza contratos interativamente, com diagramas de canais e exemplos de mensagens.

8. Desafios e Boas Práticas na Adoção

8.1. Complexidade de canais dinâmicos

Tópicos wildcard (ex: pedidos/*/status) são comuns em Kafka. AsyncAPI suporta parâmetros de canal:

channels:
  pedidos/{pedidoId}/status:
    parameters:
      pedidoId:
        schema:
          type: integer

8.2. Ambientes com múltiplos times e brokers

Recomenda-se um repositório centralizado de contratos, com revisão obrigatória. Cada time mantém seu arquivo AsyncAPI, e um agregador combina todos.

8.3. Recomendações para começar

  • Comece documentando um único fluxo crítico
  • Use o AsyncAPI Generator para criar documentação visual
  • Integre validação no CI/CD
  • Participe da comunidade AsyncAPI (GitHub, Discord)

AsyncAPI resolve o problema real de documentar sistemas orientados a eventos, oferecendo um padrão maduro, ferramentas robustas e uma comunidade ativa. Se sua arquitetura depende de mensageria, adotar AsyncAPI é um passo essencial para garantir contratos claros, integração confiável e evolução sustentável.

Referências