Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
I323214
Advisor
Advisor
Hello Guys,

In this blog we will learn new programming model RAP in detail with an example, I would recommend to go through the theory as well before you start coding to understand RAP basics.I would like to thank my colleagues in SAP specially Kailash Satapathy and Gaurav Kumar for helping me in writing this blog.

Prerequisite: 

Concepts of Core Data Services(CDS) and annotations.

Lets Start 🙂

 RESTful ABAP Programming Model (RAP): The term RESTful ABAP Programming Model (RAP) is chosen to reflect its orientation towards a “stateless” REST architecture.

We will start with ABAP evolution over the years: -

Evolution of the ABAP Programming Model

We kick started our ABAP WORLD with classical ABAP programming using reports and module pool programs, then we moved to BSP/Webdynpro with FPM (for UI harmonization), then slowly shifted from ERP style developments to S/4 HANA model developments where our major programming consists of artifacts like CDS, BOPF, GW and Fiori.



ABAP Programming model for SAP Fiori (Current best practice) : -

This development model usually consists of CDS view, BOPF and GW where CDS is tightly coupled with BOPF and GW (using RDS Mapping or using @OData.Publish annotation).



Note: Issue with this model is flow is not well defined

Future Direction - New Programming Model (ABAP Restful Programming Model)



RESTful ABAP Programming model in detail: -

The ABAP RESTful programming model defines the architecture for efficient end-to-end development of intrinsically SAP HANA-optimized OData services (such as Fiori apps) in SAP Cloud Platform ABAP Environment. It supports the development of all types of Fiori applications as well as A2X services. It is based on technologies and frameworks such as Core Data Services (CDS) for defining semantically rich data models and a service model infrastructure for creating OData services with bindings to an OData protocol and ABAP-based application services for custom logic and SAPUI5-based user interfaces – as shown in the figure below



Understanding Concepts

  1. Business Object


business object (BO) is a common term to represent a real-world artifact in enterprise application development such as the Product, the Travel, or the SalesOrder. In general, a business object contains several nodes such as Items and and common transactional operations such as for creating, updating and deleting data and additional application-specific operations, such as Approve in a SalesOrder business object.

From a formal point of view, a business object is characterized by

  • a structure,

  • a behavior and

  • the corresponding runtime implementation.


See the figure for more details.



 

1.1 Structure of a Business Object

From structural aspect, a business object consists of a tree of nodes (SalesOrderItemsScheduleLines) where the nodes are linked by means of a special kind of associations, the compositions. A composition is specialized association that defines a whole-part relationship. A composite part only exists together with its parent entity (whole).

Each node of this composition tree is an element that is modelled with a CDS entity. The root entity is of particular importance: This is considered in the source code of the CDS data definition with the keyword ROOT. The root entity serves as a representation of the business object and defines the top node in a business object's structure.



1.2 Behavior of a Business Object

Each entity of the business object can offer the standard CUD operations create(), update() and delete(). In addition, each entity can also offer specific operations with a dedicated input and output structure which are called actions. The offered CUD operations, actions as well some behavior-relevant properties, such as lock dependencies between the parent and child entities are defined in the behavior definition artifact.

A behavior definition always refers to a CDS data model. As shown in the figure below, a behavior definition relies directly on the CDS root entity. One behavior definition refers exactly to one root entity and one CDS root entity has at most one behavior definition (a 0..1 relationship), which also handles all included child entities.



1.3 Business Object’s Runtime Implementation

The business object runtime mainly consists of two parts: The first part is the interaction phase where a consumer calls the business object operations to change data and read instances with or without the transactional changes. The business object runtime keeps the changes in its internal transactional buffer which represents the state of the instance data. This transactional buffer is always required for a business object. After all changes were performed, the data can be persisted. This is realized with the save sequence.



Business object runtime in detail

Interaction phase have different steps which we segregated as Modify, Read and Lock. Save sequence phase will have steps like Finalise, check_before_save, adjust_numbers and save.



Implementations can be classified as Unmanaged, Managed and Managed with save.

Currently supported implementation is Unmanaged. Managed and Managed with save implementations will come in future releases.



  1. Business Service


The ABAP development platform can act in the roles of service provider and service consumer, such as SAP Fiori UI client.

In the context of the ABAP RESTful programming model, a business service is RESTful service which can be called by a consumer. It is defined by exposing data models and behavior models. It consists of a Service definition and a Service binding which are illustrated in the figure below: -



 

