SAP Event Mesh mit ABAP Cloud: Event-driven Architecture

kategorie
Integration
Veröffentlicht
autor
Johannes

SAP Event Mesh ermöglicht lose gekoppelte, event-basierte Kommunikation zwischen ABAP-Systemen und anderen Anwendungen auf der SAP Business Technology Platform. Mit RAP Business Events kannst du Geschäftsereignisse definieren und in Echtzeit an externe Systeme propagieren.

Was ist Event-driven Architecture?

Event-driven Architecture (EDA) ist ein Architekturmuster, bei dem Systeme über Events kommunizieren statt über direkte API-Aufrufe:

AspektDirekte IntegrationEvent-driven
KopplungEng (synchron)Lose (asynchron)
VerfügbarkeitAbhängig vom ZielsystemUnabhängig
SkalierbarkeitLimitiertHoch
FehlertoleranzNiedrigHoch (Retry/Replay)
LatenzSynchronAsynchron
Use CasesTransaktionale KonsistenzBenachrichtigungen, Replikation

SAP Event Mesh Konzept

SAP Event Mesh ist ein vollständig verwalteter Messaging-Service auf der BTP:

┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ ABAP Cloud │ │ SAP Event Mesh │ │ Subscriber │
│ (Publisher) │─────>│ (Message Broker)│─────>│ (Consumer) │
│ │ │ │ │ │
│ RAP Business │ │ - Queues │ │ - ABAP Cloud │
│ Events │ │ - Topics │ │ - CAP App │
│ │ │ - Subscriptions │ │ - 3rd Party │
└─────────────────┘ └──────────────────┘ └─────────────────┘

Kernkonzepte:

  • Event: Nachricht über ein Geschäftsereignis (z.B. “Auftrag erstellt”)
  • Topic: Logischer Kanal für Events (z.B. “sap/sales/order/created”)
  • Queue: Persistenter Speicher für Events pro Subscriber
  • Subscription: Verbindung zwischen Topic und Queue

RAP Business Event definieren

Business Events werden in der Behavior Definition deklariert und bei Zustandsänderungen ausgelöst.

Behavior Definition mit Events

managed implementation in class zbp_i_salesorder unique;
strict ( 2 );
define behavior for ZI_SalesOrder alias SalesOrder
persistent table zsalesorder
lock master
authorization master ( instance )
etag master LastChangedAt
with additional save
{
create;
update;
delete;
// Business Events definieren
event created;
event updated;
event deleted;
event statusChanged parameter ZA_StatusChangeInfo;
}

Event mit Parameter-Struktur

Für komplexe Events definierst du eine Abstract Entity als Parameter:

@EndUserText.label: 'Status Change Info'
define abstract entity ZA_StatusChangeInfo
{
SalesOrderId : abap.char(10);
OldStatus : abap.char(2);
NewStatus : abap.char(2);
ChangedBy : abap.uname;
ChangedAt : timestampl;
}

Event Publishing

Events werden in der Behavior Implementation ausgelöst – typischerweise in Determinations oder Actions.

Event bei Create auslösen

CLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS raise_created_event FOR DETERMINE ON SAVE
IMPORTING keys FOR SalesOrder~raiseCreatedEvent.
ENDCLASS.
CLASS lhc_salesorder IMPLEMENTATION.
METHOD raise_created_event.
" Event für neue Instanzen auslösen
RAISE ENTITY EVENT zi_salesorder~created
FROM VALUE #( FOR key IN keys
( %key = key-%key ) ).
ENDMETHOD.
ENDCLASS.

Event bei Statusänderung mit Parametern

CLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS on_status_change FOR DETERMINE ON SAVE
IMPORTING keys FOR SalesOrder~onStatusChange.
ENDCLASS.
CLASS lhc_salesorder IMPLEMENTATION.
METHOD on_status_change.
" Alte und neue Werte lesen
READ ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(orders).
" Event mit Parametern auslösen
RAISE ENTITY EVENT zi_salesorder~statusChanged
FROM VALUE #( FOR order IN orders
( %key = order-%key
%param = VALUE #(
salesorderid = order-SalesOrderId
oldstatus = order-OldStatus
newstatus = order-Status
changedby = sy-uname
changedat = utclong_current( ) ) ) ).
ENDMETHOD.
ENDCLASS.

Determination für Event-Trigger

managed implementation in class zbp_i_salesorder unique;
strict ( 2 );
define behavior for ZI_SalesOrder alias SalesOrder
persistent table zsalesorder
lock master
authorization master ( instance )
with additional save
{
create;
update;
delete;
// Events
event created;
event statusChanged parameter ZA_StatusChangeInfo;
// Determinations für Event-Trigger
determination raiseCreatedEvent on save { create; }
determination onStatusChange on save { field Status; }
}

