RAP Business Events : Architecture événementielle avec ABAP Cloud

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

Les Business Events dans RAP permettent une architecture événementielle : au lieu d’appels de méthodes directs, les composants communiquent via des événements. Cela découple les systèmes, permet un traitement asynchrone et rend les architectures plus évolutives et maintenables.

Le problème : Couplage fort

Sans événements (Tight Coupling)

" ❌ Dépendance directe : Travel → Email → Logging
METHOD approve_travel.
" 1. Changer le statut
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = ls_travel-%tky Status = 'A' ) ).
" 2. Envoyer un email (dépendance directe !)
TRY.
cl_email_sender=>send(
recipient = ls_travel-customer_email
subject = 'Travel approved"
).
CATCH cx_email_error.
" Que faire en cas d'erreur ?
ENDTRY.
" 3. Journaliser (dépendance directe !)
cl_logger=>log( |Travel { ls_travel-TravelId } approved| ).
" 4. Mettre à jour Analytics (dépendance directe !)
zcl_analytics=>track_approval( ls_travel-TravelId ).
" 5. Notifier un système externe (dépendance directe !)
zcl_external_api=>notify_approval( ls_travel ).
ENDMETHOD.

Problèmes :

  • 🔴 Tight Coupling : La logique d’approbation connaît tous les Consumers
  • 🔴 Synchrone : Toutes les étapes bloquent le processus principal
  • 🔴 Non extensible : Nouveau Consumer = modification du code
  • 🔴 Fragile : Un Consumer en échec interrompt tout
  • 🔴 Lent : Email + API + Logging = 2-3 secondes

Avec événements (Loose Coupling)

" ✅ Événementiel : Travel → Event → Consumers (découplé)
METHOD approve_travel.
" 1. Changer le statut
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = ls_travel-%tky Status = 'A' ) ).
" 2. Déclencher l'événement (Fire & Forget)
RAISE ENTITY EVENT zi_travel~TravelApproved
FROM VALUE #( ( %key-TravelId = ls_travel-TravelId
%param-ApprovedBy = sy-uname
%param-ApprovedAt = sy-datum ) ).
" Terminé ! Les Consumers réagissent de manière asynchrone
ENDMETHOD.

Avantages :

  • Loose Coupling : La logique d’approbation ne connaît pas les Consumers
  • Asynchrone : Les Consumers traitent en parallèle
  • Extensible : Nouveaux Consumers sans modification du code
  • Résilient : Les erreurs des Consumers n’affectent pas le processus principal
  • Rapide : Le processus principal continue immédiatement (< 100ms)

Anatomie d’un événement

┌─────────────────────────────────────────────────────┐
│ Event Publisher │
│ (RAP Business Object) │
│ │
│ METHOD approve_travel. │
│ RAISE ENTITY EVENT zi_travel~TravelApproved ... │
│ ENDMETHOD. │
└────────────────┬────────────────────────────────────┘
│ Event: TravelApproved
│ Payload: { TravelId, ApprovedBy, ApprovedAt }
┌─────────────────────────────────────────────────────┐
│ Event Infrastructure │
│ (RAP Framework / Event Mesh) │
└────────┬────────────────────────────────┬──────────┘
│ │
▼ ▼
┌────────────────────┐ ┌────────────────────────┐
│ Consumer 1 │ │ Consumer 2 │
│ (Email) │ │ (Analytics) │
│ │ │ │
│ on_event( ). │ │ on_event( ). │
│ send_email( ) │ │ track_approval( ) │
└────────────────────┘ └────────────────────────┘
┌────────────────────┐
│ Consumer 3 │
│ (External API) │
│ │
│ on_event( ). │
│ notify_system( ) │
└────────────────────┘

Définition d’événement dans BDEF

Événement simple

define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
{
create;
update;
delete;
// Définition d'événement
event TravelApproved;
// Action qui déclenche l'événement
action approve result [1] $self;
}

Événement avec paramètres

