Skip to Content
Personal Insights
Author's profile photo Siddhartha Routh

RAP Based Events in SAP S/4HANA Cloud ABAP Environment ( aka Embedded Steampunk )

Event Driven Architecture has gained a lot of popularity in recent times as businesses are looking to leverage it to become flexible , agile and scalable. Events are used more and more for real time integration with 3rd party systems without the need to extend the core. There are great many blogs already written about Event Driven Architecture and Event Mesh and how to configure event mesh to send events from S/4HANA On-Premise or cloud system to BTP Event mesh. This blog is focused more on ABAP RESTful Application Programming Model ( RAP ) based events introduced as part of the recent release.

With the release of BTP ABAP environment 2208 and developer extensibility( Embedded Steampunk) in S/4HANA Cloud , ABAP RESTful Application Programming Model framework now supports event driven architecture natively. Events represent a significant change of state of the RAP business objects or of one of its child entity that is relevant for follow-up processes . RAP BO’s can act as both Event Consumer and Event Producer.

Just like the standard SAP S/4HANA On-Premise or Cloud events RAP based events too work together with event mesh as a message broker to produce or receive events , we will continue the discussion with the assumption that Event mesh is already configured in your system and Channels are already set up for outbound topic binding.

RAP Business Objects as Event Consumers

Events can be consumed from the Event Mesh Queue using the newly introduced event consumption model . Event Consumption Model can be created using the AsyncAPI JSON file for a particular event , the consumption model based on the payload mentioned in the AsyncAPI JSON creates an abstract entity , consumer class and a consumer extension class , the consumer extension class can then be used to plugin our custom logic on how the event should be handled. For Example a sales order can be created using a RAP BO based on the event payload.

Please check the below tutorial which explains in great detail on how to configure Event Mesh in a SAP S/4HANA cloud system and how to Consume Events using the Event Consumption Model.

Event Consumption in ABAP Development Tools using RAP BO

 

RAP Business Objects as Event Producers

RAP entity events are defined through Behavior Definition on root node level for both managed and unmanaged scenario. Entity events can have a parameter ( Abstract CDS Entity ) which acts as the event payload and is optional. If no parameter is defined the keys for the root BO is used as the event payload. The RAP Event created will be then mapped to a event type using “Event Binding” which will allow the event to be configured as an Outbound Topic in our event mesh Channel. This topic is then subscribed by an event mesh queue for further processing, messages can be consumed by 3rd party systems from the queue directly.

The event can also be triggered from outside the RAP Business object by wrapping the “Raise Event” in a local class in the behavior pool class and calling the same by creating static method in the global class pool of the behavior implementation class. This static method can be then used to raise the events from outside the RAP BO by passing the payload as an importing parameter.

For this use case a simple RAP BO is created which will send an event for every new instance created using the RAP BO , a parameter is defined for the event using an abstract entity that acts as payload structure to send additional Contact Details along with the partner number created. Keyword “RAISE ENTITY EVENT” is then used to raise the event when a new instance is created , this is implemented within its own behavior pool using an additional save. 

Interface View 

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Interface View for Contact Info'
define root view entity ZI_CONINFO
  as select from ztabconinfo
{
  key bpno                  as Bpno,
      firstname             as Firstname,
      lastname              as Lastname,
      @Semantics.eMail.address: true
      email                 as Email,
      @Semantics.user.createdBy: true
      local_created_by      as LocalCreatedBy,
      @Semantics.systemDateTime.createdAt: true
      local_created_at      as LocalCreatedAt,
      @Semantics.user.lastChangedBy: true
      local_last_changed_by as LocalLastChangedBy,
      //local ETag field --> OData ETag
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      local_last_changed_at as LocalLastChangedAt,

      //total ETag field
      @Semantics.systemDateTime.lastChangedAt: true
      last_changed_at       as LastChangedAt
}

 

Consumption View 

@EndUserText.label: 'Consumption View for Contact Info'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZC_CONINFO
  provider contract transactional_query
  as projection on ZI_CONINFO
{
  key Bpno,
      Firstname,
      Lastname,
      Email,
      LocalLastChangedAt
}

 

Metadata Extension 

