RAP Business Events: Arquitectura orientada a eventos con ABAP Cloud

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Business Events en RAP habilitan arquitectura orientada a eventos: En lugar de llamadas directas a métodos, los componentes se comunican a través de eventos. Esto desacopla sistemas, permite el procesamiento asíncrono y hace que las arquitecturas sean más escalables y mantenibles.

El problema: Acoplamiento fuerte

Sin eventos (Acoplamiento fuerte)

" ❌ Dependencia directa: Travel → Email → Logging
METHOD approve_travel.
" 1. Cambiar estado
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = ls_travel-%tky Status = 'A' ) ).
" 2. Enviar email (¡dependencia directa!)
TRY.
cl_email_sender=>send(
recipient = ls_travel-customer_email
subject = 'Travel approved'
).
CATCH cx_email_error.
" ¿Qué hacer en caso de error?
ENDTRY.
" 3. Registrar log (¡dependencia directa!)
cl_logger=>log( |Travel { ls_travel-TravelId } approved| ).
" 4. Actualizar Analytics (¡dependencia directa!)
zcl_analytics=>track_approval( ls_travel-TravelId ).
" 5. Notificar sistema externo (¡dependencia directa!)
zcl_external_api=>notify_approval( ls_travel ).
ENDMETHOD.

Problemas:

  • 🔴 Acoplamiento fuerte: La lógica de aprobación conoce todos los consumidores
  • 🔴 Síncrono: Todos los pasos bloquean el proceso principal
  • 🔴 No extensible: Nuevo consumidor = cambiar código
  • 🔴 Propenso a errores: Un consumidor fallido interrumpe todo
  • 🔴 Lento: Email + API + Logging = 2-3 segundos

Con eventos (Acoplamiento débil)

" ✅ Orientado a eventos: Travel → Event → Consumers (desacoplado)
METHOD approve_travel.
" 1. Cambiar estado
MODIFY ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( Status )
WITH VALUE #( ( %tky = ls_travel-%tky Status = 'A' ) ).
" 2. Disparar evento (Fire & Forget)
RAISE ENTITY EVENT zi_travel~TravelApproved
FROM VALUE #( ( %key-TravelId = ls_travel-TravelId
%param-ApprovedBy = sy-uname
%param-ApprovedAt = sy-datum ) ).
" ¡Listo! Los consumidores reaccionan de forma asíncrona
ENDMETHOD.

Ventajas:

  • Acoplamiento débil: La lógica de aprobación no conoce consumidores
  • Asíncrono: Los consumidores procesan en paralelo
  • Extensible: Nuevos consumidores sin cambiar código
  • Resiliente: Errores del consumidor no afectan el proceso principal
  • Rápido: El proceso principal continúa inmediatamente (< 100ms)

Anatomía de un evento

┌─────────────────────────────────────────────────────┐
│ 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( ) │
└────────────────────┘

Definición de eventos en BDEF

Evento simple

define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
{
create;
update;
delete;
// Definición de evento
event TravelApproved;
// Acción que dispara el evento
action approve result [1] $self;
}

Evento con parámetros

define behavior for ZI_Travel alias Travel
{
// Evento con estructura de parámetros
event TravelApproved parameter ZA_TravelApprovedParam;
action approve result [1] $self;
}

Estructura de parámetros (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);
}

Evento con parámetro estándar

define behavior for ZI_Travel alias Travel
{
// Evento con %param predefinido
event TravelApproved parameter ZA_TravelApprovedParam;
// O: Solo campos clave (sin parámetro explícito)
event TravelCancelled;
}

Disparo de eventos (Publisher)

Disparar evento en 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. Leer datos
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED failed
REPORTED reported.
" 2. Cambiar estado
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. Disparar evento
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. Devolver resultado
READ ENTITIES OF zi_travel IN LOCAL MODE
ENTITY Travel
ALL FIELDS
WITH CORRESPONDING #( keys )
RESULT result.
ENDMETHOD.
ENDCLASS.

Evento en 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.
" Error de validación
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.
" Evento: Disparar 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.

Múltiples eventos