Event Consumption

Events können in anderen ABAP-Systemen oder externen Anwendungen konsumiert werden.

Event Handler in ABAP Cloud

CLASS zcl_salesorder_event_handler DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_event_handler.
ENDCLASS.
CLASS zcl_salesorder_event_handler IMPLEMENTATION.
METHOD if_rap_event_handler~handle.
" Event-Typ prüfen
CASE io_event->get_event_name( ).
WHEN 'CREATED'.
handle_created( io_event ).
WHEN 'STATUSCHANGED'.
handle_status_changed( io_event ).
ENDCASE.
ENDMETHOD.
METHOD handle_created.
DATA: lt_keys TYPE TABLE OF zi_salesorder.
" Event-Daten lesen
io_event->get_business_data( IMPORTING et_business_data = lt_keys ).
" Verarbeitung: z.B. Nachricht senden, Log schreiben
LOOP AT lt_keys INTO DATA(ls_key).
" Business-Logik
ENDLOOP.
ENDMETHOD.
METHOD handle_status_changed.
DATA: lt_params TYPE TABLE OF za_statuschangeinfo.
" Parameter-Daten lesen
io_event->get_business_data( IMPORTING et_business_data = lt_params ).
LOOP AT lt_params INTO DATA(ls_param).
" Z.B. E-Mail senden bei Statuswechsel
IF ls_param-newstatus = 'AP'. " Approved
send_approval_notification( ls_param ).
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Event Handler registrieren

Die Registrierung erfolgt über das Business Event Handling Framework:

" Registrierung in Customizing-Tabelle oder per API
DATA(lo_registration) = cl_beh_event_registration=>get_instance( ).
lo_registration->register_handler(
iv_event_name = 'CREATED'
iv_handler_class = 'ZCL_SALESORDER_EVENT_HANDLER'
iv_source_entity = 'ZI_SALESORDER' ).

Konfiguration im BTP Cockpit

1. Event Mesh Service erstellen

  1. Im BTP Cockpit navigiere zu deinem Subaccount
  2. Gehe zu Service MarketplaceSAP Event Mesh
  3. Erstelle eine Service-Instanz mit Plan default
  4. Erstelle einen Service Key für die Authentifizierung

2. Message Client konfigurieren

Die Service-Key-Daten enthalten alle notwendigen Verbindungsparameter:

{
"management": [
{
"oa2": {
"clientid": "sb-event-mesh-client!...",
"clientsecret": "...",
"tokenendpoint": "https://.../oauth/token"
},
"uri": "https://enterprise-messaging-hub-backend..."
}
],
"messaging": [
{
"oa2": {
"clientid": "...",
"clientsecret": "...",
"tokenendpoint": "..."
},
"protocol": ["amqp10ws"],
"broker": {
"type": "sapmgw"
},
"uri": "wss://enterprise-messaging-pubsub.cfapps..."
}
],
"namespace": "default/sap.s4/salesorder"
}

3. Communication Arrangement im ABAP-System

┌──────────────────────────────────────────────────────────────┐
│ Communication Arrangement │
├──────────────────────────────────────────────────────────────┤
│ Scenario: SAP_COM_0092 (Enterprise Eventing Integration) │
│ │
│ Communication System: EVENT_MESH_SYSTEM │
│ ├── Host: enterprise-messaging-pubsub.cfapps... │
│ ├── Port: 443 │
│ └── Auth: OAuth 2.0 Client Credentials │
│ │
│ Outbound Services: │
│ └── Enterprise Event Enablement: Active │
└──────────────────────────────────────────────────────────────┘

4. Channel konfigurieren

Im ABAP-System unter Enterprise Event Enablement:

" Event Channel anlegen
DATA(lo_channel) = cl_eee_channel_factory=>create_channel(
iv_channel_id = 'Z_SALESORDER_EVENTS'
iv_description = 'Sales Order Events'
iv_topic_space = 'default/sap.s4/salesorder'
iv_destination = 'EVENT_MESH_DESTINATION' ).
" Topic-Binding
lo_channel->add_topic_binding(
iv_event_type = 'ZI_SALESORDER.CREATED'
iv_topic = 'sap/salesorder/created/v1' ).
lo_channel->add_topic_binding(
iv_event_type = 'ZI_SALESORDER.STATUSCHANGED'
iv_topic = 'sap/salesorder/statuschanged/v1' ).

