Event-Driven Architecture (EDA) ist ein Architekturmuster, das Systeme durch asynchrone Events entkoppelt. In ABAP Cloud bilden RAP Business Events die Basis für EDA – kombiniert mit SAP Event Mesh entstehen leistungsfähige, skalierbare und wartbare Architekturen.
Was ist Event-Driven Architecture?
Event-Driven Architecture (EDA) ist ein Designparadigma, bei dem Komponenten über Events kommunizieren statt über direkte Methodenaufrufe:
┌─────────────────────────────────────────────────────────────────────────┐│ Event-Driven Architecture │├─────────────────────────────────────────────────────────────────────────┤│ ││ ┌──────────────┐ Event ┌─────────────────────────────────┐ ││ │ Producer │─────────────────>│ Event Broker │ ││ │ (ABAP BO) │ │ │ ││ └──────────────┘ │ ┌─────────┐ ┌─────────┐ │ ││ │ │ Topic 1 │ │ Topic 2 │ ... │ ││ │ └────┬────┘ └────┬────┘ │ ││ │ │ │ │ ││ └───────┼─────────────┼───────────┘ ││ │ │ ││ ┌─────────────┼─────────────┼───────────┐ ││ │ │ │ │ ││ ▼ ▼ ▼ │ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ││ │Consumer 1│ │Consumer 2│ │Consumer 3│ │ ││ │ (Email) │ │(Analytics│ │ (Extern) │ │ ││ └──────────┘ └──────────┘ └──────────┘ │ │└─────────────────────────────────────────────────────────────────────────┘Kernprinzipien
| Prinzip | Beschreibung |
|---|---|
| Loose Coupling | Producer kennt Consumer nicht – beide sind unabhängig |
| Asynchronität | Producer wartet nicht auf Consumer-Verarbeitung |
| Fire & Forget | Event wird ausgelöst, Verarbeitung ist Aufgabe des Consumers |
| Eventual Consistency | Daten werden letztlich konsistent, nicht sofort |
| Resilienz | Consumer-Fehler beeinflussen Producer nicht |
EDA vs. Request-Response
| Aspekt | Request-Response | Event-Driven |
|---|---|---|
| Kommunikation | Synchron | Asynchron |
| Kopplung | Eng | Lose |
| Verfügbarkeit | Beide müssen online sein | Entkoppelt durch Broker |
| Fehlertoleranz | Cascading Failures | Isolierte Fehler |
| Skalierbarkeit | Limitiert durch Engpässe | Horizontal skalierbar |
| Latenz | Determiniert | Variabel |
| Debugging | Einfacher (Stack Trace) | Komplexer (Event Trace) |
EDA-Bausteine in ABAP Cloud
ABAP Cloud bietet mehrere Mechanismen für EDA:
1. RAP Business Events (Lokal)
RAP Business Events ermöglichen event-basierte Kommunikation innerhalb eines ABAP-Systems:
define behavior for ZI_Order alias Orderpersistent table zorderlock masterauthorization master ( instance ){ create; update; delete;
// Event-Definitionen event orderCreated; event orderConfirmed parameter ZA_OrderConfirmedEvent; event orderShipped parameter ZA_OrderShippedEvent; event orderCancelled parameter ZA_OrderCancelledEvent;
action confirm result [1] $self; action ship result [1] $self; action cancel result [1] $self;}Vorteile:
- Einfache Definition in BDEF
- Typsichere Parameter über Abstract Entities
- Framework-gestützte Auslieferung
2. SAP Event Mesh (Verteilt)
SAP Event Mesh erweitert EDA auf verteilte Systeme:
┌─────────────────────────────────────────────────────────────────────────┐│ SAP Event Mesh │├─────────────────────────────────────────────────────────────────────────┤│ ││ ABAP Cloud Event Mesh Consumer Systeme ││ ┌───────────┐ ┌──────────────┐ ┌───────────────────────┐ ││ │ RAP │ │ Message │ │ ABAP Cloud (anderes) │ ││ │ Business │─────>│ Broker │──────>│ CAP Application │ ││ │ Events │ │ │ │ S/4HANA Cloud │ ││ └───────────┘ └──────────────┘ │ Integration Suite │ ││ │ Externe Systeme │ ││ └───────────────────────┘ │└─────────────────────────────────────────────────────────────────────────┘Vorteile:
- System-übergreifende Kommunikation
- Persistente Queues (Retry bei Fehlern)
- CloudEvents-Standard
- Monitoring und Alerting
Event Producer implementieren
Ein Event Producer ist ein Business Object, das Events bei Zustandsänderungen auslöst.
Event-Parameter definieren
@EndUserText.label: 'Order Confirmed Event'define abstract entity ZA_OrderConfirmedEvent{ OrderId : abap.char(10); CustomerId : abap.char(10); ConfirmedBy : abap.uname; ConfirmedAt : timestampl; TotalAmount : abap.dec(15,2); Currency : abap.cuky;}
@EndUserText.label: 'Order Shipped Event'define abstract entity ZA_OrderShippedEvent{ OrderId : abap.char(10); CustomerId : abap.char(10); ShippedAt : timestampl; TrackingNumber : abap.char(30); CarrierId : abap.char(10);}
@EndUserText.label: 'Order Cancelled Event'define abstract entity ZA_OrderCancelledEvent{ OrderId : abap.char(10); CustomerId : abap.char(10); CancelledBy : abap.uname; CancelledAt : timestampl; CancellationReason: abap.char(255);}Events in Actions auslösen
CLASS lhc_order DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS confirm FOR MODIFY IMPORTING keys FOR ACTION Order~confirm RESULT result.
METHODS ship FOR MODIFY IMPORTING keys FOR ACTION Order~ship RESULT result.
METHODS cancel FOR MODIFY IMPORTING keys FOR ACTION Order~cancel RESULT result.ENDCLASS.
CLASS lhc_order IMPLEMENTATION.
METHOD confirm. " Status ändern MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY Order UPDATE FIELDS ( Status ConfirmedBy ConfirmedAt ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'CF' ConfirmedBy = cl_abap_context_info=>get_user_name( ) ConfirmedAt = utclong_current( ) ) ) FAILED failed REPORTED reported.
" Aktualisierte Daten lesen READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Event auslösen RAISE ENTITY EVENT zi_order~orderConfirmed FROM VALUE #( FOR order IN orders ( %key = order-%key %param = VALUE #( orderid = order-OrderId customerid = order-CustomerId confirmedby = order-ConfirmedBy confirmedat = order-ConfirmedAt totalamount = order-TotalAmount currency = order-Currency ) ) ).
" Result zurückgeben result = VALUE #( FOR order IN orders ( %tky = order-%tky %param = order ) ). ENDMETHOD.
METHOD ship. " Tracking-Nummer generieren DATA(lv_tracking) = |TRK-{ sy-datum }-{ sy-uzeit }|.
" Status ändern MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY Order UPDATE FIELDS ( Status ShippedAt TrackingNumber ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'SH' ShippedAt = utclong_current( ) TrackingNumber = lv_tracking ) ) FAILED failed REPORTED reported.
READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Shipping Event auslösen RAISE ENTITY EVENT zi_order~orderShipped FROM VALUE #( FOR order IN orders ( %key = order-%key %param = VALUE #( orderid = order-OrderId customerid = order-CustomerId shippedat = order-ShippedAt trackingnumber = order-TrackingNumber carrierid = order-CarrierId ) ) ).
result = VALUE #( FOR order IN orders ( %tky = order-%tky %param = order ) ). ENDMETHOD.
METHOD cancel. " Status ändern MODIFY ENTITIES OF zi_order IN LOCAL MODE ENTITY Order UPDATE FIELDS ( Status CancelledBy CancelledAt ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'CA' CancelledBy = cl_abap_context_info=>get_user_name( ) CancelledAt = utclong_current( ) ) ) FAILED failed REPORTED reported.
READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Cancel Event auslösen RAISE ENTITY EVENT zi_order~orderCancelled FROM VALUE #( FOR order IN orders ( %key = order-%key %param = VALUE #( orderid = order-OrderId customerid = order-CustomerId cancelledby = order-CancelledBy cancelledat = order-CancelledAt cancellationreason = keys[ 1 ]-%param-reason ) ) ).
result = VALUE #( FOR order IN orders ( %tky = order-%tky %param = order ) ). ENDMETHOD.
ENDCLASS.Events in Determinations auslösen
Für automatische Events bei Feldänderungen:
define behavior for ZI_Order alias Orderpersistent table zorder{ create; update;
// Event bei Create event orderCreated;
// Event bei Betragsänderung event amountChanged parameter ZA_AmountChangedEvent;
// Determinations für automatische Events determination raiseCreatedEvent on save { create; } determination raiseAmountChangedEvent on save { field TotalAmount; }}CLASS lhc_order IMPLEMENTATION.
METHOD raiseCreatedEvent. " Neue Entities lesen READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Created Event für alle neuen Orders RAISE ENTITY EVENT zi_order~orderCreated FROM VALUE #( FOR order IN orders ( %key = order-%key ) ). ENDMETHOD.
METHOD raiseAmountChangedEvent. " Geänderte Entities lesen READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order FIELDS ( OrderId TotalAmount Currency ) WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Amount Changed Event RAISE ENTITY EVENT zi_order~amountChanged FROM VALUE #( FOR order IN orders ( %key = order-%key %param = VALUE #( orderid = order-OrderId newamount = order-TotalAmount currency = order-Currency ) ) ). ENDMETHOD.
ENDCLASS.Event Consumer implementieren
Consumer reagieren auf Events und führen Folgeaktionen aus.
Lokaler Event Handler
CLASS zcl_order_event_handler DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_event_handler.
PRIVATE SECTION. METHODS handle_order_confirmed IMPORTING it_events TYPE STANDARD TABLE.
METHODS handle_order_shipped IMPORTING it_events TYPE STANDARD TABLE.
METHODS handle_order_cancelled IMPORTING it_events TYPE STANDARD TABLE.
METHODS send_email IMPORTING iv_recipient TYPE string iv_subject TYPE string iv_body TYPE string.
METHODS log_event IMPORTING iv_event_type TYPE string iv_entity_key TYPE string iv_message TYPE string.ENDCLASS.
CLASS zcl_order_event_handler IMPLEMENTATION.
METHOD if_rap_event_handler~handle. CASE io_event->get_event_name( ). WHEN 'ORDERCONFIRMED'. handle_order_confirmed( io_event->get_business_data( ) ).
WHEN 'ORDERSHIPPED'. handle_order_shipped( io_event->get_business_data( ) ).
WHEN 'ORDERCANCELLED'. handle_order_cancelled( io_event->get_business_data( ) ). ENDCASE. ENDMETHOD.
METHOD handle_order_confirmed. LOOP AT it_events INTO DATA(ls_event). " 1. Kundenbenachrichtigung TRY. " Kundendaten laden SELECT SINGLE email, name FROM zcustomer WHERE customer_id = @ls_event-%param-customerid INTO @DATA(ls_customer).
IF sy-subrc = 0. send_email( iv_recipient = ls_customer-email iv_subject = |Bestellbestätigung { ls_event-%param-orderid }| iv_body = |Sehr geehrte/r { ls_customer-name },| && |\n\nIhre Bestellung wurde bestätigt.| && |\nBestellnummer: { ls_event-%param-orderid }| && |\nBetrag: { ls_event-%param-totalamount } { ls_event-%param-currency }| ). ENDIF.
CATCH cx_root INTO DATA(lx_error). log_event( iv_event_type = 'ORDERCONFIRMED' iv_entity_key = ls_event-%param-orderid iv_message = |Email-Fehler: { lx_error->get_text( ) }| ). ENDTRY.
" 2. Analytics tracken INSERT INTO zorder_analytics VALUES ( analytics_id = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ) order_id = ls_event-%param-orderid event_type = 'CONFIRMED' event_time = utclong_current( ) amount = ls_event-%param-totalamount ).
" 3. Log-Eintrag log_event( iv_event_type = 'ORDERCONFIRMED' iv_entity_key = ls_event-%param-orderid iv_message = |Order confirmed by { ls_event-%param-confirmedby }| ). ENDLOOP. ENDMETHOD.
METHOD handle_order_shipped. LOOP AT it_events INTO DATA(ls_event). " Versandbenachrichtigung TRY. SELECT SINGLE email, name FROM zcustomer WHERE customer_id = @ls_event-%param-customerid INTO @DATA(ls_customer).
IF sy-subrc = 0. send_email( iv_recipient = ls_customer-email iv_subject = |Ihre Bestellung wurde versendet| iv_body = |Tracking-Nummer: { ls_event-%param-trackingnumber }| ). ENDIF.
CATCH cx_root INTO DATA(lx_error). log_event( iv_event_type = 'ORDERSHIPPED' iv_entity_key = ls_event-%param-orderid iv_message = |Versandbenachrichtigung fehlgeschlagen: { lx_error->get_text( ) }| ). ENDTRY. ENDLOOP. ENDMETHOD.
METHOD handle_order_cancelled. LOOP AT it_events INTO DATA(ls_event). " 1. Rückerstattung initiieren TRY. DATA(lo_refund) = NEW zcl_refund_service( ). lo_refund->initiate_refund( ls_event-%param-orderid ).
CATCH cx_root INTO DATA(lx_error). log_event( iv_event_type = 'ORDERCANCELLED' iv_entity_key = ls_event-%param-orderid iv_message = |Refund-Fehler: { lx_error->get_text( ) }| ). ENDTRY.
" 2. Lagerbestand zurückbuchen DATA(lo_inventory) = NEW zcl_inventory_service( ). lo_inventory->release_reservation( ls_event-%param-orderid ). ENDLOOP. ENDMETHOD.
METHOD send_email. " Email via cl_bcs_mail_message senden DATA(lo_mail) = cl_bcs_mail_message=>create_instance( ). lo_mail->add_recipient( iv_recipient ). lo_mail->set_subject( iv_subject ). lo_mail->set_main( cl_bcs_mail_textpart=>create_instance( iv_content = iv_body iv_mimetype = 'text/plain' ) ). lo_mail->send( ). ENDMETHOD.
METHOD log_event. TRY. DATA(lo_log) = cl_bali_log=>create_with_header( cl_bali_header_setter=>create( iv_object = 'ZORDER_EVENTS' iv_subobject = iv_event_type iv_external_id = iv_entity_key ) ).
lo_log->add_item( cl_bali_free_text_setter=>create( severity = if_bali_constants=>c_severity_information text = iv_message ) ).
cl_bali_log_db=>get_instance( )->save_log( lo_log ).
CATCH cx_bali_runtime. " Logging-Fehler ignorieren ENDTRY. ENDMETHOD.
ENDCLASS.Mehrere Consumer für ein Event
EDA ermöglicht mehrere unabhängige Consumer für dasselbe Event:
" Consumer 1: Email-BenachrichtigungenCLASS zcl_notification_handler DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.ENDCLASS.
" Consumer 2: AnalyticsCLASS zcl_analytics_handler DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.ENDCLASS.
" Consumer 3: Audit TrailCLASS zcl_audit_handler DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.ENDCLASS.
" Consumer 4: External System SyncCLASS zcl_external_sync_handler DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.ENDCLASS.Jeder Consumer verarbeitet das Event unabhängig – Fehler in einem Consumer beeinflussen die anderen nicht.
Integration mit SAP Event Mesh
Für system-übergreifende EDA nutzt du SAP Event Mesh als Message Broker.
Event nach Event Mesh publizieren
CLASS zcl_event_mesh_publisher DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_rap_event_handler.
PRIVATE SECTION. METHODS publish_to_event_mesh IMPORTING iv_topic TYPE string iv_payload TYPE string RAISING cx_http_dest_provider_error cx_web_http_client_error.ENDCLASS.
CLASS zcl_event_mesh_publisher IMPLEMENTATION.
METHOD if_rap_event_handler~handle. DATA(lv_event_name) = io_event->get_event_name( ). DATA(lt_events) = io_event->get_business_data( ).
" Topic basierend auf Event-Typ DATA(lv_topic) = |sap/order/{ to_lower( lv_event_name ) }/v1|.
LOOP AT lt_events INTO DATA(ls_event). " Event als CloudEvents-JSON formatieren DATA(lv_payload) = format_cloud_event( iv_type = lv_event_name is_data = ls_event ).
TRY. publish_to_event_mesh( iv_topic = lv_topic iv_payload = lv_payload ).
CATCH cx_root INTO DATA(lx_error). " Fehler loggen, aber weiter verarbeiten log_publish_error( lx_error ). ENDTRY. ENDLOOP. ENDMETHOD.
METHOD publish_to_event_mesh. " Destination aus Communication Arrangement DATA(lo_dest) = cl_http_destination_provider=>create_by_comm_arrangement( comm_scenario = 'Z_EVENT_MESH' service_id = 'Z_EVENT_MESH_PUBLISH' ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( lo_dest ). DATA(lo_request) = lo_client->get_http_request( ).
" CloudEvents Header lo_request->set_header_field( i_name = 'Content-Type' i_value = 'application/cloudevents+json' ). lo_request->set_header_field( i_name = 'x-qos' i_value = '1' ). " At least once
lo_request->set_text( iv_payload ).
" POST an Event Mesh Topic DATA(lo_response) = lo_client->execute( i_method = if_web_http_client=>post i_uri = |/messagingrest/v1/topics/{ iv_topic }/messages| ).
IF lo_response->get_status( )-code <> 204. RAISE EXCEPTION TYPE cx_web_http_client_error. ENDIF. ENDMETHOD.
ENDCLASS.CloudEvents-Format
SAP Event Mesh nutzt den CloudEvents-Standard:
{ "specversion": "1.0", "type": "sap.order.orderconfirmed.v1", "source": "/sap/abap/ZI_ORDER", "id": "550e8400-e29b-41d4-a716-446655440000", "time": "2026-02-14T10:30:00Z", "datacontenttype": "application/json", "data": { "orderId": "0000000042", "customerId": "CUST001", "confirmedBy": "JSMITH", "confirmedAt": "2026-02-14T10:30:00Z", "totalAmount": 1500.00, "currency": "EUR" }}METHOD format_cloud_event. DATA(lv_uuid) = cl_uuid_factory=>create_system_uuid( )->create_uuid_c32( ). DATA(lv_timestamp) = format_timestamp( utclong_current( ) ).
DATA(lv_data_json) = /ui2/cl_json=>serialize( data = is_data ).
rv_json = |\{| && |"specversion":"1.0",| && |"type":"sap.order.{ to_lower( iv_type ) }.v1",| && |"source":"/sap/abap/ZI_ORDER",| && |"id":"{ lv_uuid }",| && |"time":"{ lv_timestamp }",| && |"datacontenttype":"application/json",| && |"data":{ lv_data_json }| && |\}|.ENDMETHOD.Events von Event Mesh konsumieren
Externe Events können über HTTP-Webhooks oder die Event Mesh REST API konsumiert werden.
CLASS zcl_event_mesh_consumer DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. INTERFACES if_http_service_extension.
PRIVATE SECTION. METHODS process_cloud_event IMPORTING iv_payload TYPE string RAISING cx_root.ENDCLASS.
CLASS zcl_event_mesh_consumer IMPLEMENTATION.
METHOD if_http_service_extension~handle_request. " Nur POST akzeptieren IF request->get_method( ) <> 'POST'. response->set_status( i_code = 405 i_reason = 'Method Not Allowed' ). RETURN. ENDIF.
TRY. DATA(lv_payload) = request->get_text( ). process_cloud_event( lv_payload ).
" Erfolg response->set_status( i_code = 200 i_reason = 'OK' ).
CATCH cx_root INTO DATA(lx_error). " Fehler loggen response->set_status( i_code = 500 i_reason = lx_error->get_text( ) ). ENDTRY. ENDMETHOD.
METHOD process_cloud_event. " CloudEvent parsen DATA: BEGIN OF ls_event, type TYPE string, data TYPE string, END OF ls_event.
/ui2/cl_json=>deserialize( EXPORTING json = iv_payload CHANGING data = ls_event ).
" Event-Typ auswerten CASE ls_event-type. WHEN 'sap.inventory.stockupdated.v1'. " Lagerbestand aktualisieren process_stock_update( ls_event-data ).
WHEN 'sap.customer.updated.v1'. " Kundenstamm synchronisieren sync_customer_data( ls_event-data ). ENDCASE. ENDMETHOD.
ENDCLASS.EDA-Patterns
Pattern 1: Event Sourcing
Alle Zustandsänderungen als Events speichern:
" Jede Änderung wird als Event persistiertCLASS zcl_order_event_store DEFINITION PUBLIC. PUBLIC SECTION. METHODS append_event IMPORTING iv_aggregate_id TYPE string iv_event_type TYPE string is_event_data TYPE any.
METHODS replay_events IMPORTING iv_aggregate_id TYPE string RETURNING VALUE(rs_state) TYPE zs_order_state.ENDCLASS.
CLASS zcl_order_event_store IMPLEMENTATION.
METHOD append_event. INSERT INTO zorder_events VALUES ( event_id = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ) aggregate_id = iv_aggregate_id event_type = iv_event_type event_data = /ui2/cl_json=>serialize( is_event_data ) created_at = utclong_current( ) sequence_nr = get_next_sequence( iv_aggregate_id ) ). ENDMETHOD.
METHOD replay_events. " Alle Events für dieses Aggregat chronologisch laden SELECT * FROM zorder_events WHERE aggregate_id = @iv_aggregate_id ORDER BY sequence_nr INTO TABLE @DATA(lt_events).
" State durch Replay aufbauen LOOP AT lt_events INTO DATA(ls_event). CASE ls_event-event_type. WHEN 'CREATED'. apply_created( CHANGING cs_state = rs_state is_event = ls_event ). WHEN 'CONFIRMED'. apply_confirmed( CHANGING cs_state = rs_state is_event = ls_event ). WHEN 'SHIPPED'. apply_shipped( CHANGING cs_state = rs_state is_event = ls_event ). WHEN 'CANCELLED'. apply_cancelled( CHANGING cs_state = rs_state is_event = ls_event ). ENDCASE. ENDLOOP. ENDMETHOD.
ENDCLASS.Pattern 2: Saga Pattern
Koordination verteilter Transaktionen über Events:
" Order Saga: Bestellung → Zahlung → VersandCLASS zcl_order_saga DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.
PRIVATE SECTION. METHODS handle_order_created. METHODS handle_payment_completed. METHODS handle_payment_failed. METHODS handle_shipment_completed. METHODS compensate_order.ENDCLASS.
CLASS zcl_order_saga IMPLEMENTATION.
METHOD if_rap_event_handler~handle. CASE io_event->get_event_name( ). WHEN 'ORDERCREATED'. " Schritt 1: Zahlung initiieren initiate_payment( io_event->get_business_data( ) ).
WHEN 'PAYMENTCOMPLETED'. " Schritt 2: Versand initiieren initiate_shipment( io_event->get_business_data( ) ).
WHEN 'PAYMENTFAILED'. " Kompensation: Bestellung stornieren compensate_order( io_event->get_business_data( ) ).
WHEN 'SHIPMENTCOMPLETED'. " Saga abgeschlossen complete_saga( io_event->get_business_data( ) ). ENDCASE. ENDMETHOD.
ENDCLASS.Pattern 3: Event Aggregation
Mehrere Events zu einem zusammenfassen:
CLASS zcl_daily_summary_aggregator DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.
PRIVATE SECTION. CLASS-DATA: gv_order_count TYPE i, gv_total_revenue TYPE p DECIMALS 2, gv_cancellations TYPE i.
METHODS check_and_publish_summary.ENDCLASS.
CLASS zcl_daily_summary_aggregator IMPLEMENTATION.
METHOD if_rap_event_handler~handle. CASE io_event->get_event_name( ). WHEN 'ORDERCONFIRMED'. gv_order_count = gv_order_count + 1. " Revenue aus Event-Daten addieren LOOP AT io_event->get_business_data( ) INTO DATA(ls_event). gv_total_revenue = gv_total_revenue + ls_event-%param-totalamount. ENDLOOP.
WHEN 'ORDERCANCELLED'. gv_cancellations = gv_cancellations + 1. ENDCASE.
" Prüfen ob Summary-Event fällig check_and_publish_summary( ). ENDMETHOD.
METHOD check_and_publish_summary. " Z.B. alle 100 Events oder einmal pro Stunde IF gv_order_count MOD 100 = 0. " Summary-Event auslösen RAISE EVENT daily_summary EXPORTING iv_order_count = gv_order_count iv_total_revenue = gv_total_revenue iv_cancellations = gv_cancellations. ENDIF. ENDMETHOD.
ENDCLASS.Pattern 4: Dead Letter Queue
Fehlerhafte Events separat speichern:
CLASS zcl_event_handler_with_dlq DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.
PRIVATE SECTION. CONSTANTS c_max_retries TYPE i VALUE 3.
METHODS move_to_dead_letter_queue IMPORTING is_event TYPE any iv_error_msg TYPE string iv_retry_count TYPE i.ENDCLASS.
CLASS zcl_event_handler_with_dlq IMPLEMENTATION.
METHOD if_rap_event_handler~handle. LOOP AT io_event->get_business_data( ) INTO DATA(ls_event). DATA(lv_retry_count) = 0.
DO c_max_retries TIMES. TRY. process_event( ls_event ). EXIT. " Erfolgreich, nächstes Event
CATCH cx_root INTO DATA(lx_error). lv_retry_count = lv_retry_count + 1.
IF lv_retry_count >= c_max_retries. " Max Retries erreicht → Dead Letter Queue move_to_dead_letter_queue( is_event = ls_event iv_error_msg = lx_error->get_text( ) iv_retry_count = lv_retry_count ). ELSE. " Warten vor Retry (exponential backoff) WAIT UP TO ( 2 ** lv_retry_count ) SECONDS. ENDIF. ENDTRY. ENDDO. ENDLOOP. ENDMETHOD.
METHOD move_to_dead_letter_queue. INSERT INTO zdead_letter_queue VALUES ( dlq_id = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ) event_type = io_event->get_event_name( ) event_data = /ui2/cl_json=>serialize( is_event ) error_msg = iv_error_msg retry_count = iv_retry_count created_at = utclong_current( ) status = 'PENDING' ).
" Alert auslösen cl_bali_log=>create_with_header( cl_bali_header_setter=>create( iv_object = 'ZDLQ' iv_subobject = 'ERROR' ) )->add_item( cl_bali_free_text_setter=>create( severity = if_bali_constants=>c_severity_error text = |Event failed after { iv_retry_count } retries: { iv_error_msg }| ) )->save( ). ENDMETHOD.
ENDCLASS.Idempotenz sicherstellen
Events können mehrfach zugestellt werden. Consumer müssen idempotent sein:
CLASS zcl_idempotent_handler DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.
PRIVATE SECTION. METHODS is_event_processed IMPORTING iv_event_id TYPE string RETURNING VALUE(rv_result) TYPE abap_bool.
METHODS mark_event_processed IMPORTING iv_event_id TYPE string.ENDCLASS.
CLASS zcl_idempotent_handler IMPLEMENTATION.
METHOD if_rap_event_handler~handle. LOOP AT io_event->get_business_data( ) INTO DATA(ls_event). " Eindeutige Event-ID (z.B. aus CloudEvents) DATA(lv_event_id) = ls_event-%param-event_id.
" Prüfen ob bereits verarbeitet IF is_event_processed( lv_event_id ). " Skip - bereits verarbeitet CONTINUE. ENDIF.
" Event verarbeiten TRY. process_event( ls_event ).
" Als verarbeitet markieren mark_event_processed( lv_event_id ).
CATCH cx_root INTO DATA(lx_error). " Fehlerbehandlung log_error( lx_error ). ENDTRY. ENDLOOP. ENDMETHOD.
METHOD is_event_processed. SELECT SINGLE @abap_true FROM zprocessed_events WHERE event_id = @iv_event_id INTO @rv_result. ENDMETHOD.
METHOD mark_event_processed. INSERT INTO zprocessed_events VALUES ( event_id = iv_event_id processed_at = utclong_current( ) ). ENDMETHOD.
ENDCLASS.Monitoring und Troubleshooting
Event Monitoring
" Monitoring-Abfrage für Event-VerarbeitungSELECT event_type, COUNT(*) AS total_count, SUM( CASE WHEN status = 'SUCCESS' THEN 1 ELSE 0 END ) AS success_count, SUM( CASE WHEN status = 'FAILED' THEN 1 ELSE 0 END ) AS failed_count, AVG( processing_time_ms ) AS avg_processing_timeFROM zevent_logWHERE created_at >= @lv_today_startGROUP BY event_typeINTO TABLE @DATA(lt_metrics).Event Tracing
CLASS zcl_event_tracer DEFINITION PUBLIC. PUBLIC SECTION. CLASS-METHODS trace IMPORTING iv_event_id TYPE string iv_event_type TYPE string iv_phase TYPE string " PUBLISHED, RECEIVED, PROCESSING, COMPLETED, FAILED iv_details TYPE string OPTIONAL.ENDCLASS.
CLASS zcl_event_tracer IMPLEMENTATION. METHOD trace. INSERT INTO zevent_trace VALUES ( trace_id = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ) event_id = iv_event_id event_type = iv_event_type phase = iv_phase details = iv_details timestamp = utclong_current( ) user_name = cl_abap_context_info=>get_user_name( ) ). ENDMETHOD.ENDCLASS.Best Practices
| Bereich | Empfehlung |
|---|---|
| Event-Design | Vergangenheitsform verwenden (orderCreated, nicht createOrder) |
| Payload | Nur notwendige Daten, keine vollständigen Entitäten |
| Idempotenz | Jedes Event muss sicher mehrfach verarbeitbar sein |
| Fehlertoleranz | Consumer-Fehler dürfen Producer nicht beeinflussen |
| Versionierung | Event-Schema niemals brechen, nur erweitern (v1, v2) |
| Monitoring | Alle Events loggen, Metriken erfassen |
| Dead Letter Queue | Fehlgeschlagene Events separat speichern |
| Testing | Events und Handler getrennt testen |
| Dokumentation | Event-Katalog mit Schema und Beschreibung pflegen |
| Security | Event Mesh mit OAuth 2.0 absichern |
Weiterführende Themen
- RAP Business Events - Events in RAP definieren und auslösen
- SAP Event Mesh mit ABAP Cloud - Event Mesh Konfiguration und Integration
- RAP Actions und Functions - Actions als Event-Trigger
- Destination Service - Externe Systeme anbinden