Blog SAPienteSAPiente
CAP

Como Consumir um Event no CAP

SAPiente15 de jul. de 2026· 11 min read

O evento saiu do S/4. Quem escuta?

No guia de RAP Business Events do hub, a história terminou com o evento publicado: RAISE ENTITY EVENT, COMMIT WORK, event binding, channel — e o travelBooked viajando pelo Event Mesh com o topic ztravel/Travel/Booked/v1. Mas evento sem consumidor é árvore caindo na floresta vazia. Este post é o outro lado do fio: um app CAP no BTP escutando esse evento — e também os eventos standard do S/4HANA — com uma quantidade de código que vai parecer errada de tão pequena.

Porque essa é a tese do CAP para mensageria, e a documentação oficial (o capire) é explícita: o suporte ao SAP Event Mesh é out of the box, e o framework cuida automaticamente de muita coisa nos bastidores para que o código da aplicação fique agnóstico e focado na mensageria conceitual. Na prática:

  • Você não cria queue nem subscription — o CAP cria automaticamente, a partir dos eventos declarados e dos handlers registrados. A documentação chega a avisar em destaque: não crie manualmente.

  • O handler é uma função registrada com on — o payload chega desserializado, tipado pela declaração do evento.

  • Desenvolvimento local sem broker: um "Event Mesh de mentira" baseado em arquivo permite testar o pub/sub inteiro na sua máquina, sem BTP nenhum.

O mapa mental: RAP ↔ CAP

Se você vem do lado produtor (ABAP), a tradução dos conceitos é quase 1:1 — e entender essa tabela é meio caminho:

No RAP (produtor)

No CAP (consumidor)

Event binding → type do evento (ztravel.Travel.Booked.v1)

Anotação @topic na declaração do evento

Abstract entity do payload

Elementos da declaração event ... { } no CDS do CAP

Channel / communication arrangement

Binding da aplicação à instância do Event Mesh (kind: enterprise-messaging)

Queue + subscription (que você criava na mão)

Criadas automaticamente pelo CAP

RAISE ENTITY EVENT ... FROM ...

srv.emit('evento', payload) — o caminho inverso

RAP event handler class (consumo local)

srv.on('evento', handler)

Passo 1 — Declarar o evento no CDS do CAP

Assim como no RAP o evento é um artefato do modelo (declarado na BDEF), no CAP o evento é declarado no CDS — eventos são cidadãos de primeira classe da linguagem. Para consumir o nosso travelBooked custom, declaramos um serviço com o evento e amarramos o type do event binding via anotação @topic:

// srv/external/travel-events.cds
service TravelEvents {

  @topic: 'ztravel.Travel.Booked.v1'    // o type do event binding no RAP
  event travelBooked {
    TravelId   : String(8);
    CustomerId : String(6);
    TotalPrice : Decimal(16,3);
    Currency   : String(5);
  }

}

Repare: os elementos do evento espelham a abstract entity do payload lá do lado ABAP (mais as chaves, que o RAP sempre manda). O contrato que você desenhou no produtor vira a tipagem do consumidor — dos dois lados, o payload é modelo, não string.

Passo 2 — O handler: uma linha para escutar

Com o evento declarado, consumir é registrar um handler. O padrão do capire, na forma mais enxuta:

// srv/travel-events.js
const cds = require('@sap/cds')

module.exports = async () => {
  const TravelEvents = await cds.connect.to('TravelEvents')

  TravelEvents.on('travelBooked', async (msg) => {
    const { TravelId, CustomerId, TotalPrice, Currency } = msg.data
    console.log(`Viagem ${TravelId} reservada: ${TotalPrice} ${Currency}`)

    // reagir: gravar na base local do app, disparar workflow, notificar...
    await INSERT.into('my.app.TravelMirror')
      .entries({ travelId: TravelId, customer: CustomerId, price: TotalPrice })
  })
}

É isso. Sem cliente AMQP na mão, sem parse de CloudEvents, sem gestão de acknowledge — o msg.data chega desserializado e o ciclo de confirmação da mensagem é do framework. Compare mentalmente com o que seria consumir a mesma queue via REST/AMQP "na unha" e a proposta de valor do CAP fica evidente.

Passo 3 — Configurar o messaging service

A cola entre o handler e o broker é uma configuração no package.json. Para Event Mesh com eventos no padrão CloudEvents (que é como os eventos do RAP e do S/4 viajam), a receita documentada:

{
  "cds": {
    "requires": {
      "messaging": {
        "kind": "enterprise-messaging",
        "format": "cloudevents"
      }
    }
  }
}

E aqui mora um detalhe fino que economiza horas: com format: 'cloudevents' e Event Mesh, o CAP aplica prefixos default de topicpublishPrefix: '$namespace/ce/' para publicar e subscribePrefix: '+/+/+/ce/' para assinar. Traduzindo: ao assinar, ele antepõe +/+/+/ce/ ao topic do evento — que casa exatamente com o padrão <namespace-da-instância>/ce/<topic> em que o S/4 publica (lembra da anatomia do topic no post de RAP Events?). O $namespace é resolvido dinamicamente da instância de Event Mesh vinculada, e os + são wildcards de nível.