define behavior for ZI_Travel alias Travel
{
// Diferentes eventos para diferentes escenarios
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.

Consumo de eventos (Subscriber)

Implementar 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.
" Evaluar 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étodos privados para manejo de eventos
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.

Registrar Event Handler

Service Definition:

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

O: Registro programático:

" En clase de inicialización o al inicio del sistema
DATA(lo_event_handler) = NEW zcl_travel_event_handler( ).
" Registrar handler
cl_rap_event_handler=>register(
iv_event_name = 'TravelApproved'
io_handler = lo_event_handler
).

Procesar datos del evento

METHOD handle_travel_approved.
" Los datos del evento son una tabla (pueden ser múltiples eventos)
LOOP AT it_event_data INTO DATA(ls_event).
" Extraer %key y %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. Enviar email
send_approval_email(
iv_travel_id = lv_travel_id
iv_approved_by = lv_approved_by
).
" 2. Rastrear analytics
track_approval_analytics(
iv_travel_id = lv_travel_id
iv_approved_at = lv_approved_at
).
" 3. Notificar API externa
notify_external_system(
iv_travel_id = lv_travel_id
).
" 4. Registrar 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.

Manejo de errores en Consumer

METHOD handle_travel_approved.
LOOP AT it_event_data INTO DATA(ls_event).
" Envío de email con manejo de errores
TRY.
send_approval_email( ls_event-%key-TravelId ).
CATCH cx_email_error INTO DATA(lx_email).
" Registrar error, pero NO interrumpir procesamiento del evento
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_email )
)->save( ).
" Opcional: Lógica de reintentos
add_to_retry_queue(
iv_event_type = 'TravelApproved'
iv_travel_id = ls_event-%key-TravelId
).
ENDTRY.
" Llamada API con manejo de errores
TRY.
notify_external_system( ls_event-%key-TravelId ).
CATCH cx_http_error INTO DATA(lx_http).
" Registrar error
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_http )
)->save( ).
ENDTRY.
ENDLOOP.
ENDMETHOD.

Casos de uso prácticos

Caso de uso 1: Notificación multicanal

" Evento: 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.
" Cargar preferencias del cliente
SELECT SINGLE * FROM zcustomer_prefs
WHERE travel_id = @lv_travel_id
INTO @DATA(ls_prefs).
" Notificación multicanal (paralelo)
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.

Caso de uso 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.
" Auditar todos los eventos
LOOP AT event_data INTO DATA(ls_event).
" Crear entrada de auditoría
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.

Caso de uso 3: Disparador 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.
" Cargar detalles del viaje
SELECT SINGLE * FROM zi_travel
WHERE TravelId = @lv_travel_id
INTO @DATA(ls_travel).
" Disparar workflow para viajes costosos
IF ls_travel-TotalAmount > 10000.
" Iniciar workflow de 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.

Caso de uso 4: Invalidación de caché

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.
" Para todos los eventos de Travel: Invalidar caché
CASE event_key.
WHEN 'TravelApproved' OR 'TravelRejected' OR 'TravelCancelled'.
LOOP AT event_data INTO DATA(ls_event).
" Eliminar caché para este Travel
cl_cache_manager=>invalidate(
cache_area = 'TRAVEL'
key = ls_event-%key-TravelId
).
" Opcional: Eliminar cachés relacionados también
" (por ejemplo, lista de viajes del cliente)
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.

Integración con SAP Event Mesh

Configuración de Event Mesh

¿Qué es SAP Event Mesh?

  • Servicio cloud para Enterprise Event Bus
  • Desacopla Publisher y Consumer a través de Message Queue
  • Soporta patrón Pub/Sub
  • Multi-tenant, escalable, alta disponibilidad

Publicar evento en 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.
" Reenviar evento RAP a Event Mesh
LOOP AT event_data INTO DATA(ls_event).
" Serializar evento como JSON
DATA(lv_json_payload) = /ui2/cl_json=>serialize(
data = ls_event
compress = abap_false
).
" Cliente HTTP para 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
)
).
" Solicitud 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 del evento
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.
" Publicado exitosamente
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).
" Registrar error
cl_bali_log=>create( )->add_item(
cl_bali_message_setter=>create_from_exception( lx_http )
)->save( ).
ENDTRY.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Consumir evento desde Event Mesh