define behavior for ZI_Travel alias Travel
{
// Événement avec structure de paramètres
event TravelApproved parameter ZA_TravelApprovedParam;
action approve result [1] $self;
}

Structure de paramètres (Abstract Entity) :

@EndUserText.label: 'Travel Approved Event Parameters"
define abstract entity ZA_TravelApprovedParam
{
TravelId : /dmo/travel_id;
ApprovedBy : syuname;
ApprovedAt : sydatum;
ApprovalNote : abap.string(255);
}

Événement avec paramètre standard

define behavior for ZI_Travel alias Travel
{
// Événement avec %param prédéfini
event TravelApproved parameter ZA_TravelApprovedParam;
// Ou : Seulement les champs clés (pas de paramètre explicite)
event TravelCancelled;
}

Déclenchement d’événements (Publisher)

Déclencher un événement dans une Action

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS approve FOR MODIFY
IMPORTING keys FOR ACTION Travel~approve RESULT result.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD approve.
" 1. Lire les données
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED failed
REPORTED reported.
" 2. Changer le statut
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status ApprovedBy ApprovedAt )
WITH VALUE #( FOR travel IN lt_travel
( %tky = travel-%tky
Status = 'A"
ApprovedBy = cl_abap_context_info=>get_user_name( )
ApprovedAt = cl_abap_context_info=>get_system_date( ) ) )
FAILED failed
REPORTED reported.
" 3. Déclencher l'événement
RAISE ENTITY EVENT zi_travel~TravelApproved
FROM VALUE #( FOR travel IN lt_travel
( %key-TravelId = travel-TravelId
%param-ApprovedBy = cl_abap_context_info=>get_user_name( )
%param-ApprovedAt = cl_abap_context_info=>get_system_date( )
%param-ApprovalNote = 'Approved via action' ) ).
" 4. Retourner le résultat
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT result.
ENDMETHOD.
ENDCLASS.

Événement dans Validation/Determination

METHOD validateDates.
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
FIELDS ( BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel).
LOOP AT lt_travel INTO DATA(ls_travel).
IF ls_travel-EndDate < ls_travel-BeginDate.
" Erreur de validation
APPEND VALUE #( %tky = ls_travel-%tky ) TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Invalid date range"
)
) TO reported-travel.
" Event: Déclencher ValidationFailed
RAISE ENTITY EVENT zi_travel~ValidationFailed
FROM VALUE #( ( %key-TravelId = ls_travel-TravelId
%param-ErrorType = 'DATE_RANGE"
%param-ErrorMessage = 'End date before begin date' ) ).
ENDIF.
ENDLOOP.
ENDMETHOD.

Plusieurs événements

define behavior for ZI_Travel alias Travel
{
// Différents événements pour différents scénarios
event TravelCreated;
event TravelApproved;
event TravelRejected parameter ZA_RejectionParam;
event TravelCancelled;
event TravelCompleted;
}
METHOD approve.
" ...
RAISE ENTITY EVENT zi_travel~TravelApproved FROM ...
ENDMETHOD.
METHOD reject.
" ...
RAISE ENTITY EVENT zi_travel~TravelRejected
FROM VALUE #( ( %key-TravelId = ls_travel-TravelId
%param-RejectionReason = ls_key-%param-Reason ) ).
ENDMETHOD.
METHOD cancel.
" ...
RAISE ENTITY EVENT zi_travel~TravelCancelled FROM ...
ENDMETHOD.

Consommation d’événements (Subscriber)

Implémenter Event Handler

CLASS zcl_travel_event_handler DEFINITION PUBLIC CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_travel_event_handler IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
" Évaluer l'event-key
CASE event_key.
WHEN 'TravelApproved'.
handle_travel_approved( event_data ).
WHEN 'TravelRejected'.
handle_travel_rejected( event_data ).
WHEN 'TravelCancelled'.
handle_travel_cancelled( event_data ).
ENDCASE.
ENDMETHOD.
" Méthodes privées pour la gestion des événements
METHODS:
handle_travel_approved
IMPORTING it_event_data TYPE STANDARD TABLE,
handle_travel_rejected
IMPORTING it_event_data TYPE STANDARD TABLE,
handle_travel_cancelled
IMPORTING it_event_data TYPE STANDARD TABLE.
ENDCLASS.