Confira as topic rules da instância: esses prefixos precisam ser permitidos na configuração da sua instância de Event Mesh (seção de topic rules) — especialmente o padrão de assinatura com wildcards. Prefixo bloqueado na instância = subscription que nunca acontece, sem erro óbvio no app.

Os kinds disponíveis, do dev à produção:

kind

O que é

Quando

file-based-messaging

Broker "de mentira" num arquivo local (~/.cds-msg-box)

Desenvolvimento local, sem BTP

enterprise-messaging-shared

Event Mesh via AMQP, single-tenant

Testes híbridos e apps single-tenant — bem menos setup

enterprise-messaging

Event Mesh via protocolos HTTP

Produção (inclusive multitenant)

plugin @cap-js/advanced-event-mesh

SAP Integration Suite, advanced event mesh (Solace)

Cenários AEM — o plugin cria queue/subscription no broker

O pulo do gato: testar tudo sem broker nenhum

Essa é a parte que faz inveja a qualquer stack: em desenvolvimento, o CAP troca o Event Mesh por um arquivo local (~/.cds-msg-box) que faz papel de fila. Dois processos cds watch na sua máquina — um emitindo, outro consumindo — e o pub/sub completo funciona sem BTP, sem instância, sem service key. Dá até para abrir o arquivo num editor e ver as mensagens paradas na "fila" enquanto o consumidor está desligado, esperando a entrega.

E o degrau seguinte é o teste híbrido: os serviços continuam rodando localmente, mas ligados à instância real de Event Mesh na nuvem, via cds bind à service key e execução com o profile hybrid. É o jeito de validar a assinatura do topic de verdade (o teu evento do RAP chegando na tua máquina!) antes de qualquer deploy — para o deploy final, o binding entra via MTA como um managed-service do tipo enterprise-messaging.

Consumindo eventos standard do S/4HANA

Para eventos standard (Business Partner, Sales Order...), o capire tem um guia dedicado, e o fluxo tem um passo extra elegante: importar o modelo do serviço e estender com os eventos, já que o EDMX da API não traz as definições de evento.

1. Baixe a especificação da API no SAP Business Accelerator Hub (ex.: Business Partner A2X) e importe: cds import <arquivo.edmx>.

2. No Accelerator Hub, abra a página de Events do objeto e copie o event type e o schema do payload (a aba Schema do POST, expandindo o data — a documentação mostra exatamente onde olhar).

3. Estenda o serviço importado com as declarações de evento:

using { API_BUSINESS_PARTNER as S4 } from './API_BUSINESS_PARTNER';

extend service S4 with {
  event BusinessPartner.Created @(topic: 'sap.s4.beh.businesspartner.v1.BusinessPartner.Created.v1') {
    BusinessPartner : String
  }
  event BusinessPartner.Changed @(topic: 'sap.s4.beh.businesspartner.v1.BusinessPartner.Changed.v1') {
    BusinessPartner : String
  }
}

4. E o consumo fica idêntico ao de um serviço CAP qualquer — é o que a documentação chama de consumo agnóstico:

const S4Bupa = await cds.connect.to('API_BUSINESS_PARTNER')

S4Bupa.on('BusinessPartner.Changed', async (msg) => {
  const { BusinessPartner } = msg.data
  // padrão clássico: o evento avisa, a API busca o resto
  const bp = await S4Bupa.read('A_BusinessPartner', BusinessPartner)
  // ... atualizar a réplica local
})

Repare no padrão da última linha — o evento avisa, a API busca: o payload standard traz pouco (às vezes só a chave), e o handler complementa lendo a API OData do próprio serviço conectado. É exatamente a filosofia do "payload enxuto" pregada no post de RAP Events, vista do lado de quem recebe. E se o payload standard for magro demais para o seu caso, a resposta está lá no post irmão: derived events no lado do S/4.

Do lado do S/4, o pré-requisito é o de sempre: o sistema configurado para publicar os eventos naquela instância de Event Mesh (channel + outbound binding — o passo a passo completo está no post de RAP Business Events).

O caminho inverso: emitindo do CAP

Só para fechar o circuito: o CAP também emite. Um evento declarado no serviço é publicado com emit — e, com os mesmos prefixos CloudEvents, pode ser consumido por um Event Consumption Model no ABAP, pelo CPI ou por outro app CAP:

this.on('UPDATE', 'Reviews', async (req, next) => {
  const result = await next()
  await this.emit('reviewed', { subject: req.data.subject, rating: req.data.rating })
  return result
})

S/4 emitindo via RAP, CAP consumindo, CAP emitindo, ABAP consumindo via Event Consumption Model — a malha fecha nos dois sentidos, com o Event Mesh no meio e CloudEvents como língua franca.

