logo

Are you need IT Support Engineer? Free Consultant

Recommendations for Wrapper Interface Design

  • By Sanjay
  • 25/06/2026
  • 1 Views


With the shift towards clean core extensibility in SAP S/4HANA, developers are encouraged to build extensions that are upgrade-stable, loosely coupled, and compliant with ABAP Cloud principles. One key design pattern to achieve this is the use of wrappers.

As described in Clean core extensibility for SAP S/4HANA Cloud and, in more detail, in Extend SAP S/4HANA in the cloud and on premise with ABAP based extensions, SAP recommends encapsulating classic APIs and internal objects within a wrapper layer. This wrapper can then be released for use in ABAP Cloud development.

In this blog post, we will walk through:

  • Why wrappers are essential
  • How to design them effectively
  • Best practices for data types and consistency
  • Common pitfalls and how to avoid them

 

A wrapper acts as a controlled façade around existing SAP APIs (for example, classic BAPIs). Instead of consuming these APIs directly, your application interacts only with the wrapper.

Key Benefits

  • Encapsulation of internal APIs: Only the necessary parts of the underlying API are exposed.
  • Clear contract: The wrapper defines a stable and explicit interface towards SAP.
  • Reduced dependencies: Consumers depend on the wrapper, not the underlying implementation.
  • Improved flexibility: The implementation can be replaced or adapted without impacting consumers.
  • Better testability: Wrappers make it easier to create test doubles and isolate logic during testing.

 

A typical case is wrapping a classic BAPI such as BAPI_USER_GET_DETAIL. Instead of calling the BAPI directly,

  • You define a wrapper interface
  • Call the BAPI inside the wrapper implementation
  • Expose only the required parameters and results

Let’s assume your task is getting the first name, the last name, and the phone number of a user. The wrapper interface and implementation may look like this:

C1-released interface:

INTERFACE zkd_if_wrap_bapi_user_get PUBLIC .

  TYPES:
    ty_username TYPE xubname,
    BEGIN OF ts_person,
      firstname TYPE ad_namefir,
      lastname  TYPE ad_namelas,
    END OF ts_person,
    BEGIN OF ts_telephone,
      std_no TYPE ad_flgdfnr,
      tel_no TYPE ad_telnrlg,
    END OF ts_telephone,
    tt_telephone TYPE STANDARD TABLE OF ts_telephone WITH NON-UNIQUE DEFAULT KEY,
    tt_bapiret2  TYPE STANDARD TABLE OF bapiret2 WITH NON-UNIQUE DEFAULT KEY.

  METHODS:
    user_get_details
      IMPORTING
        iv_username      TYPE ty_username
        iv_cache_results TYPE abap_boolean DEFAULT abap_false
      EXPORTING
        es_person        TYPE ts_person
        et_telephone     TYPE tt_telephone
        et_return        TYPE tt_bapiret2.

ENDINTERFACE.

Non-released implementation:

CLASS zkd_cl_wrap_bapi_user_get DEFINITION PUBLIC.

 PUBLIC SECTION.
    INTERFACES zkd_if_wrap_bapi_user_get.

ENDCLASS.

CLASS zkd_cl_wrap_bapi_user_get IMPLEMENTATION.

  METHOD zkd_if_wrap_bapi_user_get~user_get_details.

    DATA ls_address TYPE bapiaddr3.
    DATA lt_addtel TYPE STANDARD TABLE OF bapiadtel.

    CALL FUNCTION 'BAPI_USER_GET_DETAIL'
      EXPORTING
        username      = iv_username
        cache_results = iv_cache_results
      IMPORTING
        address       = ls_address
      TABLES
        addtel        = lt_addtel
        return        = et_return.

    es_person = CORRESPONDING #( ls_address ).
    et_telephone = CORRESPONDING #( lt_addtel ).

  ENDMETHOD.

ENDCLASS.

C1-released factory:

CLASS zkd_cl_wrap_factory DEFINITION PUBLIC CREATE PRIVATE.

  PUBLIC SECTION.
    CLASS-METHODS:
      get_wrap_bapi_user_get RETURNING VALUE(ro_wrapper) TYPE REF TO zkd_if_wrap_bapi_user_get.

ENDCLASS.

CLASS zkd_cl_wrap_factory IMPLEMENTATION.

  METHOD get_wrap_bapi_user_get.
    ro_wrapper = NEW zkd_cl_wrap_bapi_user_get( ).
  ENDMETHOD.

ENDCLASS.

The wrapper may be accessed as indicated in the following console application:

CLASS zkd_cl_call_wrap_bapi_user_get DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

ENDCLASS.

CLASS zkd_cl_call_wrap_bapi_user_get IMPLEMENTATION.

  METHOD if_oo_adt_classrun~main.

    DATA lo_wrapper TYPE REF TO zkd_if_wrap_bapi_user_get.
    lo_wrapper = zkd_cl_wrap_factory=>get_wrap_bapi_user_get(  ).

    lo_wrapper->user_get_details(
      EXPORTING
        iv_username="DEHMANN"
      IMPORTING
        es_person    = DATA(ls_person)
        et_telephone = DATA(lt_telephone)
        et_return    = DATA(lt_return) ).

    out->write( |First name: { ls_person-firstname }| ).
    out->write( |Last name : { ls_person-lastname }| ).
    out->write( |Phone numbers:| ).
    LOOP AT lt_telephone INTO DATA(ls_telephone).
      out->write( ls_telephone-tel_no ).
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

 

