L’architecture Event-Driven (EDA) est un pattern architectural qui découple les systèmes par des événements asynchrones. Dans ABAP Cloud, les RAP Business Events constituent la base de l’EDA – combinés avec SAP Event Mesh, ils permettent de créer des architectures performantes, évolutives et maintenables.
Qu’est-ce que l’Architecture Event-Driven ?
L’Architecture Event-Driven (EDA) est un paradigme de conception où les composants communiquent via des événements plutôt que par des appels de méthode directs :
┌─────────────────────────────────────────────────────────────────────────┐│ Event-Driven Architecture │├─────────────────────────────────────────────────────────────────────────┤│ ││ ┌──────────────┐ Event ┌─────────────────────────────────┐ ││ │ Producer │─────────────────>│ Event Broker │ ││ │ (ABAP BO) │ │ │ ││ └──────────────┘ │ ┌─────────┐ ┌─────────┐ │ ││ │ │ Topic 1 │ │ Topic 2 │ ... │ ││ │ └────┬────┘ └────┬────┘ │ ││ │ │ │ │ ││ └───────┼─────────────┼───────────┘ ││ │ │ ││ ┌─────────────┼─────────────┼───────────┐ ││ │ │ │ │ ││ ▼ ▼ ▼ │ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ││ │Consumer 1│ │Consumer 2│ │Consumer 3│ │ ││ │ (Email) │ │(Analytics│ │ (Extern) │ │ ││ └──────────┘ └──────────┘ └──────────┘ │ │└─────────────────────────────────────────────────────────────────────────┘Principes fondamentaux
| Principe | Description |
|---|---|
| Couplage faible | Le Producer ne connaît pas le Consumer – les deux sont indépendants |
| Asynchronicité | Le Producer n’attend pas le traitement du Consumer |
| Fire & Forget | L’événement est déclenché, le traitement est la responsabilité du Consumer |
| Cohérence à terme | Les données deviennent cohérentes ultérieurement, pas immédiatement |
| Résilience | Les erreurs du Consumer n’affectent pas le Producer |
EDA vs. Request-Response
| Aspect | Request-Response | Event-Driven |
|---|---|---|
| Communication | Synchrone | Asynchrone |
| Couplage | Fort | Faible |
| Disponibilité | Les deux doivent être en ligne | Découplé par le Broker |
| Tolérance aux pannes | Défaillances en cascade | Erreurs isolées |
| Évolutivité | Limitée par les goulots d’étranglement | Évolutivité horizontale |
| Latence | Déterminée | Variable |
| Débogage | Plus simple (Stack Trace) | Plus complexe (Event Trace) |
Composants EDA dans ABAP Cloud
ABAP Cloud offre plusieurs mécanismes pour l’EDA :
1. RAP Business Events (Local)
Les RAP Business Events permettent une communication basée sur les événements au sein d’un système ABAP :
define behavior for ZI_Order alias Orderpersistent table zorderlock masterauthorization master ( instance ){ create; update; delete;
// Définitions d'événements 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;}Avantages :
- Définition simple dans le BDEF
- Paramètres typés via Abstract Entities
- Distribution gérée par le framework
2. SAP Event Mesh (Distribué)
SAP Event Mesh étend l’EDA aux systèmes distribués :
┌─────────────────────────────────────────────────────────────────────────┐│ SAP Event Mesh │├─────────────────────────────────────────────────────────────────────────┤│ ││ ABAP Cloud Event Mesh Systèmes Consumer ││ ┌───────────┐ ┌──────────────┐ ┌───────────────────────┐ ││ │ RAP │ │ Message │ │ ABAP Cloud (autre) │ ││ │ Business │─────>│ Broker │──────>│ Application CAP │ ││ │ Events │ │ │ │ S/4HANA Cloud │ ││ └───────────┘ └──────────────┘ │ Integration Suite │ ││ │ Systèmes externes │ ││ └───────────────────────┘ │└─────────────────────────────────────────────────────────────────────────┘Avantages :
- Communication inter-systèmes
- Files d’attente persistantes (retry en cas d’erreur)
- Standard CloudEvents
- Monitoring et alerting
Implémenter un Event Producer
Un Event Producer est un Business Object qui déclenche des événements lors de changements d’état.
Définir les paramètres d’événement
@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);}Déclencher des événements dans les Actions
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. " Changer le statut 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.
" Lire les données mises à jour READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Déclencher l'événement 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 ) ) ).
" Retourner le résultat result = VALUE #( FOR order IN orders ( %tky = order-%tky %param = order ) ). ENDMETHOD.
METHOD ship. " Générer le numéro de suivi DATA(lv_tracking) = |TRK-{ sy-datum }-{ sy-uzeit }|.
" Changer le statut 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).
" Déclencher l'événement d'expédition 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. " Changer le statut 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).
" Déclencher l'événement d'annulation 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.Déclencher des événements dans les Determinations
Pour les événements automatiques lors de modifications de champs :
define behavior for ZI_Order alias Orderpersistent table zorder{ create; update;
// Événement à la création event orderCreated;
// Événement lors de modification du montant event amountChanged parameter ZA_AmountChangedEvent;
// Determinations pour les événements automatiques determination raiseCreatedEvent on save { create; } determination raiseAmountChangedEvent on save { field TotalAmount; }}CLASS lhc_order IMPLEMENTATION.
METHOD raiseCreatedEvent. " Lire les nouvelles entités READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Événement Created pour toutes les nouvelles commandes RAISE ENTITY EVENT zi_order~orderCreated FROM VALUE #( FOR order IN orders ( %key = order-%key ) ). ENDMETHOD.
METHOD raiseAmountChangedEvent. " Lire les entités modifiées READ ENTITIES OF zi_order IN LOCAL MODE ENTITY Order FIELDS ( OrderId TotalAmount Currency ) WITH CORRESPONDING #( keys ) RESULT DATA(orders).
" Événement Amount Changed 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.Implémenter un Event Consumer
Les Consumers réagissent aux événements et exécutent des actions de suivi.
Handler d’événement local
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. Notification client TRY. " Charger les données client 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 = |Confirmation de commande { ls_event-%param-orderid }| iv_body = |Cher/Chère { ls_customer-name },| && |\n\nVotre commande a été confirmée.| && |\nNuméro de commande : { ls_event-%param-orderid }| && |\nMontant : { 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 = |Erreur e-mail : { lx_error->get_text( ) }| ). ENDTRY.
" 2. Tracking analytique 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. Entrée de log log_event( iv_event_type = 'ORDERCONFIRMED" iv_entity_key = ls_event-%param-orderid iv_message = |Commande confirmée par { ls_event-%param-confirmedby }| ). ENDLOOP. ENDMETHOD.
METHOD handle_order_shipped. LOOP AT it_events INTO DATA(ls_event). " Notification d'expédition 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 = |Votre commande a été expédiée| iv_body = |Numéro de suivi : { 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 = |Échec de la notification d'expédition : { lx_error->get_text( ) }| ). ENDTRY. ENDLOOP. ENDMETHOD.
METHOD handle_order_cancelled. LOOP AT it_events INTO DATA(ls_event). " 1. Initier le remboursement 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 = |Erreur de remboursement : { lx_error->get_text( ) }| ). ENDTRY.
" 2. Libérer le stock réservé DATA(lo_inventory) = NEW zcl_inventory_service( ). lo_inventory->release_reservation( ls_event-%param-orderid ). ENDLOOP. ENDMETHOD.
METHOD send_email. " Envoyer l'e-mail via cl_bcs_mail_message DATA(lo_mail) = cl_bcs_mail_message=>create_instance( ). lo_mail->set_sender( '[email protected]' ). 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. " Ignorer les erreurs de logging ENDTRY. ENDMETHOD.
ENDCLASS.Plusieurs Consumers pour un événement
L’EDA permet plusieurs Consumers indépendants pour le même événement :
" Consumer 1 : Notifications par e-mailCLASS 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 : Synchronisation système externeCLASS zcl_external_sync_handler DEFINITION PUBLIC. PUBLIC SECTION. INTERFACES if_rap_event_handler.ENDCLASS.Chaque Consumer traite l’événement indépendamment – les erreurs dans un Consumer n’affectent pas les autres.
Intégration avec SAP Event Mesh
Pour une EDA inter-systèmes, utilisez SAP Event Mesh comme Message Broker.
Publier un événement vers Event Mesh
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 basé sur le type d'événement DATA(lv_topic) = |sap/order/{ to_lower( lv_event_name ) }/v1|.
LOOP AT lt_events INTO DATA(ls_event). " Formater l'événement en JSON CloudEvents 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). " Logger l'erreur mais continuer le traitement log_publish_error( lx_error ). ENDTRY. ENDLOOP. ENDMETHOD.
METHOD publish_to_event_mesh. " Destination depuis le 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( ).
" Header CloudEvents 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 vers le Topic Event Mesh 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.Format CloudEvents
SAP Event Mesh utilise le standard CloudEvents :
{ "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.Consommer des événements depuis Event Mesh
Les événements externes peuvent être consommés via des webhooks HTTP ou l’API REST Event Mesh.
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. " Accepter uniquement POST 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 ).
" Succès response->set_status( i_code = 200 i_reason = 'OK' ).
CATCH cx_root INTO DATA(lx_error). " Logger l'erreur response->set_status( i_code = 500 i_reason = lx_error->get_text( ) ). ENDTRY. ENDMETHOD.
METHOD process_cloud_event. " Parser le CloudEvent 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 ).
" Évaluer le type d'événement CASE ls_event-type. WHEN 'sap.inventory.stockupdated.v1'. " Mettre à jour le stock process_stock_update( ls_event-data ).
WHEN 'sap.customer.updated.v1'. " Synchroniser les données client sync_customer_data( ls_event-data ). ENDCASE. ENDMETHOD.
ENDCLASS.Patterns EDA
Pattern 1 : Event Sourcing
Stocker tous les changements d’état sous forme d’événements :
" Chaque modification est persistée comme événementCLASS 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. " Charger tous les événements pour cet agrégat chronologiquement SELECT * FROM zorder_events WHERE aggregate_id = @iv_aggregate_id ORDER BY sequence_nr INTO TABLE @DATA(lt_events).
" Reconstruire l'état par replay 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 : Pattern Saga
Coordination de transactions distribuées via événements :
" Order Saga : Commande → Paiement → ExpéditionCLASS 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'. " Étape 1 : Initier le paiement initiate_payment( io_event->get_business_data( ) ).
WHEN 'PAYMENTCOMPLETED'. " Étape 2 : Initier l'expédition initiate_shipment( io_event->get_business_data( ) ).
WHEN 'PAYMENTFAILED'. " Compensation : Annuler la commande compensate_order( io_event->get_business_data( ) ).
WHEN 'SHIPMENTCOMPLETED'. " Saga terminée complete_saga( io_event->get_business_data( ) ). ENDCASE. ENDMETHOD.
ENDCLASS.Pattern 3 : Agrégation d’événements
Combiner plusieurs événements en un seul :
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. " Ajouter le chiffre d'affaires depuis les données d'événement 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.
" Vérifier si l'événement résumé est dû check_and_publish_summary( ). ENDMETHOD.
METHOD check_and_publish_summary. " Par ex. tous les 100 événements ou une fois par heure IF gv_order_count MOD 100 = 0. " Déclencher l'événement résumé 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
Stocker séparément les événements en erreur :
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. " Succès, événement suivant
CATCH cx_root INTO DATA(lx_error). lv_retry_count = lv_retry_count + 1.
IF lv_retry_count >= c_max_retries. " Max retries atteint → 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. " Attendre avant 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" ).
" Déclencher une alerte 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 = |Événement échoué après { iv_retry_count } tentatives : { iv_error_msg }| ) )->save( ). ENDMETHOD.
ENDCLASS.Garantir l’idempotence
Les événements peuvent être délivrés plusieurs fois. Les Consumers doivent être idempotents :
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). " ID d'événement unique (par ex. depuis CloudEvents) DATA(lv_event_id) = ls_event-%param-event_id.
" Vérifier si déjà traité IF is_event_processed( lv_event_id ). " Ignorer - déjà traité CONTINUE. ENDIF.
" Traiter l'événement TRY. process_event( ls_event ).
" Marquer comme traité mark_event_processed( lv_event_id ).
CATCH cx_root INTO DATA(lx_error). " Gestion des erreurs 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 et dépannage
Monitoring des événements
" Requête de monitoring pour le traitement des événementsSELECT 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).Traçage des événements
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.Bonnes pratiques
| Domaine | Recommandation |
|---|---|
| Conception d’événement | Utiliser le passé (orderCreated, pas createOrder) |
| Payload | Uniquement les données nécessaires, pas d’entités complètes |
| Idempotence | Chaque événement doit pouvoir être traité plusieurs fois en toute sécurité |
| Tolérance aux pannes | Les erreurs du Consumer ne doivent pas affecter le Producer |
| Versionnement | Ne jamais casser le schéma d’événement, uniquement l’étendre (v1, v2) |
| Monitoring | Logger tous les événements, collecter des métriques |
| Dead Letter Queue | Stocker séparément les événements échoués |
| Tests | Tester les événements et les handlers séparément |
| Documentation | Maintenir un catalogue d’événements avec schéma et description |
| Sécurité | Sécuriser Event Mesh avec OAuth 2.0 |
Sujets connexes
- RAP Business Events - Définir et déclencher des événements dans RAP
- SAP Event Mesh avec ABAP Cloud - Configuration et intégration Event Mesh
- RAP Actions et Functions - Actions comme déclencheurs d’événements
- Destination Service - Connecter des systèmes externes