Stop polling, start emitting
Synchronous OData calls couple systems tightly: if the receiver is down, the sender fails. Asynchronous messaging through SAP Event Mesh decouples them — emit an event, move on, let subscribers catch up.
Use messaging when: the receiver doesn't need an immediate answer, multiple systems care about the change, or the receiver might be temporarily unavailable.
The outbox pattern: reliable emitting
CAP's killer feature for messaging is the transactional outbox: events are written to a database table in the same transaction as your business data, then forwarded to the broker asynchronously.
// in package.json cds.requires
"messaging": {
"kind": "enterprise-messaging-shared"
}
No lost events on crashes, no dual-write problem. If the transaction commits, the event is guaranteed to be delivered.
Emitting events
Define the event in your service, then emit it from a handler:
service OrderService {
entity Orders as projection on my.Orders;
event OrderApproved : { orderID : String; };
}
this.on('approve', 'Orders', async (req) => {
// ... approve logic ...
const messaging = await cds.connect.to('messaging');
await messaging.emit('OrderService/OrderApproved', {
orderID: req.params[0].ID
});
});
The emit writes to the outbox table; CAP's messaging service forwards it to Event Mesh in the background.
Subscribing to events
const messaging = await cds.connect.to('messaging');
messaging.on('OrderService/OrderApproved', async (msg) => {
const { orderID } = msg.data;
// e.g. trigger downstream fulfillment
});
Handlers receive the event payload and run asynchronously. Failed handling retries with backoff — design handlers to be idempotent, since redelivery can happen.
Idempotency is non-negotiable for message handlers: the same event may arrive twice. Check-then-act on a processed-events record.
Local development without a broker
- File-based messaging — CAP supports a local file-system broker for development; no Event Mesh instance needed.
- In-process — emit and subscribe in the same
cds watchprocess to test the full flow. - Promote to shared — switch the
kindtoenterprise-messaging-sharedfor deployed environments.
Event design guidelines
Good events are facts about the past, not commands about the future: OrderApproved, not ApproveOrder. Subscribers decide what a fact means to them; commands couple the emitter to the receiver's behavior.
Keep payloads self-contained — include the IDs and key attributes a subscriber needs, so it doesn't have to call back synchronously. But don't stuff the entire entity in; large payloads make versioning painful.
Version your events: add fields, never rename or remove them. Old subscribers should ignore what they don't understand. A breaking event change is a coordinated deployment across teams — exactly what messaging was supposed to avoid.
Think in events
Not everything needs to be a synchronous API call. The systems that scale best on BTP are the ones that emit facts and react to them.
Designing an event-driven flow? Sketch it in the comments.