This is a test message to test the length of the message box.
Login
|
ABAP RAP Auxiliary Class
Created by Software-Heroes

RAP - Auxiliary Class

876

As the implementation grows in the behavior implementation of a RAP object, what options do you still have for clean encapsulation? Let's look at this in detail.

Advertising


In this article, we implement further methods in our RAP object and consider how we can handle code more cleanly.

 

Introduction

So far, our implementations within the Sales App have been very manageable. In the last article, we included the change documents in the implementation, which would be too large for the actual method. In such a case, we should consider refactoring before the implementation becomes too large. and becomes unmanageable and/or maintainable.

 

MVC

The actual separation in RAP is already very well defined by SAP. We separate the various objects and layers according to the Model-View-Controller principle, thus providing automatic guidelines for good software development. We can assign the different objects to the different layers:

 

 

This then corresponds to the following objects:

  • Model - Table, Core Data Service, Service and Version
  • View - SAP Fiori App
  • Controller - Behavior definition and implementation

 

The different layers are separate from each other and each fulfills its own task. The comparison with MVC is quite close, but not 100% accurate. However, here too we can quickly ruin the Controller layer with information and poor implementations. For example, if we carry out the complete implementation of an Action in the Action's method. This violates Clean ABAP principles, and the method quickly becomes unwieldy.

 

Preparation 

In the first step, we want to implement an additional action in the RAP object. The "Check Consistency" button should validate the entire object and the data and create a logo, which is then linked in the object. We have defined the action for this, deployed it in the UI, but haven't implemented it yet.

 

Implementation

So let's start with the implementation of the "ConsistencyCheck" method in the behavior implementation and read in all the data from the current record so that we can check it.

READ ENTITIES OF zbs_r_sasale IN LOCAL MODE
     ENTITY SASale
     ALL FIELDS WITH CORRESPONDING #( keys )
     RESULT DATA(selected_sales)

     ENTITY SASale BY \_SASeller
     ALL FIELDS WITH CORRESPONDING #( keys )
     RESULT DATA(selected_sellers)

     ENTITY SASale BY \_SAInfo
     ALL FIELDS WITH CORRESPONDING #( keys )
     RESULT DATA(selected_infos)

     ENTITY SASale BY \_SASold
     ALL FIELDS WITH CORRESPONDING #( keys )
     RESULT DATA(selected_materials).

 

After reading the information via EML, we can now evaluate the various components and generate messages in an application log. For this, we use our small open-source project, the ABAP Message Logger (AML), and want to perform the following checks.

  • Difference - Was only one of the two differences populated? Neither field may be present, nor may any information be missing.
  • Information - Has information been recorded and translated into all necessary languages?
  • Seller - Are there any sellers, have they been approved, and is the total 100%?
  • Material - Is material present and maintained?

 

We will also create our own messages to generate good and translatable output for the user.

 

Now we can implement the actual method and perform the checks. For now, we will leave all components within this method. We generate the messages locally to support the usage tracking, using a specific attribute of the logger so that we do not create any additional variables with "NEEDED".

LOOP AT selected_sales INTO DATA(sale).
  IF    sale-DifferenceAmount IS NOT INITIAL AND sale-DifferenceQuantity IS NOT INITIAL
     OR sale-DifferenceAmount IS INITIAL     AND sale-DifferenceQuantity IS INITIAL.
    MESSAGE e001(zbs_demo_rap) INTO logger->message_text.
    logger->add_message_system( ).
  ENDIF.
ENDLOOP.

LOOP AT selected_sellers INTO DATA(seller).
  IF seller-Confirmed IS INITIAL.
    MESSAGE e004(zbs_demo_rap) WITH seller-SellerId INTO logger->message_text.
    logger->add_message_system( ).
  ENDIF.
ENDLOOP.

SELECT FROM @selected_sellers AS table
  FIELDS SUM( Quota ) AS QuotaSum
  INTO @DATA(overall_sum).

IF overall_sum <> 100.
  MESSAGE e002(zbs_demo_rap) INTO logger->message_text.
  logger->add_message_system( ).
ENDIF.

IF selected_sellers IS INITIAL.
  MESSAGE e003(zbs_demo_rap) INTO logger->message_text.
  logger->add_message_system( ).