When designing wrapper interfaces, follow these principles:

  • Keep it lean
    • The wrapper implementation should stay close to the underlying API
    • Avoid adding unnecessary logic inside the wrapper
  • Expose only what you need
    • Restrict the interface to required parameters and outputs
    • Avoid overexposing SAP structures
  • Separate concerns
    • Handle conversions (internal ↔ external formats) outside or in dedicated components

In our example, the wrapper exposes only the parameters that are needed. Moreover, BAPIRET2 is the only SAP structure that is being used. Besides mapping parameters, the wrapper does not contain any additional logic.

 

Handling data types correctly is essential. Follow these principles when implementing your wrapper interface:

  • Use released types when available: If SAP provides a suitable, released data type, always use it directly in your interface definition.
  • Define your own types when needed: If the required data types are not released, declare them explicitly within your wrapper interface using the TYPES statement. If these types are shared among a set of wrappers, then consider declaring these in a central interface with corresponding TYPES declaration. If technically needed, you may also create such types in ABAP Dictionary and release them for use in Cloud Development.
  • Define smaller structures: Avoid tight coupling to SAP structures. Rather than reusing or copying large SAP structures, declare smaller, dedicated structures with required fields only. This reduces the dependency footprint.
  • Be careful when declaring table types: Always declare a corresponding line type explicitly.

Let's check how these recommendations map to our example:

  • The released data element ABAP_BOOL is being used in the type declaration of the IV_CACHE_RESULTS parameter and the released BAPIRET2 structure is being used for the declaration of the TT_BAPIRET2 table type.
  • We define our own types TY_USERNAME, TS_PERSON, and TS_TELEPHONE referencing non-released SAP data elements such as XUBNAME or AD_NAMEFIR.
  • Instead of exposing all components of the BAPIADDR3 structure we declare the (small) TS_PERSON structure which contains only the relevant components. We keep the component names to ease the mapping in the implementation.
  • For the phone numbers we declare the TT_TELEPHONE table type and TS_TELEPHONE line type.

If you decide to reference non-released SAP structures in the type declarations, keep the following restrictions in mind:

  • If the SAP structure is complex (that is, it contains nested or non-elementary data types such as structure, table type, data reference, or object reference), you can't access these nested or non-elementary components from ABAP for Cloud Development unless they're released.
  • If the SAP structure is flat and contains only elementary data types, you can access the components of the structure from ABAP for Cloud Development. However, rework might be required if SAP enhances the structure with non-elementary data types in the future.

Let’s take a look at a negative example. Assume your task is reading the text of an object. You consider using the READ_MULTIPLE_TEXTS function module, which is a classic API. The wrapper implementation may look like this:

C1-released interface:

INTERFACE zkd_if_wrap_read_texts PUBLIC .

  TYPES:
    tt_texttable TYPE text_lh.

  METHODS:
    read_texts
      IMPORTING
        iv_name       TYPE tdobname
        iv_object     TYPE tdobject
        iv_id         TYPE tdid
      EXPORTING
        et_text_table TYPE tt_texttable.

ENDINTERFACE.

Non-released implementation:

CLASS zkd_cl_wrap_read_texts DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES zkd_if_wrap_read_texts.

ENDCLASS.

CLASS zkd_cl_wrap_read_texts IMPLEMENTATION.

  METHOD zkd_if_wrap_read_texts~read_texts.

    CALL FUNCTION 'READ_MULTIPLE_TEXTS'
      EXPORTING
        name       = iv_name
        object     = iv_object
        id         = iv_id
      IMPORTING
        text_table = et_text_table.

  ENDMETHOD.

ENDCLASS.

C1-released factory:

CLASS zkd_cl_wrap_factory DEFINITION PUBLIC CREATE PRIVATE.

  PUBLIC SECTION.
    CLASS-METHODS:
      get_wrap_bapi_user_get RETURNING VALUE(ro_wrapper) TYPE REF TO zkd_if_wrap_bapi_user_get,
      get_wrap_read_texts RETURNING VALUE(ro_wrapper) TYPE REF TO zkd_if_wrap_read_texts.

ENDCLASS.

CLASS zkd_cl_wrap_factory IMPLEMENTATION.

  METHOD get_wrap_bapi_user_get.
    ro_wrapper = NEW zkd_cl_wrap_bapi_user_get( ).
  ENDMETHOD.

  METHOD get_wrap_read_texts.
    ro_wrapper = NEW zkd_cl_wrap_read_texts( ).
  ENDMETHOD.

ENDCLASS.

It uses the non-released TEXT_LH table type and does not declare a corresponding line type. Because there is no line type declaration, the “The use of element of is not permitted” syntax check message is returned when trying to access a component of the underlying structure from ABAP for Cloud Development.