" Endpoint Webhook para callbacks de 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 envía eventos vía HTTP POST
" Extraer payload JSON
DATA(lv_payload) = request->get_text( ).
" Deserializar
DATA ls_event TYPE zi_travel_event.
/ui2/cl_json=>deserialize(
EXPORTING json = lv_payload
CHANGING data = ls_event
).
" Procesar evento
CASE ls_event-event_type.
WHEN 'TravelApproved'.
" Procesamiento local
process_travel_approved( ls_event ).
WHEN 'TravelRejected'.
process_travel_rejected( ls_event ).
ENDCASE.
" Respuesta
response->set_status( i_code = 200 i_reason = 'OK' ).
ENDMETHOD.
ENDCLASS.

Testing de eventos

Probar disparo de eventos

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'
).
" Datos de prueba
mo_environment->insert_test_data(
i_data = VALUE zi_travel(
( TravelId = '00000001' Status = 'O' CustomerId = '000042' )
)
).
ENDMETHOD.
METHOD test_approve_raises_event.
" Arrange: Registrar Event Spy
DATA(lo_event_spy) = NEW lcl_event_spy( ).
" (En pruebas reales: Spy específico del framework)
" Act: Ejecutar action (debería disparar evento)
MODIFY ENTITIES OF zi_travel
ENTITY Travel
EXECUTE approve FROM VALUE #( ( TravelId = '00000001' ) )
FAILED DATA(failed).
COMMIT ENTITIES.
" Assert: El evento fue disparado
" (Dependiente del framework - aquí conceptual)
cl_abap_unit_assert=>assert_equals(
exp = 1
act = lo_event_spy->get_event_count( 'TravelApproved' )
msg = 'Event TravelApproved should be raised'
).
" Assert: Parámetros del evento correctos
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.

Probar 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: Verificar que el handler procesó correctamente
" (por ejemplo, email enviado, entrada de log creada)
" → Depende de la implementación
ENDMETHOD.
ENDCLASS.

Patrones de eventos

Patrón 1: Encadenamiento de eventos

" Evento 1 dispara Evento 2 dispara Evento 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'.
" Realizar validación
validate_travel( event_data ).
" Siguiente evento
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'.
" Iniciar proceso de aprobación
start_approval( event_data ).
" Siguiente evento
RAISE ENTITY EVENT zi_travel~ApprovalStarted FROM ...
ENDMETHOD.
ENDCLASS.

Patrón 2: Agregación de eventos

" Agregar múltiples eventos en un evento resumen
CLASS zcl_event_aggregator IMPLEMENTATION.
METHOD on_business_event.
" Recopilar eventos
CASE event_key.
WHEN 'TravelApproved' OR 'TravelRejected' OR 'TravelCancelled'.
" Contador en memoria
add_to_statistics( event_key ).
ENDCASE.
" Cada hora: Evento resumen
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.

Patrón 3: Filtrado de eventos

" Procesar solo ciertos eventos
CLASS zcl_high_value_handler IMPLEMENTATION.
METHOD on_business_event.
CHECK event_key = 'TravelCreated'.
LOOP AT event_data INTO DATA(ls_event).
" Cargar detalles del viaje
SELECT SINGLE TotalAmount FROM zi_travel
WHERE TravelId = @ls_event-%key-TravelId
INTO @DATA(lv_amount).
" Solo viajes de alto valor (> 10.000)
CHECK lv_amount > 10000.
" Manejo especial
notify_manager( ls_event-%key-TravelId ).
require_additional_approval( ls_event-%key-TravelId ).
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Notas importantes / Mejores prácticas

  • Acoplamiento débil: Los eventos desacoplan Publisher y Consumer
  • Asíncrono: Los eventos permiten procesamiento asíncrono
  • Fire & Forget: El Publisher no se preocupa por el éxito del Consumer
  • Idempotencia: Los Event Handlers deben ser idempotentes (múltiples procesamientos = OK)
  • Manejo de errores: Errores del Consumer no deben afectar al Publisher
  • Nomenclatura de eventos: Tiempo pasado (TravelApproved, no ApproveTravel)
  • Payload del evento: Solo datos necesarios (no objetos grandes)
  • Versionado: Nunca romper la estructura del evento (solo extender)
  • Testing: Probar eventos explícitamente (patrón Spy)
  • Monitoreo: Registrar y monitorear el procesamiento de eventos
  • Lógica de reintentos: Eventos fallidos en cola para reintento
  • Event Mesh: Usar para eventos entre sistemas
  • Documentación: Documentar eventos y sus consumidores

Recursos adicionales