ENDIF.

DATA(helper) = NEW zcl_bs_demo_rap_auxiliary( ).
LOOP AT helper->get_supported_languages( ) INTO DATA(supported_langauge).
  IF NOT line_exists( selected_infos[ Language = supported_langauge-language ] ).
    MESSAGE e005(zbs_demo_rap) WITH supported_langauge-language INTO logger->message_text.
    logger->add_message_system( ).
  ENDIF.
ENDLOOP.

IF selected_materials IS INITIAL.
  MESSAGE e003(zbs_demo_rap) INTO logger->message_text.
  logger->add_message_system( ).
ENDIF.

IF NOT logger->has_error( ).
  MESSAGE s007(zbs_demo_rap) INTO logger->message_text.
  logger->add_message_system( ).
ENDIF.

 

Once the check is complete, we save the log via a second database connection, otherwise RAP will throw an exception because we are not allowed to perform commit, work, or insert operations during this phase (interaction phase). We configure the second connection when creating the object; here is the beginning of the method and the saving process.

DATA(logger) = zcl_aml_log_factory=>create( VALUE #( object                = 'Z_AML_LOG'
                                                     subobject             = 'TEST'
                                                     default_message_class = 'ZBS_DEMO_RAP'
                                                     use_2nd_db_connection = abap_true ) ).

" ...

logger->save( ).

 

Using EML, we now update the LoggingId field to persist the information to the object. Finally, we send a success message for the check to the UI so that the user is notified of the change.

MODIFY ENTITIES OF zbs_r_sasale IN LOCAL MODE
       ENTITY SASale
       UPDATE FIELDS ( LoggingId )
       WITH VALUE #( FOR key IN keys
                     ( %tky = key-%tky LoggingId = logger->get_log_handle( ) ) )
       FAILED DATA(failed_updates).

IF failed_updates-sasale IS INITIAL.
  INSERT new_message( id       = 'ZBS_DEMO_RAP'
                      number   = '008'
                      severity = if_abap_behv_message=>severity-success )
         INTO TABLE reported-%other.
ELSE.
  INSERT new_message( id       = 'ZBS_DEMO_RAP'
                      number   = '009'
                      severity = if_abap_behv_message=>severity-success )
         INTO TABLE reported-%other.
ENDIF.

 

Update

Currently, after the log is created and updated, a message is displayed, but the UI is not updated, and therefore we don't see the new LoggingId directly in the UI. For this, we use a Side Effect and update the field in the UI after the action is executed. Here, we don't need to reload the entire entity, but only update the field. We include the Side Effect in the behavior definition ZBS_R_SASALE, since the action is available from this level onwards.

side effects {
  action ConsistencyCheck affects field LoggingId;
}

 

Looking at the point in the browser and on the network (Developer Tools - F12), we still only see one batch request on the network after the action has been executed. However, there will be a change in detail, as this request actually contains two separate actions.

 

In the first step, our action, which is supposed to perform the actual check, is triggered via POST. As a second request, we send a GET request, which reads the new data. This only reads the LoggingId field, as we defined it in the side effect.

 

Helper Class

Now we already have two large and two similar implementations in our behavior implementation. Actually, much too large for the individual methods. Therefore, we begin with the actual refactoring of the existing logic.

 

Solutions

We currently have several solutions available for this. For example, we can create additional local classes and methods to move the coding away from the actual implementation. However, this still doesn't achieve reusability in the object-oriented context. We could also use a global class, which would make the code globally and reusable. However, there's a problem here too, since we primarily work with EML in a Behavior Pool (BP), we can't use LOCAL MODE in a normal class. This is important, however, if we want to access the current state of an entry.

 

Auxiliary Class

For correct usage, we can therefore use an auxiliary class, which we specify in our behavior definition. This gives the class the same properties as a Behavior Pool, and we can outsource our Coe. As a first step, we create our new global class ZCL_BS_DEMO_RAP_AUXILIARY.

CLASS zcl_bs_demo_rap_auxiliary DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
ENDCLASS.


CLASS zcl_bs_demo_rap_auxiliary IMPLEMENTATION.
ENDCLASS.

 