Enregistrer Event Handler

Service Definition :

@EndUserText.label: 'Travel Event Handler"
define service ZUI_TRAVEL_EVENTS {
expose ZI_Travel as Travel;
// Activer Event Handler
expose zcl_travel_event_handler as TravelEventHandler;
}

Ou : Enregistrement programmatique :

" Dans la classe d'initialisation ou au démarrage du système
DATA(lo_event_handler) = NEW zcl_travel_event_handler( ).
" Enregistrer le handler
cl_rap_event_handler=>register(
iv_event_name = 'TravelApproved"
io_handler = lo_event_handler
).

Traiter les données d’événement

METHOD handle_travel_approved.
" Les données d'événement sont une table (peut contenir plusieurs événements)
LOOP AT it_event_data INTO DATA(ls_event).
" Extraire %key et %param
DATA(lv_travel_id) = ls_event-%key-TravelId.
DATA(lv_approved_by) = ls_event-%param-ApprovedBy.
DATA(lv_approved_at) = ls_event-%param-ApprovedAt.
" 1. Envoyer un email
send_approval_email(
iv_travel_id = lv_travel_id
iv_approved_by = lv_approved_by
).
" 2. Suivre dans Analytics
track_approval_analytics(
iv_travel_id = lv_travel_id
iv_approved_at = lv_approved_at
).
" 3. Notifier le système externe
notify_external_system(
iv_travel_id = lv_travel_id
).
" 4. Journaliser
cl_bali_log=>create( )->add_item(
cl_bali_free_text_setter=>create(
severity = if_bali_constants=>c_severity_information
text = |Travel { lv_travel_id } approved by { lv_approved_by }|
)
)->save( ).
ENDLOOP.
ENDMETHOD.

Gestion des erreurs dans le Consumer

METHOD handle_travel_approved.
LOOP AT it_event_data INTO DATA(ls_event).
" Envoi d'email avec gestion des erreurs
TRY.
send_approval_email( ls_event-%key-TravelId ).
CATCH cx_email_error INTO DATA(lx_email).
" Journaliser l'erreur, mais NE PAS interrompre le traitement de l'événement
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_email )
)->save( ).
" Optionnel : Logique de retry
add_to_retry_queue(
iv_event_type = 'TravelApproved"
iv_travel_id = ls_event-%key-TravelId
).
ENDTRY.
" Appel API avec gestion des erreurs
TRY.
notify_external_system( ls_event-%key-TravelId ).
CATCH cx_http_error INTO DATA(lx_http).
" Journaliser l'erreur
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_http )
)->save( ).
ENDTRY.
ENDLOOP.
ENDMETHOD.

Cas d’usage pratiques

Cas d’usage 1 : Notification multi-canal

" Event: TravelApproved
" → Consumer 1: Email
" → Consumer 2: SMS
" → Consumer 3: Push Notification
" → Consumer 4: Slack
CLASS zcl_notification_handler DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_notification_handler IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
CHECK event_key = 'TravelApproved'.
LOOP AT event_data INTO DATA(ls_event).
DATA(lv_travel_id) = ls_event-%key-TravelId.
" Charger les préférences client
SELECT SINGLE * FROM zcustomer_prefs
WHERE travel_id = @lv_travel_id
INTO @DATA(ls_prefs).
" Notification multi-canal (parallèle)
IF ls_prefs-notify_email = abap_true.
send_email( lv_travel_id ).
ENDIF.
IF ls_prefs-notify_sms = abap_true.
send_sms( lv_travel_id ).
ENDIF.
IF ls_prefs-notify_push = abap_true.
send_push_notification( lv_travel_id ).
ENDIF.
IF ls_prefs-notify_slack = abap_true.
send_slack_message( lv_travel_id ).
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Cas d’usage 2 : Piste d’audit

