RAP Business Events: Event-driven Architecture with ABAP Cloud

Category
ABAP-Statements
Published
Author
Johannes

Business Events in RAP enable event-driven architecture: Instead of direct method calls, components communicate via events. This decouples systems, enables asynchronous processing, and makes architectures more scalable and maintainable.

The Problem: Tight Coupling

Without Events (Tight Coupling)

" Bad: Direct dependency: Travel -> Email -> Logging
METHOD approve_travel.
" 1. Change status
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = ls_travel-%tky Status = 'A' ) ).
" 2. Send email (direct dependency!)
TRY.
cl_email_sender=>send(
recipient = ls_travel-customer_email
subject = 'Travel approved'
).
CATCH cx_email_error.
" What to do on error?
ENDTRY.
" 3. Log (direct dependency!)
cl_logger=>log( |Travel { ls_travel-TravelId } approved| ).
" 4. Update analytics (direct dependency!)
zcl_analytics=>track_approval( ls_travel-TravelId ).
" 5. Notify external system (direct dependency!)
zcl_external_api=>notify_approval( ls_travel ).
ENDMETHOD.

Problems:

  • Tight Coupling: Approval logic knows all consumers
  • Synchronous: All steps block the main process
  • Not extensible: New consumer = change code
  • Error-prone: One failed consumer breaks everything
  • Slow: Email + API + Logging = 2-3 seconds

With Events (Loose Coupling)

" Good: Event-driven: Travel -> Event -> Consumers (decoupled)
METHOD approve_travel.
" 1. Change status
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = ls_travel-%tky Status = 'A' ) ).
" 2. Raise event (Fire & Forget)
RAISE ENTITY EVENT zi_travel~TravelApproved
FROM VALUE #( ( %key-TravelId = ls_travel-TravelId
%param-ApprovedBy = sy-uname
%param-ApprovedAt = sy-datum ) ).
" Done! Consumers react asynchronously
ENDMETHOD.

Benefits:

  • Loose Coupling: Approval logic knows no consumers
  • Asynchronous: Consumers process in parallel
  • Extensible: New consumers without code change
  • Resilient: Consumer errors don’t affect main process
  • Fast: Main process continues immediately (< 100ms)

Event Anatomy

+-----------------------------------------------------+
| Event Publisher |
| (RAP Business Object) |
| |
| METHOD approve_travel. |
| RAISE ENTITY EVENT zi_travel~TravelApproved ... |
| ENDMETHOD. |
+----------------+------------------------------------+
|
| Event: TravelApproved
| Payload: { TravelId, ApprovedBy, ApprovedAt }
|
v
+-----------------------------------------------------+
| Event Infrastructure |
| (RAP Framework / Event Mesh) |
+--------+------------------------+-------------------+
| |
v v
+--------------------+ +------------------------+
| Consumer 1 | | Consumer 2 |
| (Email) | | (Analytics) |
| | | |
| on_event( ). | | on_event( ). |
| send_email( ) | | track_approval( ) |
+--------------------+ +------------------------+
|
v
+--------------------+
| Consumer 3 |
| (External API) |
| |
| on_event( ). |
| notify_system( ) |
+--------------------+

Event Definition in BDEF

Simple Event

define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
{
create;
update;
delete;
// Event Definition
event TravelApproved;
// Action that raises event
action approve result [1] $self;
}

Event with Parameters

define behavior for ZI_Travel alias Travel
{
// Event with parameter structure
event TravelApproved parameter ZA_TravelApprovedParam;
action approve result [1] $self;
}

Parameter Structure (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);
}

Event with Standard Parameter

define behavior for ZI_Travel alias Travel
{
// Event with predefined %param
event TravelApproved parameter ZA_TravelApprovedParam;
// Or: Only key fields (no explicit parameter)
event TravelCancelled;
}

Event Raising (Publisher)

Raise Event in 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. Read data
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED failed
REPORTED reported.
" 2. Change status
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. Raise event
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. Return result
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT result.
ENDMETHOD.
ENDCLASS.

