Modularization
ABAP Developmentadvanced

Modularization Part 9: Reusable Service Class Design

Key takeaway

Design a reusable global service class for a business object: clear responsibility, small public interface, separation of data access and logic, exceptions, buffering, and how function modules, reports and BAdIs consume it.

Explanation

📌 KEY POINTS
✅ A service class owns one business area (e.g. flight bookings) and offers a small, stable public interface.

✅ Separate data access (DAO/repository) from business logic — easier to test and change.

✅ Signal errors with class-based exceptions carrying messages; don't show messages inside the service.

💡 Reports, RFC function modules, BAdIs and ALV handlers all call the same service — logic exists once.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Parts 6–8 built classes and interfaces. This part combines them into a design you can reuse across a project.

━━━━━━━━━━━━━━━━━━━━
▶ 1. RESPONSIBILITY

ZCL_FLIGHT_BOOKING_SERVICE: check availability, book a seat, cancel a booking, get bookings of a customer. Not: ALV display, file upload, mail sending — those are other classes.

━━━━━━━━━━━━━━━━━━━━
▶ 2. LAYERS INSIDE THE SERVICE

  • Public methods: business operations (check_availability, book_seat).
  • Data access: a separate class/interface ZIF_FLIGHT_DAO with read/write methods (SELECT, updates) — replaceable in tests (Part 10).
  • Validation helpers: private methods or a validator class.

━━━━━━━━━━━━━━━━━━━━
▶ 3. INTERFACE DESIGN

  • Business names: book_seat( is_request ) RETURNING rv_booking_id RAISING zcx_flight_booking.
  • Input structures for many fields; output via RETURNING or EXPORTING structures.
  • Declare exceptions with meaningful texts (message class) — callers decide how to show them.

━━━━━━━━━━━━━━━━━━━━
▶ 4. INSTANCE CREATION

  • Factory method (get_instance / create) or constructor injection for the DAO.
  • Singleton only if the service holds a shared buffer and is stateless otherwise.

━━━━━━━━━━━━━━━━━━━━
▶ 5. BUFFERING

Read-heavy master data (airline texts) can be cached in a hashed table attribute. Keep caches per object/session; don't cache transactional data that changes.

━━━━━━━━━━━━━━━━━━━━
▶ 6. TRANSACTIONS

The service performs changes via update FMs or direct custom-table DML + lock objects, but does NOT COMMIT — the caller (report, RFC wrapper) controls the LUW. This keeps the service usable inside exits and other transactions.

━━━━━━━━━━━━━━━━━━━━
▶ 7. CONSUMERS

  • Report: creates the service, calls methods, shows results in ALV.
  • RFC FM Z_FLIGHT_BOOK_RFC: thin wrapper — maps parameters, calls book_seat, converts exceptions to RETURN messages, commits.
  • BAdI implementation: calls check_availability during another transaction (no commit).

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

Build the service outline in the code example, then call it from a report and an RFC wrapper.

✅ CHECKPOINT: you can design a service class that others reuse safely.

Code example