Event Consumption in externen Systemen

CAP Application (Node.js)

const cds = require('@sap/cds');
module.exports = async (srv) => {
// Event Mesh Verbindung
const messaging = await cds.connect.to('messaging');
// Event-Handler registrieren
messaging.on('sap/salesorder/created/v1', async (msg) => {
const { SalesOrderId } = msg.data;
console.log(`New sales order created: ${SalesOrderId}`);
// Business-Logik
await processSalesOrder(SalesOrderId);
});
messaging.on('sap/salesorder/statuschanged/v1', async (msg) => {
const { SalesOrderId, OldStatus, NewStatus } = msg.data;
console.log(`Status changed: ${SalesOrderId} from ${OldStatus} to ${NewStatus}`);
});
};

Python Consumer

from sap_event_mesh_client import EventMeshClient
# Client initialisieren
client = EventMeshClient(
service_key_path='event-mesh-service-key.json'
)
# Queue abonnieren
@client.subscribe('queue:salesorder-events')
def handle_salesorder_event(message):
event_type = message.headers.get('type')
if event_type == 'sap/salesorder/created/v1':
order_id = message.body['SalesOrderId']
print(f'Processing new order: {order_id}')
# Business-Logik
message.ack() # Nachricht bestätigen
# Consumer starten
client.start()

Komplettes Beispiel: Order Processing Pipeline

Business Object mit Events

managed implementation in class zbp_i_purchaseorder unique;
strict ( 2 );
define behavior for ZI_PurchaseOrder alias PurchaseOrder
persistent table zpurchaseorder
lock master
authorization master ( instance )
etag master LastChangedAt
with additional save
{
create;
update;
delete;
// Aktionen
action ( features : instance ) approve result [1] $self;
action ( features : instance ) reject result [1] $self;
// Events für jede wichtige Zustandsänderung
event created;
event approved parameter ZA_ApprovalInfo;
event rejected parameter ZA_RejectionInfo;
event amountChanged parameter ZA_AmountChangeInfo;
// Trigger
determination triggerCreated on save { create; }
determination triggerAmountChange on save { field TotalAmount; }
}

Event-Parameter

@EndUserText.label: 'Approval Info'
define abstract entity ZA_ApprovalInfo
{
PurchaseOrderId : abap.char(10);
ApprovedBy : abap.uname;
ApprovedAt : timestampl;
TotalAmount : abap.dec(15,2);
}
@EndUserText.label: 'Rejection Info'
define abstract entity ZA_RejectionInfo
{
PurchaseOrderId : abap.char(10);
RejectedBy : abap.uname;
RejectedAt : timestampl;
RejectionReason : abap.char(255);
}

Action Implementation mit Event

CLASS lhc_purchaseorder IMPLEMENTATION.
METHOD approve.
" Status aktualisieren
MODIFY ENTITIES OF zi_purchaseorder IN LOCAL MODE
ENTITY PurchaseOrder
UPDATE FIELDS ( Status ApprovedBy ApprovedAt )
WITH VALUE #( FOR key IN keys
( %tky = key-%tky
Status = 'AP'
ApprovedBy = sy-uname
ApprovedAt = utclong_current( ) ) )
FAILED failed
REPORTED reported.
" Ergebnis lesen
READ ENTITIES OF zi_purchaseorder IN LOCAL MODE
ENTITY PurchaseOrder
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(orders).
" Event auslösen
RAISE ENTITY EVENT zi_purchaseorder~approved
FROM VALUE #( FOR order IN orders
( %key = order-%key
%param = VALUE #(
purchaseorderid = order-PurchaseOrderId
approvedby = order-ApprovedBy
approvedat = order-ApprovedAt
totalamount = order-TotalAmount ) ) ).
" Result zurückgeben
result = VALUE #( FOR order IN orders
( %tky = order-%tky
%param = order ) ).
ENDMETHOD.
ENDCLASS.

Best Practices

ThemaEmpfehlung
Event-GranularitätEin Event pro Geschäftsereignis, nicht zu feingranular
IdempotenzConsumer müssen doppelte Events verarbeiten können
Event-SchemaVersionierung beachten (v1, v2), Breaking Changes vermeiden
ParameterNur notwendige Daten im Event, nicht gesamte Entität
Error HandlingDead-Letter-Queue für fehlgeschlagene Events konfigurieren
MonitoringSAP Event Mesh Dashboard für Metriken nutzen
TestingLokale Events testen vor Produktiv-Integration
SecuritySeparate Service Keys für Entwicklung und Produktion

Weiterführende Themen