Event in 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.
" Validation error
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: ValidationFailed raised
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.

Multiple Events

define behavior for ZI_Travel alias Travel
{
// Different events for different scenarios
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.

Event Consumption (Subscriber)

Implement 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.
" Evaluate 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.
" Private methods for event handling
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.

Register Event Handler

Service Definition:

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

Or: Programmatic Registration:

" In initialization class or at system startup
DATA(lo_event_handler) = NEW zcl_travel_event_handler( ).
" Register handler
cl_rap_event_handler=>register(
iv_event_name = 'TravelApproved'
io_handler = lo_event_handler
).

Process Event Data

METHOD handle_travel_approved.
" Event data is a table (can be multiple events)
LOOP AT it_event_data INTO DATA(ls_event).
" Extract %key and %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. Send email
send_approval_email(
iv_travel_id = lv_travel_id
iv_approved_by = lv_approved_by
).
" 2. Track analytics
track_approval_analytics(
iv_travel_id = lv_travel_id
iv_approved_at = lv_approved_at
).
" 3. Notify external API
notify_external_system(
iv_travel_id = lv_travel_id
).
" 4. Log
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.

Error Handling in Consumer

METHOD handle_travel_approved.
LOOP AT it_event_data INTO DATA(ls_event).
" Email sending with error handling
TRY.
send_approval_email( ls_event-%key-TravelId ).
CATCH cx_email_error INTO DATA(lx_email).
" Log error, but DON'T abort event processing
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_email )
)->save( ).
" Optional: Retry logic
add_to_retry_queue(
iv_event_type = 'TravelApproved'
iv_travel_id = ls_event-%key-TravelId
).
ENDTRY.
" API call with error handling
TRY.
notify_external_system( ls_event-%key-TravelId ).
CATCH cx_http_error INTO DATA(lx_http).
" Log error
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_http )
)->save( ).
ENDTRY.
ENDLOOP.
ENDMETHOD.

Practical Use Cases

Use Case 1: Multi-Channel Notification

" 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.
" Load customer preferences
SELECT SINGLE * FROM zcustomer_prefs
WHERE travel_id = @lv_travel_id
INTO @DATA(ls_prefs).
" Multi-channel notification (parallel)
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.

Use Case 2: Audit Trail

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.
" Audit all events
LOOP AT event_data INTO DATA(ls_event).
" Create audit entry
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.

Use Case 3: Workflow Trigger

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.
" Load travel details
SELECT SINGLE * FROM zi_travel
WHERE TravelId = @lv_travel_id
INTO @DATA(ls_travel).
" Trigger workflow for expensive travels
IF ls_travel-TotalAmount > 10000.
" Start SAP Build Process Automation workflow
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.

Use Case 4: Cache Invalidation

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.
" Invalidate cache on all travel events
CASE event_key.
WHEN 'TravelApproved' OR 'TravelRejected' OR 'TravelCancelled'.
LOOP AT event_data INTO DATA(ls_event).
" Delete cache for this travel
cl_cache_manager=>invalidate(
cache_area = 'TRAVEL'
key = ls_event-%key-TravelId
).
" Optional: Also delete related caches
" (e.g., 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.

Integration with SAP Event Mesh

Event Mesh Setup

What is SAP Event Mesh?

  • Cloud service for Enterprise Event Bus
  • Decouples publishers and consumers via message queue
  • Supports Pub/Sub pattern
  • Multi-tenant, scalable, highly available

Publish Event to 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.
" Forward RAP event to Event Mesh
LOOP AT event_data INTO DATA(ls_event).
" Serialize event as JSON
DATA(lv_json_payload) = /ui2/cl_json=>serialize(
data = ls_event
compress = abap_false
).
" HTTP Client for 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
)
).
" POST Request
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 ).
" Event Topic
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.
" Successfully published
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).
" Log error
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_http )
)->save( ).
ENDTRY.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Consume Event from Event Mesh