@Metadata.layer: #CORE
@UI: { headerInfo: {
typeName: 'User',
typeNamePlural: 'Users',
title: { type: #STANDARD, value: 'Bpno' } },
         presentationVariant: [{
         sortOrder: [{ by: 'Bpno', direction: #DESC }],
         visualizations: [{type: #AS_LINEITEM}] }] }
annotate view ZC_CONINFO with
{
  @UI.facet: [    {
                 label: 'General Information',
                 id: 'GeneralInfo',
                 type: #COLLECTION,
                 position: 10
                 },
                      { id:            'Userdetails',
                     purpose:       #STANDARD,
                     type:          #IDENTIFICATION_REFERENCE,
                     label:         'User Details',
                     parentId: 'GeneralInfo',
                     position:      10 } ]

  @UI: { lineItem:       [ { position: 10, importance: #HIGH , label: 'User Number'} ] ,
         identification: [ { position: 10 , label: 'User Number' } ] }
  Bpno;
  @UI: { lineItem:       [ { position: 20, importance: #HIGH , label: 'First Name'} ] ,
        identification: [ { position: 20 , label: 'First Name' } ] }
  Firstname;
  @UI: { lineItem:       [ { position: 30, importance: #HIGH , label: 'Last Name'} ] ,
        identification: [ { position: 30 , label: 'Last Name' } ] }
  Lastname;
  @UI: { lineItem:       [ { position: 40, importance: #HIGH , label: 'Email Address'} ] ,
        identification: [ { position: 40 , label: 'Email Address' } ] }
  Email;

}

 

Behavior Definition for Interface view

In the below behavior definition the event is defined along with a CDS abstract entity as a parameter. The Keys for the RAP BO along with the parameter makes the payload for the event.

managed implementation in class zbp_i_coninfo unique;
strict ( 2 );
with draft;

define behavior for ZI_CONINFO alias Userdetails
persistent table ztabconinfo
draft table ztabconinfod
lock master
total etag LocalLastChangedAt
authorization master ( instance )
with additional save
{
  create;
  update;
  delete;

  field ( readonly ) LastChangedAt, LocalLastChangedBy, LocalLastChangedAt, LocalCreatedBy,
  LocalCreatedAt;

  event SendContact parameter ZABS_EVENTSTRUC;

  draft action Edit;
  draft action Activate;
  draft action Discard;
  draft action Resume;

  draft determine action Prepare;

}

 

CDS Abstract Entity

This will act as the Event Payload along with the RAP BO Key.

@EndUserText.label: 'Event Structure'
define abstract entity ZABS_EVENTSTRUC
{
  firstname  : abap.char(20);
  lastname   : abap.char(20);
  email      : abap.char(20);
  created_by : abp_creation_user;
  created_at : abp_creation_tstmpl;

}

 

Raising the event in Save Modified 

The event is then raised in Save Modified method of the Implementing class for the behavior definition. For our use case we don’t want to raise an event for update or delete and hence the additional check is performed to ensure that the event is raised only for Create Scenarios.

method save_modified.

    if create-userdetails is not initial.


      raise entity event zi_coninfo~SendContact
        from value #(
          for userdet in create-userdetails (
              Bpno        = userdet-Bpno
              firstname        = userdet-Firstname
              lastname          = userdet-Lastname
              email  = userdet-Email
            )
          ).
    endif.

  endmethod.

 

The Behavior definition for the consumption view , the Service definition and the ODataV4 UI Service binding is then created to expose the Business object. Once the Service is published we can then  create an event binding to map the RAP Event and RAP BO and create a custom topic which will be the configured in the EM outbound channel for outbound communication.

The Namespace, Business object and Business Object Operation together becomes the topic for this event as can be seen in the field “Type” below.

The Root Entity and the Event defined in the BDEF is then added as shown below to the Event Binding to map it to the name space.

Event%20Binding

Event Binding

 

The Event is then available in the dropdown for outbound topics and can be configured in the SAP S/4HANA Cloud Event mesh Channel as an Outbound Topic.

Configured%20as%20an%20Outbound%20Topic

Available under Outbound Topic

RAP%20Event%20configured%20as%20outbound%20Topic

RAP Event configured as outbound Topic

 

This is now ready for consumption and can be subscribed to an event mesh queue as shown below. The message can then be consumed from event mesh queue using the standard consumption model such as AMQP, REST , Webhook or now from another RAP business object.

Topic%20Subscribed%20in%20the%20EM%20Queue

Topic Subscribed in the EM Queue

 

To test the application we can then create a new Instance with the corresponding contact details, on saving the contact info an event will be raised which will then send the contact details to the EM queue.

New%20Instance%20Created

New Instance Created

 

Message%20Received%20in%20Queue

Message Received in Queue

Event payload

 

In conclusion, native support of RAP based events dramatically strengthens SAP’s capability to support event driven architecture and gives us as developers the capability make our applications agile and flexible. This will also enable us to extend standard business processes with additional functionality or post processing steps without the need of enhancement keeping the core clean. Using RAP as an event producer we can now have our own custom event which can be used for all the scenarios where Standard events are not available or Simple Notification event doesn’t suffice the requirement , in saying so we should not forget that events are means to be lightweight and hence the payload should be kept to essential fields only.

Below are some of the materials I referred to while writing this blog and can provide more insights additionally I would highly recommend going through the tutorial for event consumption scenarios .  Let me know what you think of this new feature in comments below and please provide feedback.

RAP Business Events

Creating Business Events

Creating Event Consumption Model

 

Assigned Tags

      14 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Syambabu Allu
      Syambabu Allu

      Hi Siddhartha,

      Very well explained blog on RAP with Event Mesh. Thanks for sharing!!

      Thank you,

      Syam

      Author's profile photo Pankaj Yadav
      Pankaj Yadav

      Hi Siddhartha,

      Thanks for sharing this blog.

      Author's profile photo Arun Kumar Agrawal
      Arun Kumar Agrawal

      Very well explained. Thank you Siddhartha..

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Siddhartha Routh ,

       

      thanks for the helpful blog!

      Is it possible in Embedded Steampunk to directly/locally consume RAP Business Events, eg. an event from a S/4HANA Cloud Standard BO in an ABAP Environment based extension?

       

      Regards,

      Wolfgang

      Author's profile photo Siddhartha Routh
      Siddhartha Routh
      Blog Post Author

      Hi , Wolf Gang , yes it is possible to RAP events outside the context of the RAP BO  .

       

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Siddhartha Routh ,

      thanks for the quick answer!

      And how can the achieved, ie direct local consumption of an RAP business event without going through event mesh?

      Regards,

      Wolfgang

      Author's profile photo Ly-Na Phu
      Ly-Na Phu

      Wolfgang Röckelein, did you find a solution to consume the event without using the Event Mesh? We have a similar requirement to consume the event from an external client App on another SaaS platform. We need somehow to use the outbound channel that is not linked to the SAP Event Mesh but to a free HTTP endpoint where the event can be sent to that endpoint. Furthermore, for the authentication to the external App, we need OAuth2 authentication.

      Somebody has an idea how to send the event to a Webhook URL? Many blogs mostly show how to push events to SAP BTP Event Mesh. But meanwhile, we have many external client Apps sitting on different Cloud platforms, and not every customer has an SAP BTP Event Mesh license.

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Ly-Na Phu ,

      meanwhile I learned that local consumption is on the roadmap for next year.

      Event Mesh can at least forward events via standard based protocols and REST APIs, cf. https://help.sap.com/docs/SAP_EM/bf82e6b26456494cbdd197057c09979f/3f424ff1ae3b4bc084c4f1ea0be96f54.html

      Regards,

      Wolfgang

      Author's profile photo Ly-Na Phu
      Ly-Na Phu

      Thanks Wolfgang Röckelein

      Yes, with event mesh, we can use webhook to send the event to an endpoint. The problem is that some customers don't have an SAP Event Mesh license. They already have another Event Broker in use and don't need another event broker, such as the SAP Event Mesh in the chain. Using two event brokers in the chain is quite unnecessary.

      What does local consumption mean? Does it mean that we can write our own abap class as the subscriber or event consumer and do what we want, e.g., in our code, we would use the HTTP client library to invoke an HTTP endpoint to push the event to that endpoint. That would be great.

      Thanks,
      Ly-Na

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Ly-Na Phu ,

      yes, this is my understanding.

      Regards,

      Wolfgang

      Author's profile photo Christian Holtfurth
      Christian Holtfurth

      Hi Sidharta,

      Great post!
      Quick question on the support for dynamic topics:
      Could I use the RAP approach to add data from the event into the topic that I'm publishing to?

      E.g. it looks like in your example you have it configured to publish to topic:
      zsap/s4/beh/Coninfo/Create/v1/

      Could I append that with some dynamic fields like e.g.
      zsap/s4/beh/Coninfo/Create/v1/<user lastname>/<user firstname>

      To promote fields from the actual event into the topic for filtering purposes?

      p.s. adding first name or last name may not be the most logical fields, but it would be useful for other fields like e.g. country codes or deparment ids in other use cases/with other business objects.

      Regards,
      Christian

      Author's profile photo Siddhartha Routh
      Siddhartha Routh
      Blog Post Author

      Hi Chirstian,
      I don't think you can do that.
      The topic needs to be pre-defined and also the queues subscribing them needs the predefined topic to receive the message.

       

      Regards

      Sidd

      Author's profile photo Christian Holtfurth
      Christian Holtfurth

      Hi Siddhartha,

      Is that a conscious decision to limit it to predefined/static topics in RAP?

      Both SAP Event Mesh as well as SAP Advanced Event Mesh are capable of handling dynamic topics, which is one of the strength of them.

      You can use wildcards in queue subscriptions if you want to match all dynamic values of a topic field. E.g. In my example above you could use:

      zsap/s4/beh/Coninfo/Create/v1/>

      For the multi-level wildcard (> in Solace notation) or the equivalent of # in AMQP notation.
      Or you could subscribe using the single level wildcards to match precisely two levels:

      zsap/s4/beh/Coninfo/Create/v1/*/*

      This becomes really powerful when you e.g. have a topic level that contains a countrycode and you want to attract events for one country to one specific queue for some specific countries.

      Would be great if this could be supported with the RAP approach.

      Best regards,
      Christian

      Author's profile photo Priya Gupta
      Priya Gupta

      As per your blog, Create button is not coming in App and getting error in Event Binding that SAP Object Type Coninfo does not exist.