CLASS zcl_audit_trail_handler DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_audit_trail_handler IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
" Auditer tous les événements
LOOP AT event_data INTO DATA(ls_event).
" Créer une entrée d'audit
INSERT INTO zaudit_log VALUES (
audit_id = cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( )
entity_type = 'TRAVEL"
entity_key = ls_event-%key-TravelId
event_type = event_key
event_date = sy-datum
event_time = sy-uzeit
user_name = sy-uname
event_payload = /ui2/cl_json=>serialize( ls_event )
).
ENDLOOP.
COMMIT WORK.
ENDMETHOD.
ENDCLASS.

Cas d’usage 3 : Déclencheur de workflow

CLASS zcl_workflow_trigger_handler DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_workflow_trigger_handler IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
CHECK event_key = 'TravelApproved'.
LOOP AT event_data INTO DATA(ls_event).
DATA(lv_travel_id) = ls_event-%key-TravelId.
" Charger les détails du voyage
SELECT SINGLE * FROM zi_travel
WHERE TravelId = @lv_travel_id
INTO @DATA(ls_travel).
" Déclencher le workflow pour les voyages coûteux
IF ls_travel-TotalAmount > 10000.
" Démarrer le workflow SAP Build Process Automation
DATA(lo_workflow) = cl_spa_workflow=>get_instance( ).
lo_workflow->start_workflow(
workflow_id = 'HighValueTravelApproval"
context = VALUE #(
travel_id = lv_travel_id
amount = ls_travel-TotalAmount
approved_by = ls_event-%param-ApprovedBy
)
).
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Cas d’usage 4 : Invalidation du cache

CLASS zcl_cache_handler DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_cache_handler IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
" Pour tous les événements Travel : invalider le cache
CASE event_key.
WHEN 'TravelApproved' OR 'TravelRejected' OR 'TravelCancelled'.
LOOP AT event_data INTO DATA(ls_event).
" Supprimer le cache pour ce Travel
cl_cache_manager=>invalidate(
cache_area = 'TRAVEL"
key = ls_event-%key-TravelId
).
" Optionnel : Supprimer aussi les caches liés
" (par ex. Customer-Travel-List)
SELECT SINGLE CustomerId FROM zi_travel
WHERE TravelId = @ls_event-%key-TravelId
INTO @DATA(lv_customer_id).
cl_cache_manager=>invalidate(
cache_area = 'CUSTOMER_TRAVELS"
key = lv_customer_id
).
ENDLOOP.
ENDCASE.
ENDMETHOD.
ENDCLASS.

Intégration avec SAP Event Mesh

Configuration Event Mesh

Qu’est-ce que SAP Event Mesh ?

  • Service cloud pour Enterprise Event Bus
  • Découple Publisher et Consumer via Message Queue
  • Supporte le pattern Pub/Sub
  • Multi-tenant, évolutif, haute disponibilité

Publier vers Event Mesh

CLASS zcl_event_mesh_publisher DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_rap_entity_event_subscriber.
ENDCLASS.
CLASS zcl_event_mesh_publisher IMPLEMENTATION.
METHOD if_rap_entity_event_subscriber~on_business_event.
" Transférer l'événement RAP vers Event Mesh
LOOP AT event_data INTO DATA(ls_event).
" Sérialiser l'événement en JSON
DATA(lv_json_payload) = /ui2/cl_json=>serialize(
data = ls_event
compress = abap_false
).
" Client HTTP pour Event Mesh
DATA(lo_http) = cl_web_http_client_manager=>create_by_http_destination(
i_destination = cl_http_destination_provider=>create_by_cloud_destination(
i_name = 'EVENT_MESH"
i_authn_mode = if_a4c_cp_service=>service_specific
)
).
" Requête POST
DATA(lo_request) = lo_http->get_http_request( ).
lo_request->set_header_field(
i_name = 'Content-Type"
i_value = 'application/json"
).
lo_request->set_header_field(
i_name = 'x-qos' " Quality of Service
i_value = '1' " At least once
).
lo_request->set_text( lv_json_payload ).
" Topic de l'événement
DATA(lv_topic) = |sap/s4/travel/{ event_key }|.
TRY.
DATA(lo_response) = lo_http->execute(
i_method = if_web_http_client=>post
i_uri = |/messagingrest/v1/topics/{ lv_topic }/messages|
).
IF lo_response->get_status( )-code = 204.
" Publié avec succès
cl_bali_log=>create( )->add_item(
cl_bali_free_text_setter=>create(
severity = if_bali_constants=>c_severity_information
text = |Event { event_key } published to Event Mesh|
)
)->save( ).
ENDIF.
CATCH cx_web_http_client_error INTO DATA(lx_http).
" Journaliser l'erreur
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_http )
)->save( ).
ENDTRY.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Consommer depuis Event Mesh