" Webhook endpoint for Event Mesh callbacks
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 sends events via HTTP POST
" Extract JSON payload
DATA(lv_payload) = request->get_text( ).
" Deserialize
DATA ls_event TYPE zi_travel_event.
/ui2/cl_json=>deserialize(
EXPORTING json = lv_payload
CHANGING data = ls_event
).
" Process event
CASE ls_event-event_type.
WHEN 'TravelApproved'.
" Local processing
process_travel_approved( ls_event ).
WHEN 'TravelRejected'.
process_travel_rejected( ls_event ).
ENDCASE.
" Response
response->set_status( i_code = 200 i_reason = 'OK' ).
ENDMETHOD.
ENDCLASS.

Testing Events

Test Event Raising

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'
).
" Test data
mo_environment->insert_test_data(
i_data = VALUE zi_travel(
( TravelId = '00000001' Status = 'O' CustomerId = '000042' )
)
).
ENDMETHOD.
METHOD test_approve_raises_event.
" Arrange: Register event spy
DATA(lo_event_spy) = NEW lcl_event_spy( ).
" (In real tests: Framework-specific spy)
" Act: Execute action (should raise event)
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE approve FROM VALUE #( ( TravelId = '00000001' ) )
FAILED DATA(failed).
COMMIT ENTITIES.
" Assert: Event was raised
" (Framework-dependent - here conceptual)
cl_abap_unit_assert=>assert_equals(
exp = 1
act = lo_event_spy->get_event_count( 'TravelApproved' )
msg = 'Event TravelApproved should be raised'
).
" Assert: Event parameters correct
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.

Test 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: Verify handler processed correctly
" (e.g., email sent, log entry created)
" -> Depends on implementation
ENDMETHOD.
ENDCLASS.

Event Patterns

Pattern 1: Event Chaining

" Event 1 triggers Event 2 triggers 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'.
" Perform validation
validate_travel( event_data ).
" Next event
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'.
" Start approval process
start_approval( event_data ).
" Next event
RAISE ENTITY EVENT zi_travel~ApprovalStarted FROM ...
ENDMETHOD.
ENDCLASS.

Pattern 2: Event Aggregation

" Aggregate multiple events to a summary event
CLASS zcl_event_aggregator IMPLEMENTATION.
METHOD on_business_event.
" Collect events
CASE event_key.
WHEN 'TravelApproved' OR 'TravelRejected' OR 'TravelCancelled'.
" In-memory counter
add_to_statistics( event_key ).
ENDCASE.
" Every hour: Summary event
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: Event Filtering

" Only process certain events
CLASS zcl_high_value_handler IMPLEMENTATION.
METHOD on_business_event.
CHECK event_key = 'TravelCreated'.
LOOP AT event_data INTO DATA(ls_event).
" Load travel details
SELECT SINGLE TotalAmount FROM zi_travel
WHERE TravelId = @ls_event-%key-TravelId
INTO @DATA(lv_amount).
" Only high-value travels (> 10,000)
CHECK lv_amount > 10000.
" Special handling
notify_manager( ls_event-%key-TravelId ).
require_additional_approval( ls_event-%key-TravelId ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Important Notes / Best Practice

  • Loose Coupling: Events decouple publishers and consumers
  • Asynchronous: Events enable asynchronous processing
  • Fire & Forget: Publisher doesn’t care about consumer success
  • Idempotency: Event handlers should be idempotent (multiple processing = OK)
  • Error Handling: Consumer errors must not affect publisher
  • Event Naming: Past tense (TravelApproved, not ApproveTravel)
  • Event Payload: Only necessary data (no large objects)
  • Versioning: Never break event structure (only extend)
  • Testing: Explicitly test events (spy pattern)
  • Monitoring: Log and monitor event processing
  • Retry Logic: Queue failed events for retry
  • Event Mesh: Use for cross-system events
  • Documentation: Document events and their consumers

Further Resources