The Service Definition is a projection of the data model and the related behavior to be exposed, whereas the Service Binding implements a specific protocol and the kind of service to be offered for a consumer. This separation allows the data models and service definitions to be integrated into various protocols without any re-implementation.

2.1 Service Definition

 A service definition represents the service model that is generically derived from the underlying CDS-based data model.

Example :-

DEFINE SERVICE service_definition_name

{

EXPOSE cds_entity_1 [AS alias_1];

EXPOSE cds_entity_2 [AS alias_2];

EXPOSE ...

EXPOSE cds_entity_m [AS alias_m];

}

2.2 Service Binding

 The business service binding (short form: service binding) is an ABAP Repository object used to bind a service definition to a client-server communication protocol such as OData.

Relationship between the Data Model, the Service Definition and Service Binding



Let's learn RAP with a CRUD Example.

Here we will go with the Sales Order CRUD Example.

 For testing purpose, I have created Sales Order Header table – ZSSK_VBAK and Sales Order Item  table   – ZSSK_VBAP

ZSSK_VBAK





 

ZSSK_VBAP





We have 3 steps to implement here.

  1. Provide the CDS Data Model with Business Object Structure

  2. Defining and Implementing Behavior of the Business Object.

  3. Defining Business Service for Fiori UI.


 

  1. Provide the CDS Data Model with Business Object Structure


 

 Here we will create CDS view for Business object using Composition and associations to sub nodes.

 Code snippet for Sales Order Header CDS view: -

@AbapCatalog.sqlViewName: 'ZSSKSOHDR'

@AbapCatalog.compiler.compareFilter: true

@AbapCatalog.preserveKey: true

@AccessControl.authorizationCheck: #NOT_REQUIRED

@EndUserText.label: 'Sales Order Header'