" Endpoint Webhook pour les callbacks Event Mesh
CLASS zcl_event_mesh_consumer DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_http_service_extension.
ENDCLASS.
CLASS zcl_event_mesh_consumer IMPLEMENTATION.
METHOD if_http_service_extension~handle_request.
" Event Mesh envoie des événements via HTTP POST
" Extraire le payload JSON
DATA(lv_payload) = request->get_text( ).
" Désérialiser
DATA ls_event TYPE zi_travel_event.
/ui2/cl_json=>deserialize(
EXPORTING json = lv_payload
CHANGING data = ls_event
).
" Traiter l'événement
CASE ls_event-event_type.
WHEN 'TravelApproved'.
" Traitement local
process_travel_approved( ls_event ).
WHEN 'TravelRejected'.
process_travel_rejected( ls_event ).
ENDCASE.
" Réponse
response->set_status( i_code = 200 i_reason = 'OK' ).
ENDMETHOD.
ENDCLASS.

Tests d’événements

Tester le déclenchement d’événements

CLASS ltc_travel_events DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_environment TYPE REF TO if_cds_test_environment.
METHODS:
setup,
teardown,
test_approve_raises_event FOR TESTING.
ENDCLASS.
CLASS ltc_travel_events IMPLEMENTATION.
METHOD setup.
mo_environment = cl_cds_test_environment=>create(
i_for_entity = 'ZI_Travel"
).
" Données de test
mo_environment->insert_test_data(
i_data = VALUE zi_travel(
( TravelId = '00000001' Status = 'O' CustomerId = '000042' )
)
).
ENDMETHOD.
METHOD test_approve_raises_event.
" Arrange: Enregistrer Event Spy
DATA(lo_event_spy) = NEW lcl_event_spy( ).
" (Dans les vrais tests : Spy spécifique au framework)
" Act: Exécuter l'action (devrait lever l'événement)
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE approve FROM VALUE #( ( TravelId = '00000001' ) )
FAILED DATA(failed).
COMMIT ENTITIES.
" Assert: L'événement a été déclenché
" (Dépendant du framework - ici conceptuel)
cl_abap_unit_assert=>assert_equals(
exp = 1
act = lo_event_spy->get_event_count( 'TravelApproved' )
msg = 'Event TravelApproved should be raised"
).
" Assert: Paramètres de l'événement corrects
DATA(ls_event) = lo_event_spy->get_event( 'TravelApproved' ).
cl_abap_unit_assert=>assert_equals(
exp = '00000001"
act = ls_event-%key-TravelId
).
ENDMETHOD.
METHOD teardown.
ROLLBACK ENTITIES.
mo_environment->destroy( ).
ENDMETHOD.
ENDCLASS.

Tester Event Handler

CLASS ltc_event_handler DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO zcl_travel_event_handler.
METHODS:
setup,
test_handle_approved_event FOR TESTING.
ENDCLASS.
CLASS ltc_event_handler IMPLEMENTATION.
METHOD setup.
mo_cut = NEW zcl_travel_event_handler( ).
ENDMETHOD.
METHOD test_handle_approved_event.
" Arrange: Mock Event Data
DATA(lt_event_data) = VALUE zi_travel_event_tab(
( %key-TravelId = '00000001"
%param-ApprovedBy = 'TEST_USER"
%param-ApprovedAt = '20250101' )
).
" Act
mo_cut->if_rap_entity_event_subscriber~on_business_event(
event_key = 'TravelApproved"
event_data = lt_event_data
).
" Assert: Vérifier que le Handler a traité correctement
" (par ex. email envoyé, entrée de log créée)
" → Dépend de l'implémentation
ENDMETHOD.
ENDCLASS.