Once the class is defined, we can extend the behavior definition ZBS_R_SASALE and declare the class to the RAP object. To do this, we use the addition AUXILIARY CLASS in the header and can specify one or more global classes here.

managed implementation in class ZBS_BP_R_SASALE unique;
strict ( 2 );
with draft;
extensible;
auxiliary class zcl_bs_demo_rap_auxiliary;

 

Before we begin the actual refactoring, we need to tell the class which RAP object or behavior definition the class represents. We do this by defining the relationship to the RAP object in the class properties using FOR BEHAVIOR.

CLASS zcl_bs_demo_rap_auxiliary DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC
  FOR BEHAVIOR OF zbs_r_sasale.

  PUBLIC SECTION.
ENDCLASS.


CLASS zcl_bs_demo_rap_auxiliary IMPLEMENTATION.
ENDCLASS.

 

Refactoring

We can now start refactoring the local implementation and outsourcing the various components to our helper class. Of course, we shouldn't just simply move things around, but also make the methods smaller and easier to handle. In this example, we use the class directly without an interface; a clean object is always preferable here, even if it means a lot of work. More information about the different designs can be found in this article.

 

Change Documents

In the last article, we implemented change documents in RAP. For this, we defined two helper methods and implemented a relatively large amount of code for the different changes. We would leave the actual CATCH statement as is, since we also want to retain the option of sending a message back to the UI. For this purpose, we implement the various helper methods for the individual tables and move the code to a separate section. This makes the implementation of the SAVE_MODIFED method quite manageable.

TRY.
    DATA(helper) = NEW zcl_bs_demo_rap_auxiliary( ).
    helper->change_document_for_create( create ).
    helper->change_document_for_update( update ).
    helper->change_document_for_delete( delete ).

  CATCH cx_chdo_write_error INTO DATA(error).
    RAISE SHORTDUMP error.
ENDTRY.

 

Consistency Check

We would now completely outsource the actual consistency check. We'll move it into a new method that will also return whether the check was successful or if there were any errors in creating the log. We'll leave the message generation in the actual check; all other checks will then be further outsourced to smaller methods.

DATA(consistent_flag) = NEW zcl_bs_demo_rap_auxiliary( )->is_consistent( keys ).

IF consistent_flag = abap_true.
  INSERT new_message( id       = 'ZBS_DEMO_RAP'
                      number   = '008'
                      severity = if_abap_behv_message=>severity-success )
         INTO TABLE reported-%other.
ELSE.
  INSERT new_message( id       = 'ZBS_DEMO_RAP'
                      number   = '009'
                      severity = if_abap_behv_message=>severity-success )
         INTO TABLE reported-%other.
ENDIF.

 

Finalize

We won't go into all the details of the refactoring, as there may be variations depending on the developer, such as how the actual methods are redefined. After the refactoring, we have several new types and methods in the new helper class. In principle, you can further subdivide the code and define other methods. This is just a first version of the migration.

 

Complete Example

You can find the complete example in GitHub in the corresponding package for the Sales App. The changes from this article can be found in this Commit and you can follow the changes, plus the additional information.

 

Conclusion

The additional helper class offers the possibility to better structure the code and bring order to a business object. However, you should address such changes early on if you notice that the code is becoming too extensive and unclear.

 

Source:
SAP Help - Auxiliary class


Included topics:
RAPBTPAuxiliary ClassREX7
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Tuesday and Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


RAP - Implement Change Documents (native)

Category - ABAP

If you have the appropriate release, you can now implement change documents natively in RAP without much manual implementation. Let's look at the different steps.

04/24/2026

RAP - Implement change documents (Manual)

Category - ABAP

This article delves into the manual implementation of change documents in our RAP object and examines the various integration steps. The goal is to generate change documents automatically.

04/14/2026

RAP - Draft Query

Category - ABAP

In this article, we'll look at the Draft Query in RAP and how you can use it to control entries and their visibility. We'll also look at a practical example.

04/03/2026

RAP - Importance

Category - ABAP

Let's look at the importance of information within an SAP Fiori application and how we can use it to control visibility in the RAP application.

03/24/2026

RAP - Criticality

Category - ABAP

What do you actually need criticality for in your application, and what can you achieve with it? Let's look at different forms and scenarios.

03/21/2026