ABAP Code
* ===== Data access interface (replaceable in tests) =====INTERFACE zif_flight_dao PUBLIC.  METHODS read_flight    IMPORTING iv_carrid TYPE s_carr_id iv_connid TYPE s_conn_id iv_fldate TYPE s_date    RETURNING VALUE(rs_flight) TYPE sflight.  METHODS insert_booking IMPORTING is_booking TYPE sbook.ENDINTERFACE. * ===== Service class =====CLASS zcl_flight_booking_service DEFINITION PUBLIC FINAL CREATE PRIVATE.  PUBLIC SECTION.    CLASS-METHODS create      IMPORTING io_dao           TYPE REF TO zif_flight_dao OPTIONAL      RETURNING VALUE(ro_service) TYPE REF TO zcl_flight_booking_service.    METHODS check_availability      IMPORTING iv_carrid TYPE s_carr_id iv_connid TYPE s_conn_id iv_fldate TYPE s_date      RETURNING VALUE(rv_free) TYPE i      RAISING   zcx_flight_booking.    METHODS book_seat      IMPORTING is_booking TYPE sbook      RAISING   zcx_flight_booking.                   " no COMMIT inside!  PRIVATE SECTION.    DATA mo_dao TYPE REF TO zif_flight_dao.ENDCLASS. CLASS zcl_flight_booking_service IMPLEMENTATION.  METHOD create.    CREATE OBJECT ro_service.    IF io_dao IS BOUND.      ro_service->mo_dao = io_dao.                    " test double    ELSE.      CREATE OBJECT ro_service->mo_dao TYPE zcl_flight_dao_db.   " real DB access    ENDIF.  ENDMETHOD.   METHOD check_availability.    DATA ls_flight TYPE sflight.    ls_flight = mo_dao->read_flight( iv_carrid = iv_carrid iv_connid = iv_connid                                     iv_fldate = iv_fldate ).    IF ls_flight IS INITIAL.      RAISE EXCEPTION TYPE zcx_flight_booking        EXPORTING textid = zcx_flight_booking=>flight_not_found.    ENDIF.    rv_free = ls_flight-seatsmax - ls_flight-seatsocc.  ENDMETHOD.   METHOD book_seat.    IF check_availability( iv_carrid = is_booking-carrid iv_connid = is_booking-connid                           iv_fldate = is_booking-fldate ) <= 0.      RAISE EXCEPTION TYPE zcx_flight_booking        EXPORTING textid = zcx_flight_booking=>flight_full.    ENDIF.    mo_dao->insert_booking( is_booking ).             " caller controls COMMIT  ENDMETHOD.ENDCLASS. * ===== Consumer: RFC wrapper (thin) =====* FUNCTION z_flight_book_rfc. " IMPORTING is_booking, EXPORTING et_return TYPE bapiret2_t*   TRY.*       zcl_flight_booking_service=>create( )->book_seat( is_booking ).*       COMMIT WORK.*     CATCH zcx_flight_booking INTO lx.              " DATA lx TYPE REF TO zcx_flight_booking*       ROLLBACK WORK.*       ls_return-type = 'E'. ls_return-message = lx->get_text( ).*       APPEND ls_return TO et_return.*   ENDTRY.* ENDFUNCTION.

Real project scenario

Booking logic existed in an online report, an RFC module for a web portal and a nightly job — three implementations with different checks. A portal booking bypassed the "no overbooking" rule that only the report had.
The team created ZCL_FLIGHT_BOOKING_SERVICE with the rules in one place, a DAO interface for database access, and exceptions with message texts. All three consumers became thin callers; the overbooking loophole closed, and unit tests covered the rules with a fake DAO.

Common mistakes

  • Services that also do UI (ALV, messages).
  • COMMIT WORK inside reusable services.
  • Huge public interfaces exposing internal helpers.
  • Direct SELECTs everywhere, making tests impossible.
  • Logic duplicated in RFC wrappers and reports.

Best practices

  • One business responsibility per service.
  • Small public interface with business names and exceptions.
  • Separate data access behind an interface.
  • Caller controls the LUW; services don't commit.
  • Thin consumers (reports, FMs, BAdIs) calling the service.

Interview angle

Questions: "How do you design a reusable service class?", "Should a service class commit?", "Why separate data access?"
Strong answer: a service owns one business area with a small public interface of business operations, class-based exceptions with message texts, private helpers, and a data access layer behind an interface for replaceability/testing; it doesn't commit — callers control the LUW; reports, RFC FMs and BAdIs are thin consumers; buffer only stable master data.

Practice this lesson

Quick check

1. The same delivery-block rule exists in a report, a user exit and an OData service with different results. Best fix?

2. Why does ZCL_FLIGHT_BOOKING_SERVICE depend on the interface ZIF_FLIGHT_DAO rather than on ZCL_FLIGHT_DAO directly?

3. A service class method detects that a flight is full. How should it report this?

Interview questions

1. How do you design a service layer for a large ABAP application?

Answer it in your own words first, then compare.

2. The same delivery-block rule is coded separately in a report, a user exit and an OData service, and they now give different results. How do you fix this?

Answer it in your own words first, then compare.

Practice more with Interview AI →

Reference-verified: the steps are checked against official SAP documentation and tutorials, and object and field names in the sample code are checked for consistency. The code has not been compiled or run on an SAP system by ERPClimb. Syntax marked release-dependent varies by SAP release, so check it in your own system.

More lessons in this topic

Interview questions on this topic

Related SAP topics