Patterns d’événements

Pattern 1 : Chaînage d’événements

" Event 1 déclenche Event 2 déclenche Event 3
" Publisher 1
METHOD create_travel.
" ...
RAISE ENTITY EVENT zi_travel~TravelCreated FROM ...
ENDMETHOD.
" Consumer 1 = Publisher 2
CLASS zcl_handler_1 IMPLEMENTATION.
METHOD on_business_event.
CHECK event_key = 'TravelCreated'.
" Effectuer la validation
validate_travel( event_data ).
" Événement suivant
RAISE ENTITY EVENT zi_travel~TravelValidated FROM ...
ENDMETHOD.
ENDCLASS.
" Consumer 2 = Publisher 3
CLASS zcl_handler_2 IMPLEMENTATION.
METHOD on_business_event.
CHECK event_key = 'TravelValidated'.
" Démarrer le processus d'approbation
start_approval( event_data ).
" Événement suivant
RAISE ENTITY EVENT zi_travel~ApprovalStarted FROM ...
ENDMETHOD.
ENDCLASS.

Pattern 2 : Agrégation d’événements

" Agréger plusieurs événements en un événement récapitulatif
CLASS zcl_event_aggregator IMPLEMENTATION.
METHOD on_business_event.
" Collecter les événements
CASE event_key.
WHEN 'TravelApproved' OR 'TravelRejected' OR 'TravelCancelled'.
" Compteur en mémoire
add_to_statistics( event_key ).
ENDCASE.
" Chaque heure : Événement récapitulatif
IF hour_passed( ).
RAISE EVENT DailySummary
FROM VALUE #( ( approvals = mv_approval_count
rejections = mv_rejection_count
cancellations = mv_cancellation_count ) ).
reset_statistics( ).
ENDIF.
ENDMETHOD.
ENDCLASS.

Pattern 3 : Filtrage d’événements

" Traiter uniquement certains événements
CLASS zcl_high_value_handler IMPLEMENTATION.
METHOD on_business_event.
CHECK event_key = 'TravelCreated'.
LOOP AT event_data INTO DATA(ls_event).
" Charger les détails du voyage
SELECT SINGLE TotalAmount FROM zi_travel
WHERE TravelId = @ls_event-%key-TravelId
INTO @DATA(lv_amount).
" Seulement les voyages de grande valeur (> 10.000)
CHECK lv_amount > 10000.
" Traitement spécial
notify_manager( ls_event-%key-TravelId ).
require_additional_approval( ls_event-%key-TravelId ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Remarques importantes / Bonnes pratiques

  • Loose Coupling : Les événements découplent Publisher et Consumer
  • Asynchrone : Les événements permettent un traitement asynchrone
  • Fire & Forget : Le Publisher ne se soucie pas du succès du Consumer
  • Idempotence : Les Event Handlers doivent être idempotents (traitement multiple = OK)
  • Gestion des erreurs : Les erreurs des Consumers ne doivent pas affecter le Publisher
  • Nommage des événements : Temps passé (TravelApproved, pas ApproveTravel)
  • Payload de l’événement : Seulement les données nécessaires (pas de gros objets)
  • Versioning : Ne jamais casser la structure de l’événement (seulement étendre)
  • Testing : Tester explicitement les événements (pattern Spy)
  • Monitoring : Journaliser et monitorer le traitement des événements
  • Logique de retry : Événements échoués dans une queue pour retry
  • Event Mesh : Utiliser pour les événements inter-systèmes
  • Documentation : Documenter les événements et leurs Consumers

Ressources supplémentaires