BAdI Part 8: Implementation Design — Keep BAdI Logic Clean
Design BAdI implementations that stay maintainable: thin implementing classes delegating to service classes, clear responsibilities per method, message handling, no UI and no COMMIT, testability with ABAP Unit, and naming/documentation standards.
Explanation
📌 KEY POINTS
✅ The implementing class is an adapter: it translates BAdI parameters into calls to your own service classes.
✅ Business rules live in reusable, unit-tested classes — the same rule can then serve a report, OData service or another BAdI.
✅ Respect the BAdI's contract: use its error mechanism, don't COMMIT, don't display dialogs unless the method is meant for it.
⚠️ A 600-line BAdI method with SELECTs, rules and messages mixed together is the most common maintenance problem in enhancement code.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Parts 1–7 covered mechanics. This lesson is about code quality inside implementations.
━━━━━━━━━━━━━━━━━━━━
▶ 1. ADAPTER PATTERN
BAdI method → read what it needs from parameters/objects → map to a simple structure → call ZCL_…_RULES=>check( ) → map results back (CH_FAILED, exceptions, messages, CHANGING values).
━━━━━━━━━━━━━━━━━━━━
▶ 2. SEPARATE RESPONSIBILITIES
- Data access in DAO classes (with buffering, Part 9).
- Rules in rule classes (no dependencies on the BAdI interface).
- Message conversion in one place.
This makes rules unit-testable without starting ME21N or VA01.
━━━━━━━━━━━━━━━━━━━━
▶ 3. CONTRACT RULES
- Use only the method's intended purpose: CHECK methods check, PROCESS methods change data, POST methods run at save.
- Errors: interface exceptions (e.g. APPLICATION_ERROR), CHANGING flags, application message macros — as documented.
- No COMMIT WORK, no CALL TRANSACTION, no popups (unless explicitly a dialog method).
- Clear your own buffers/attributes when a new document starts.
━━━━━━━━━━━━━━━━━━━━
▶ 4. TESTABILITY
- Rule classes with injected DAOs (OOP Part 9) → ABAP Unit tests.
- The adapter stays small enough that a manual transaction test covers it.
━━━━━━━━━━━━━━━━━━━━
▶ 5. STANDARDS
- Naming: implementation ZIM_/ZEI_<area>_<purpose>, class ZCL_IM_/ZCL_BI_…
- Header comment: requirement ID, owner, filter values, called from which transactions.
- One implementation per purpose or a dispatcher (Part 5).
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Take a long existing BAdI method and identify data access, rules and message handling.
- Extract the rule into a class and write two unit tests.
- Reduce the BAdI method to mapping + call + result mapping.
✅ CHECKPOINT: your BAdI implementations are thin adapters around tested business logic.
Code example
* ===== Thin adapter: BAdI implementation =====CLASS zcl_im_mat_check DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES if_ex_badi_material_check.ENDCLASS. CLASS zcl_im_mat_check IMPLEMENTATION. METHOD if_ex_badi_material_check~check_data.* Requirement MM-017, owner: MM team. Called from MM01/MM02, MASS, material upload. DATA: ls_input TYPE zcl_material_rules=>ty_input, lo_rules TYPE REF TO zcl_material_rules, lx_rule TYPE REF TO zcx_material_rule. * 1) map BAdI parameters to own structure ls_input-matnr = wmara-matnr. ls_input-mtart = wmara-mtart. ls_input-matkl = wmara-matkl. * 2) call the business rule CREATE OBJECT lo_rules. TRY. lo_rules->validate( ls_input ). CATCH zcx_material_rule INTO lx_rule.* 3) map the result to the BAdI's error mechanism MESSAGE lx_rule TYPE 'E' RAISING application_error. ENDTRY. ENDMETHOD.ENDCLASS. * ===== Business rule: independent of the BAdI, unit-testable =====CLASS zcl_material_rules DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF ty_input, matnr TYPE matnr, mtart TYPE mtart, matkl TYPE matkl, END OF ty_input. METHODS validate IMPORTING is_input TYPE ty_input RAISING zcx_material_rule.ENDCLASS. CLASS zcl_material_rules IMPLEMENTATION. METHOD validate. IF is_input-mtart = 'ZFER' AND is_input-matkl IS INITIAL. RAISE EXCEPTION TYPE zcx_material_rule EXPORTING textid = zcx_material_rule=>matkl_missing. ENDIF. ENDMETHOD.ENDCLASS. * ===== ABAP Unit (local test class of ZCL_MATERIAL_RULES) =====* METHOD fert_without_group_fails.* ls_input-mtart = 'ZFER'.* TRY.* f_cut->validate( ls_input ).* cl_abap_unit_assert=>fail( 'Exception expected' ).* CATCH zcx_material_rule.* ENDTRY.* ENDMETHOD.Real project scenario
A purchase order BAdI had grown to 900 lines over five years: approvals, budget checks, vendor rules and email notifications. Every change caused regressions, and nobody dared to touch it.
The team split it into an adapter plus four rule classes with unit tests, and moved the email notification to an event after save. Changes now take hours instead of days, and the unit tests run in ATC before each transport.
Common mistakes
- Business logic directly in BAdI methods.
- SELECTs, rules and messages mixed together.
- Using a CHECK method to change data.
- COMMIT or emails inside save-time methods.
- No documentation of owner and purpose.
Best practices
- Implementing class = thin adapter.
- Rules in unit-tested service classes.
- Use each method for its intended purpose.
- Map errors to the BAdI's mechanism in one place.
- Document requirement, owner and callers.
Interview angle
Questions: "How do you structure BAdI code?", "How do you test logic in a BAdI?"
Strong answer: implementing class as adapter mapping parameters to own classes; rules and data access separate and injectable; unit tests on rule classes; respect method purpose and error mechanism; no COMMIT/dialogs; documentation and naming standards; dispatcher for multiple rules.
Practice this lesson
Quick check
1. What should a well-designed BAdI implementing method mainly contain?
2. Where should an email notification triggered by a saved document NOT be sent from?
3. What must be true regarding the visibility and instantiation of methods defined in a BAdI interface?
Interview questions
1. How would you structure BAdI implementations for a large program?
Answer it in your own words first, then compare.
2. A sales order BAdI method contains validation, SQL, message formatting and email sending in one block. How would you redesign it?
Answer it in your own words first, then compare.
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
- BAdI Part 1: What a BAdI Is and How SAP Calls It
- BAdI Part 10: BAdIs in S/4HANA and Clean Core
- BAdI Part 2: Finding and Implementing a BAdI Step by Step
- BAdI Part 3: Designing Your Own BAdI — Fallback Class and Filters
- BAdI Part 4: Classic BAdI vs New BAdI
- BAdI Part 5: Single-Use vs Multiple-Use BAdIs
- BAdI Part 6: Debugging BAdIs in Real Projects
- BAdI Part 7: Using Filter-Dependent BAdIs