Como Consumir um Event no CAP
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 ( | Anotação |
Abstract entity do payload | Elementos da declaração |
Channel / communication arrangement | Binding da aplicação à instância do Event Mesh ( |
Queue + subscription (que você criava na mão) | Criadas automaticamente pelo CAP |
|
|
RAP event handler class (consumo local) |
|
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 topic — publishPrefix: '$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 |
|---|---|---|
| Broker "de mentira" num arquivo local ( | Desenvolvimento local, sem BTP |
| Event Mesh via AMQP, single-tenant | Testes híbridos e apps single-tenant — bem menos setup |
| Event Mesh via protocolos HTTP | Produção (inclusive multitenant) |
plugin | 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
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.
Deixe o CAP criar as filas. Queue criada na mão + queue criada pelo framework = duas filas competindo pelo mesmo topic e mensagens "sumindo".
format: 'cloudevents'sempre que o produtor for S/4/RAP — e valide os prefixos contra as topic rules da instância.Desenvolva com
file-based-messaging, valide com hybrid, publique comenterprise-messaging— três profiles, zero mudança de código.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.
Handler idempotente, como sempre: a entrega é at least once dos dois lados do fio.
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
Comentários 0
Ainda sem comentários
Seja o primeiro a comentar este artigo.
Entre na conversa
Faça login para deixar seu comentário neste artigo.