@UI: {

headerInfo: {

typeName: 'Sales Order',

typeNamePlural: 'Sales Orders',

title: { type: #STANDARD, value: 'vbeln' }

}

}

@ObjectModel.semanticKey: ['vbeln']

@ObjectModel.representativeKey: 'vbeln'

define root view Zssk_SoHeader as select from zssk_vbak as SOHeader

composition [0..*] of Zssk_SoItem as _SOItem

{

@UI.facet: [

{

id: 'GeneralData',

type: #COLLECTION,

position: 10,

label: 'Sales Order Header'

},

{

type: #FIELDGROUP_REFERENCE,

position: 10,

targetQualifier: 'GeneralData1',

parentId: 'GeneralData',

isSummary: true,

isPartOfPreview: true

},

{

type: #FIELDGROUP_REFERENCE,

position: 20,

targetQualifier: 'GeneralData2',

parentId: 'GeneralData',

isSummary: true,

isPartOfPreview: true

},

{

id: 'SOItem',

purpose: #STANDARD,

type: #LINEITEM_REFERENCE,

label: 'Sales Order Item',

position: 10,

targetElement: '_SOItem'

}

]

@UI: {

lineItem: [ { position: 10, importance: #HIGH } ],

selectionField: [{position: 10 }],

fieldGroup: [{qualifier: 'GeneralData1',position: 10,importance: #HIGH }]

}

key   SOHeader.vbeln,

@UI: {

lineItem: [ { position: 20, importance: #HIGH } ],

selectionField: [{position: 20 }],

fieldGroup: [{qualifier: 'GeneralData2',position: 10,importance: #HIGH }]

}

SOHeader.erdat,

@UI: {

lineItem: [ { position: 30, importance: #HIGH } ],

fieldGroup: [{qualifier: 'GeneralData2',position: 20,importance: #HIGH }]

}

SOHeader.ernam,

@UI: {

lineItem: [ { position: 40, importance: #HIGH } ],

fieldGroup: [{qualifier: 'GeneralData1',position: 20,importance: #HIGH }]

}

SOHeader.vkorg,

@UI: {

lineItem: [ { position: 50, importance: #HIGH } ],

fieldGroup: [{qualifier: 'GeneralData1',position: 30,importance: #HIGH }]

}

SOHeader.vtweg,

@UI: {

lineItem: [ { position: 60, importance: #HIGH } ],

fieldGroup: [{qualifier: 'GeneralData1',position: 40,importance: #HIGH }]

}

SOHeader.spart,

//For action Set Favourite in UI

@UI.lineItem: [{ type: #FOR_ACTION,dataAction: 'set_favourite',label: 'Set Favourite' },

{position: 15, importance: #HIGH}]

@EndUserText.label: 'Favourite'

SOHeader.favourite,

SOHeader.lastchangedby,

SOHeader.lastchangedat,

/*Association*/

_SOItem

}

 

Code Snipper for Sales Order Item CDS view :-

@AbapCatalog.sqlViewName: 'ZSSKSOITM'

@AbapCatalog.compiler.compareFilter: true

@AbapCatalog.preserveKey: true

@AccessControl.authorizationCheck: #CHECK

@EndUserText.label: 'Sales Order Item'

@ObjectModel.representativeKey: ['vbeln','posnr']

define view Zssk_SoItem as select from zssk_vbap as SOItem

association to parent Zssk_SoHeader as _SOHeader on(

$projection.vbeln = _SOHeader.vbeln

)

{

@UI.facet: [

{type: #COLLECTION, position: 10, id: 'SoItem', label: 'Sales Order Item'},

{type: #FIELDGROUP_REFERENCE, position: 10, targetQualifier: 'SoItem1',parentId: 'SoItem', isSummary: true, isPartOfPreview: true},

{type: #FIELDGROUP_REFERENCE, position: 20, targetQualifier: 'SoItem2',parentId: 'SoItem', isSummary: true, isPartOfPreview: true}

]

@UI: {

lineItem: [ { position: 10, importance: #HIGH } ],

fieldGroup: [{qualifier: 'SoItem1',position: 10,importance: #HIGH }]

}

key  SOItem.vbeln,

@UI: {

lineItem: [ { position: 20, importance: #HIGH } ],

fieldGroup: [{qualifier: 'SoItem1',position: 20,importance: #HIGH }]

}

key  SOItem.posnr,

@UI: {

lineItem: [ { position: 30, importance: #HIGH } ],

fieldGroup: [{qualifier: 'SoItem2',position: 10,importance: #HIGH }]

}

SOItem.matnr,

@UI: {

lineItem: [ { position: 40, importance: #HIGH } ],

fieldGroup: [{qualifier: 'SoItem2',position: 20,importance: #HIGH }]

}

SOItem.zmeng,

SOItem.meins,

/*Association*/

_SOHeader

}

 

  1. Defining and Implementing Behavior of the Business Object


Here we will create Behavior definition and implementation for BO.



Right click on CDS view Zssk_SoHeader and create New Behavior definition

Code Snippet for Behavior Definition: -

implementation unmanaged;

define behavior for Zssk_SoHeader alias SOHeader

{

create;

update;

delete;

action set_favourite result [1] $self;

association  _SOItem { create; }

}

define behavior for Zssk_SoItem alias  SOItem

{

create;

update;

delete;

}

We can implement e-tag and locking also in behavior definition: -

 

After this step, we will create Behavior Implementation, right click Behavior definition and opt New Behavior Implementation.

 

Note: The moment you create a new Behavior implementation, local classes will get generated with Behavior class definition for Interaction Phase and Save Sequence Phase (Please refer topic Business object runtime in detail)



 

We will write our logic in respective class, to make it simple I am putting code only in Modify Method in lcl_handler class.

Note : Ideally scenario will be like entries from the UI will be putting into the transaction buffer by Interaction phase( modify(), read() and lock() methods) with a proper locking mechanism and Save Sequence phase( finalize(),check_before_save(),adjust_numbers() and save() methods) will take care of database commit and rollback part with proper validation.

 

Code Snippet for Modify method to update Sales Order Header and Item tables.

Here I am using INSERT,UPDATE and DELETE statements for CRUD operations to make it simple, but in ideal scenarios we will be using BAPI/APIs for CRUD operations.

CLASS lcl_handler DEFINITION INHERITING FROM cl_abap_behavior_handler.

PRIVATE SECTION.

DATA : lt_soheader TYPE TABLE  OF Zssk_vbak,

lt_soitem     TYPE TABLE OF Zssk_vbap,

ls_vbap       TYPE Zssk_vbap,

lt_fields       TYPE TABLE OF dfies.

 

METHODS modify FOR BEHAVIOR IMPORTING

" For Header table

it_set_fav FOR ACTION soheader~set_favourite RESULT et_soheader

it_soheader_create    FOR CREATE soheader

it_soheader_update   FOR UPDATE soheader

it_soheader_delete    FOR DELETE soheader

" For Item table

it_soheader_soitem_create     FOR CREATE soheader\_soitem

it_soitem_create    FOR CREATE soitem

it_soitem_update    FOR UPDATE soitem

it_soitem_delete    FOR DELETE soitem.

ENDCLASS.

CLASS lcl_handler IMPLEMENTATION.

METHOD modify.

/**************************Action Set Favourite************************/

IF it_set_fav IS NOT INITIAL.

DATA(ls_fav_so) = it_set_fav[ 1 ].

UPDATE zssk_vbak SET favourite = 'X' WHERE vbeln = ls_fav_so-vbeln.

IF sy-subrc EQ 0.

SELECT * FROM zssk_vbak INTO CORRESPONDING FIELDS OF TABLE et_soheader WHERE vbeln = ls_fav_so-vbeln.

ENDIF.

ENDIF.

/***************************************************************************/

/* CRUD Operations  for Header table */

/***************************Create****************************************/

IF it_soheader_create IS NOT INITIAL.

MOVE-CORRESPONDING   it_soheader_create TO lt_soheader.

INSERT  zssk_vbak FROM TABLE lt_soheader.

ENDIF.

/*************************************************************************/

/***************************Update*************************************/

IF it_soheader_update IS NOT INITIAL.

CALL FUNCTION 'DDIF_FIELDINFO_GET'

EXPORTING

tabname        = 'ZSSK_VBAK'

TABLES

dfies_tab      = lt_fields

EXCEPTIONS

not_found      = 1

internal_error = 2

OTHERS         = 3.

IF sy-subrc <> 0.

ENDIF.

DATA(ls_hdr) = it_soheader_update[ 1 ].

SELECT SINGLE *

FROM zssk_vbak

INTO @DATA(ls_vbak)

WHERE vbeln = @LieneS_hdr-vbeln.

LOOP AT lt_fields ASSIGNING FIELD-SYMBOL(<fs_field>).

ASSIGN COMPONENT <fs_field>-fieldname OF STRUCTURE ls_hdr TO FIELD-SYMBOL(<fs_target>).

IF <fs_target> IS ASSIGNED AND <fs_target> IS NOT INITIAL.

ASSIGN COMPONENT <fs_field>-fieldname OF STRUCTURE ls_vbak TO FIELD-SYMBOL(<fs_source>).

<fs_source> = <fs_target>.

ENDIF.

ENDLOOP.

APPEND ls_vbak TO lt_soheader.

UPDATE  zssk_vbak  FROM TABLE lt_soheader.

ENDIF.

/*************************************************************************/

/****************************Delete*************************************/

IF it_soheader_delete IS NOT INITIAL.

MOVE-CORRESPONDING   it_soheader_delete TO lt_soheader.

DELETE  zssk_vbak FROM TABLE lt_soheader.

IF sy-subrc EQ 0.

DATA(lv_vbeln) = lt_soheader[ 1 ]-vbeln.

IF  lv_vbeln IS NOT INITIAL.

DELETE FROM zssk_vbap WHERE vbeln EQ lv_vbeln.

ENDIF.

ENDIF.

ENDIF.

/*************************************************************************/

/* CRUD Operations  for Item table */

/***************************Create****************************************/

IF  it_soheader_soitem_create IS NOT INITIAL.

READ TABLE it_soheader_soitem_create INTO DATA(ls_item) INDEX 1.

IF sy-subrc EQ 0.

MOVE-CORRESPONDING ls_item-%target TO lt_soitem.

ENDIF.

IF lt_soitem IS NOT INITIAL.

INSERT  zssk_vbap FROM TABLE lt_soitem.

ENDIF.

ENDIF.

IF it_soitem_create IS NOT INITIAL.

MOVE-CORRESPONDING   it_soitem_create TO lt_soitem.

INSERT  zssk_vbap FROM TABLE lt_soitem.

ENDIF.

/*************************************************************************/

/***************************Update*************************************/

IF it_soitem_update IS NOT INITIAL.

CALL FUNCTION 'DDIF_FIELDINFO_GET'

EXPORTING

tabname        = 'ZSSK_VBAP'

TABLES

dfies_tab      = lt_fields

EXCEPTIONS

not_found      = 1

internal_error = 2

OTHERS         = 3.

IF sy-subrc <> 0.

ENDIF.

DATA(ls_itm) = it_soitem_update[ 1 ].

SELECT  *

FROM zssk_vbap

INTO TABLE @DATA(lt_vbap)

WHERE vbeln = @LieneS_itm-vbeln.

CLEAR ls_itm.

LOOP AT  it_soitem_update INTO ls_itm.

READ TABLE lt_vbap INTO DATA(ls_vbap) WITH KEY vbeln = ls_itm-vbeln posnr = ls_itm-posnr.

IF sy-subrc EQ 0.

LOOP AT lt_fields ASSIGNING FIELD-SYMBOL(<fs_itm_field>).

ASSIGN COMPONENT <fs_itm_field>-fieldname OF STRUCTURE ls_itm TO FIELD-SYMBOL(<fs_itm_target>).

IF <fs_itm_target> IS ASSIGNED AND <fs_itm_target> IS NOT INITIAL.

ASSIGN COMPONENT <fs_itm_field>-fieldname OF STRUCTURE ls_vbap TO  FIELD-SYMBOL(<fs_itm_source>).

<fs_itm_source> = <fs_itm_target>.

ENDIF.

ENDLOOP.

ENDIF.

APPEND ls_vbap TO lt_soitem.

CLEAR: ls_vbap,

ls_itm.

ENDLOOP.

UPDATE  zssk_vbap  FROM TABLE lt_soitem.

ENDIF.

/*************************************************************************/

/****************************Delete*************************************/

IF it_soitem_delete IS NOT INITIAL.

MOVE-CORRESPONDING   it_soitem_delete TO lt_soitem.

DELETE  zssk_vbap FROM TABLE lt_soitem.

ENDIF.

/*************************************************************************/

ENDMETHOD.

ENDCLASS.

CLASS lcl_saver DEFINITION INHERITING FROM cl_abap_behavior_saver.

PROTECTED SECTION.

METHODS finalize          REDEFINITION.

METHODS check_before_save REDEFINITION.

METHODS save              REDEFINITION.

ENDCLASS.

 

CLASS lcl_saver IMPLEMENTATION.

METHOD finalize.

ENDMETHOD.

METHOD check_before_save.

ENDMETHOD.

METHOD save.

ENDMETHOD.

ENDCLASS.

 

  1. Defining Business Service for Fiori UI


Here we will create Service definition and Service Binding.



 

 Service Definition.

The service definition is a projection of the data model and the related behavior to be exposed.

Right click your Package->New->other ABAP Repository Object->Business Service->Service Definition



Code Snippet for Service Definition: -



 

 Service Binding

The service binding implements a specific protocol and the kind of service to be offered for a consumer.

Right click your Package->New->other ABAP Repository Object->Business Service->Service Binding.



Give name of Service Binding and service definition name for which binding needs to be created and click on Finish



Click on the button Publish as shown below to publish Odata service locally which makes service ready for consumption



Once we published, we can see published Information on right side.



Click on Service Url to see the Metadata



Right click on SOHeader/SoItem to see the UI Preview



 

APP Preview

This is just a preview and it will be useful to check how the UI looks with annotations implemented in CDS.



 

Note: - We can create proper UI project from Web IDE and map our Service Binding to test this application. Launch Web IDE -> Create Project from template->List Report->Map Service Binding



Our App will look like this :- It will show a list of sales orders.



Once we select the line item, Action 'Set Favourite' and CUD operations will be enabled:-



Once we click on the line item, It will go to the respective Sales order item page.



The above steps will guide you on how to create a RAP CRUD application.

Overview: ABAP Restful Programming Model: Development Flow



Steps to remember:-

  1. Model CDS view with proper annotations.( For simplicity I have created only one CDS view for root and then one for Item, but ideally, we will follow like we will create basic views for SO header and Item, then transactional( BO views ) for SO header and Item and then Projection views for SO Header and Item ).

  2. Create a Behavior definition for CDS views. ( Behavior definition for root transactional view and Behavior projection for Projection view )

  3. Create Behavior implementation for Behavior definition.

  4. Create a Service definition.

  5. Create Service Binding and add Service definition name for which binding needs to be created and Publish.

  6. Create WebIde project and map Service binding to it.


Conclusion: This programming model will be the future of ABAP which will be protocol agnostic and the good news is SAP will continue to support the ABAP Programming Model for SAP Fiori beside the ABAP RESTful Programming Model in the future for upwards compatibility reasons.

Hope you find this blog helpful.

 
51 Comments