Operação: o que o CAP faz sozinho (e o que sobra para você)

  • Queues e subscriptions: criadas automaticamente — por padrão, uma queue por messaging service, concentrando todas as assinaturas dos handlers registrados. Se quiser separar filas (ex.: uma para eventos do S/4, outra para os seus), configure múltiplos messaging services apontando para o mesmo broker, cada um com sua queue — ou fixe o nome via queue.name (se a fila já existe, é reusada).

  • O que NUNCA é automático: deletar. A documentação é explícita — queues não são deletadas, renomeadas nem têm mensagens purgadas automaticamente (o risco de perda catastrófica de mensagens é a razão). Fila que ficou órfã de um teste é limpeza manual, pelo console do Event Mesh ou pela REST API de gestão.

  • DMQ, redelivery e os detalhes do broker continuam sendo configuração da fila no Event Mesh — tudo que vimos no post de RAP Events (Max Redelivery, 0 = ilimitado, DMQ dedicada) vale igual para as filas que o CAP criou.

Boas práticas consolidadas

  1. Declare os eventos no CDS, mesmo os externos — a mensageria "conceitual" (evento como parte do modelo) é o que mantém o código agnóstico de broker.

  2. Deixe o CAP criar as filas. Queue criada na mão + queue criada pelo framework = duas filas competindo pelo mesmo topic e mensagens "sumindo".

  3. format: 'cloudevents' sempre que o produtor for S/4/RAP — e valide os prefixos contra as topic rules da instância.

  4. Desenvolva com file-based-messaging, valide com hybrid, publique com enterprise-messaging — três profiles, zero mudança de código.

  5. Evento avisa, API busca: não infle o handler tentando extrair tudo do payload; conecte-se ao serviço OData e leia o que faltar.

  6. Handler idempotente, como sempre: a entrega é at least once dos dois lados do fio.

  7. Inclua a limpeza de filas no ciclo de vida do projeto — cada dev com seu queue de teste órfão vira zoológico no broker em um mês.

Perguntas frequentes

Preciso criar a queue e a subscription no Event Mesh antes?

Não — e não deve. O CAP cria a queue do processo consumidor e adiciona as subscriptions automaticamente com base nos eventos declarados e nos handlers registrados. Só gerencie manualmente se fixar queue.name de propósito (fila existente é reusada).

Qual a diferença entre enterprise-messaging e enterprise-messaging-shared?

A variante shared é single-tenant e usa AMQP por padrão — exige bem menos setup e é a recomendada para testes locais/híbridos. A variante enterprise-messaging (produção) usa protocolos HTTP e atende inclusive cenários multitenant.

Como o CAP sabe qual topic assinar para o meu evento custom do RAP?

Pela anotação @topic com o type do event binding (ztravel.Travel.Booked.v1) + os prefixos do formato CloudEvents: o subscribePrefix default +/+/+/ce/ casa com o padrão <namespace>/ce/<topic> em que o S/4 publica. Se a sua instância usa outra convenção, ajuste subscribePrefix explicitamente.

E se eu uso o Advanced Event Mesh (AEM) em vez do Event Mesh?

Existe o plugin oficial @cap-js/advanced-event-mesh, que integra o CAP ao broker Solace do AEM — inclusive criando queue e subscription (com opção de desligar e gerenciar manualmente). O broker em si e a confiança com o Identity Services são pré-requisitos configurados no AEM.

O payload do RAP chega em que formato no handler?

Como CloudEvents: os campos de cabeçalho (type, source, id, time...) ficam nos headers da mensagem, e o msg.data traz o conteúdo — as chaves da entidade + os campos da abstract entity do payload, já desserializados conforme a declaração do evento no CDS.

Consigo testar o fluxo de ponta a ponta sem deploy?

Sim — é o teste híbrido: cds bind à service key da instância de Event Mesh e execução local com o profile hybrid. Dispare o evento no S/4 (com o COMMIT WORK!) e veja o handler rodar no seu terminal.

Conclusão

O lado consumidor, que no mundo REST/AMQP "na unha" significa cliente de protocolo, parse de CloudEvents, gestão de queue e de acknowledge, no CAP vira três artefatos: uma declaração de evento no CDS, um handler de poucas linhas e um bloco de configuração. O framework cria a fila, assina o topic, desserializa o payload e ainda oferece um broker de mentira para você desenvolver offline. Somado ao post de RAP Business Events, o circuito está completo: o S/4 grita, o BTP escuta — e nenhum dos dois lados precisou saber que o outro existe. Arquitetura orientada a eventos como ela foi prometida.

Referências oficiais

TagsClean Core
Avalie este conteúdo
para avaliar
SAPiente
@sapiente

Blog sobre SAP, ABAP, BTP, Fiori e tudo que envolve o ecossistema SAP.

Ver perfil →
FacebookInstagramYouTubeTwitter

Comentários 0

Entre na conversa

Faça login para deixar seu comentário neste artigo.

Ainda sem comentários

Seja o primeiro a comentar este artigo.