Syntax_Error.png

You could fix this by adding a corresponding line type declaration using the non-released ITCLH structure, which is the base structure of the TEXT_LH table type.

INTERFACE zkd_if_wrap_read_texts PUBLIC .

  TYPES:
    ts_texttable TYPE itclh,
    tt_texttable TYPE STANDARD TABLE OF ts_texttable.

  METHODS:
    read_texts
      IMPORTING
        iv_name       TYPE tdobname
        iv_object     TYPE tdobject
        iv_id         TYPE tdid
      EXPORTING
        et_text_table TYPE tt_texttable.

ENDINTERFACE.

 You can now access the LINES component.

CLASS zkd_cl_call_wrap_read_texts DEFINITION PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

ENDCLASS.

CLASS zkd_cl_call_wrap_read_texts IMPLEMENTATION.

  METHOD if_oo_adt_classrun~main.

    DATA lo_wrapper TYPE REF TO zkd_if_wrap_read_texts.
    lo_wrapper = zkd_cl_wrap_factory=>get_wrap_read_texts(  ).

    lo_wrapper->read_texts(
      EXPORTING
        iv_name="GLOSSAR         SAP"
        iv_object="FORM"
        iv_id         = 'TXT'
      IMPORTING
        et_text_table = DATA(lt_text_table) ).

    READ TABLE lt_text_table INTO DATA(ls_text_table) INDEX 1.
    DATA(lt_lines) = ls_text_table-lines.

  ENDMETHOD.

ENDCLASS.

However, the ITCLH structure has the TDFORMAT component, which has a non-elementary type. When you try to access it from ABAP for Cloud Development,  you’ll receive the “The use of element of is not permitted” syntax check message.

Syntax_Error2.Png

This time, you won’t be able to fix it unless you define your own types.

 

Run the “API Release: Consistency of Released Objects” (API_CONSISTENCY) ATC check for your wrappers. Among other things, this checks if the components of your wrapper interface are accessible from ABAP for Cloud Development. In case of findings, rework your wrapper interfaces as recommended above.

Let’s take a look again at the negative example.

INTERFACE zkd_if_wrap_read_texts PUBLIC .

  TYPES:
    tt_texttable TYPE text_lh.

  METHODS:
    read_texts
      IMPORTING
        iv_name       TYPE tdobname
        iv_object     TYPE tdobject
        iv_id         TYPE tdid
      EXPORTING
        et_text_table TYPE tt_texttable.

ENDINTERFACE.

If you run the consistency check for this wrapper interface, you’ll receive the following check messages. It’ll report problems with the missing line types:

Check_Messages.png

Read the Checking the Consistency of Released APIs documentation for more information.

 

Usage of Unreleased Objects

A known issue (see SAP note 3450531) is that earlier syntax checks allowed access to:

  • Unreleased CDS entities via associations
  • Unreleased components of a complex structure

This got fixed and the syntax checks were tightened, and violations result in the “The use of element of is not permitted” check message. The new syntax check message was added as a warning in 2023 and the priority got increased to error in 2025.

Hence, if you receive this check message,  follow the guidance provided in the note. If the check message is caused by your  wrapper being released incompletely as shown above,  adjust the wrapper interface as recommended in this blog.

False Positives in Syntax Checks

Unfortunately, there is another known issue (see SAP note 3362119) where some of these syntax check messages “The use of element of is not permitted” were incorrect (false positives). Follow the guidance provided in the note and either apply the kernel correction (recommended) or exempt the false positive. In latter case, please make sure to exempt only false positives, since you’ll get real syntax errors when you upgrade the system to the 2025 release. If you’re unsure if it’s a false positive, consider running the API_CONSISTENCY ATC check. If the check reports a finding, then fix it, since it’s not a false positive.

 

In the ABAP Cloud Development model, using released SAP APIs is a core principle. Released SAP APIs are officially supported and have stable interfaces.

  • SAP guarantees backward compatibility
  • Your code will continue to work after upgrades or patches
  • No risk from using internal or unreleased objects that might change or disappear

A released API is not upgrade-stable just because it’s released. SAP provides tools and checks that can be used to identify incompatible changes for released APIs: To detect and prevent incompatible changes in released APIs, the API_COMPATIBILITY ATC check is provided. Any change that is not allowed according to the compatibility rules of the release contract will be reported by this ATC check. To check compatibility, the ATC check uses so-called API snapshots. You can create API snapshots at any time using the Manage API Snapshots app.

Even if you don’t ship the released APIs to third parties but just release APIs for bridging classic APIs to the ABAP Cloud Development model, make use of the compatibility check. For example, if you reference a non-released SAP data element in your wrapper interface and SAP changes the data element in an incompatible way, then the compatibility check will inform you about this change and you can react accordingly.

Read the Checking the Compatibility of Released APIs documentation for more information.

 

To sum up,

  • Wrapper interfaces are a core pattern for clean core extensibility
  • They decouple your application from SAP internals
  • Proper data type declaration is crucial
  • ATC checks help you create consistent wrappers
  • Take the “The use of element of is not permitted” syntax check message seriously



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *