Managed vs. Unmanaged ist die zentrale Architekturentscheidung in RAP (RESTful ABAP Programming). Sie bestimmt, wer die Transaktionssteuerung und CRUD-Operationen übernimmt: das RAP Framework (Managed) oder Sie selbst (Unmanaged).
Die Grundfrage
" Wer führt CREATE, UPDATE, DELETE aus?
" Managed: "SAP, mach du das!"managed implementation in class zbp_i_travel unique;
" Unmanaged: "Ich mache es selbst!"unmanaged implementation in class zbp_i_travel unique;Managed Szenario: Framework macht die Arbeit
Wann Managed nutzen?
✅ Perfekt für:
- Neue Anwendungen (Greenfield-Entwicklung)
- Standardgeschäftsprozesse ohne komplexe Legacy-Logik
- Transaktionale Fiori-Apps mit CRUD-Operationen
- Wenn Sie schnell ein funktionierendes BO brauchen
- Draft-Funktionalität (Zwischenspeichern)
❌ Nicht geeignet für:
- Integration mit Legacy-Code (Funktionsbausteine, BAPIs)
- Komplexe Transaktionslogik außerhalb von RAP
- Wenn Sie volle Kontrolle über DB-Zugriffe brauchen
- Migration von Dynpro/Web Dynpro mit bestehendem Verhalten
Managed: Behavior Definition
managed implementation in class zbp_i_travel unique;strict ( 2 );with draft; " Draft nur in Managed verfügbar!
define behavior for ZI_Travel alias Travelpersistent table ztravel " Framework schreibt hier automatischdraft table zdraft_travel " Für Draft-Datenlock master " Framework verwaltet Lockstotal etag LastChangedAt " Optimistic Locking via ETagauthorization master ( instance ){ // CRUD: NUR deklarieren, keine Implementierung nötig! create; update; delete;
// Felder: Framework kümmert sich um Mapping field ( readonly ) TravelId; field ( readonly ) CreatedBy, CreatedAt, LastChangedBy, LastChangedAt; field ( numbering : managed ) TravelId; " Auto-Nummernvergabe!
// Geschäftslogik: Hier implementieren Sie validation validateDates on save { field BeginDate, EndDate; } determination setStatusNew on modify { create; } action acceptTravel result [1] $self;
// Draft Actions: Framework liefert sie automatisch draft action Edit; draft action Activate optimized; draft action Discard; draft action Resume;
// Assoziationen association _Bookings { create; with draft; }}
define behavior for ZI_Booking alias Bookingpersistent table zbookingdraft table zdraft_bookinglock dependent by _Travel " Lock vom Parentauthorization dependent by _Travel{ update; delete;
field ( readonly ) TravelId, BookingId; field ( numbering : managed ) BookingId;
association _Travel { with draft; }}Managed: Behavior Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS: " ✅ NUR Geschäftslogik implementieren!
" Validation: Wird bei Save ausgeführt validateDates FOR VALIDATE ON SAVE IMPORTING keys FOR Travel~validateDates,
" Determination: Automatische Werte setzen setStatusNew FOR DETERMINE ON MODIFY IMPORTING keys FOR Travel~setStatusNew,
" Action: Geschäftsoperation acceptTravel FOR MODIFY IMPORTING keys FOR ACTION Travel~acceptTravel RESULT result.
" ❌ NICHT implementieren: get_global_authorizations, read, create, update, delete " → Framework macht das automatisch!ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD validateDates. " Framework hat Daten BEREITS gelesen → nur validieren READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( BeginDate EndDate ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_travel).
LOOP AT lt_travel INTO DATA(ls_travel). " Business Rule prüfen IF ls_travel-EndDate < ls_travel-BeginDate. " Framework-Struktur für Fehler füllen APPEND VALUE #( %tky = ls_travel-%tky %element-EndDate = if_abap_behv=>mk-on ) TO failed-travel.
APPEND VALUE #( %tky = ls_travel-%tky %element-EndDate = if_abap_behv=>mk-on %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'Enddatum muss nach Beginndatum liegen' ) ) TO reported-travel. ENDIF. ENDLOOP. ENDMETHOD.
METHOD setStatusNew. " Framework hat Entity erstellt → Defaultwerte setzen READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_travel).
" Nur Neue (Status initial) auf 'O' (Open) setzen MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR travel IN lt_travel WHERE ( Status IS INITIAL ) ( %tky = travel-%tky Status = 'O' ) ) REPORTED DATA(reported_modify). ENDMETHOD.
METHOD acceptTravel. " Action = Business Operation (nicht einfach nur Update) MODIFY ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status LastChangedAt ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'A' LastChangedAt = cl_abap_context_info=>get_system_date( ) ) ) FAILED failed REPORTED reported.
" Ergebnis zurückgeben (result [1] $self) READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT result. ENDMETHOD.
ENDCLASS.Was Sie NICHT schreiben müssen:
- ❌
SELECT * FROM ztravel– Framework macht das - ❌
INSERT ztravel FROM ...– Framework macht das - ❌
UPDATE ztravel SET ...– Framework macht das - ❌
DELETE FROM ztravel– Framework macht das - ❌ Lock-Handling – Framework macht das
- ❌ Nummernvergabe – Framework macht das (bei
numbering : managed)
Unmanaged Szenario: Sie haben volle Kontrolle
Wann Unmanaged nutzen?
✅ Perfekt für:
- Legacy-Integration (BAPIs, Funktionsbausteine einbinden)
- Komplexe Transaktionslogik (mehrstufige Commits)
- Migrating von bestehenden Dynpro/Web-Dynpro-Programmen
- Wenn Sie spezielle DB-Operationen brauchen (z.B. Native SQL)
- Custom Lock-Mechanismen
❌ Nicht geeignet für:
- Quick Prototyping
- Standard-CRUD ohne Besonderheiten
- Draft-Funktionalität (nicht verfügbar in Unmanaged!)
Unmanaged: Behavior Definition
unmanaged implementation in class zbp_i_travel unique;strict ( 2 );
define behavior for ZI_Travel alias Travellock masterauthorization master ( instance )etag master LastChangedAt{ // CRUD: Alles muss implementiert werden! create; update; delete;
// Auch Read MUSS implementiert werden (anders als Managed!)
// Geschäftslogik wie bei Managed validation validateDates on save { field BeginDate, EndDate; } determination setStatusNew on modify { create; } action acceptTravel result [1] $self;
// Lock-Handling müssen SIE implementieren lock ( lock_key );}Unmanaged: Behavior Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS: " ✅ Alles SELBST implementieren:
" Create: Neue Entities erstellen create FOR MODIFY IMPORTING entities FOR CREATE Travel,
" Update: Bestehende Entities ändern update FOR MODIFY IMPORTING entities FOR UPDATE Travel,
" Delete: Entities löschen delete FOR MODIFY IMPORTING keys FOR DELETE Travel,
" Read: Entities lesen read FOR READ IMPORTING keys FOR READ Travel RESULT result,
" Lock: Sperren verwalten lock FOR LOCK IMPORTING keys FOR LOCK Travel,
" Feature Control get_instance_features FOR INSTANCE FEATURES IMPORTING keys REQUEST requested_features FOR Travel RESULT result,
" Geschäftslogik (wie bei Managed) validateDates FOR VALIDATE ON SAVE IMPORTING keys FOR Travel~validateDates,
setStatusNew FOR DETERMINE ON MODIFY IMPORTING keys FOR Travel~setStatusNew,
acceptTravel FOR MODIFY IMPORTING keys FOR ACTION Travel~acceptTravel RESULT result.ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD create. " Daten aus entities-Parameter extrahieren DATA lt_travel TYPE TABLE FOR CREATE zi_travel. lt_travel = entities.
" Nummernvergabe (MANUELL, da unmanaged!) LOOP AT lt_travel ASSIGNING FIELD-SYMBOL(<fs_travel>). " Nummer aus Nummernkreis holen TRY. <fs_travel>-TravelId = cl_numberrange_runtime=>get_next_number( nr_range_nr = '01' object = 'ZTRAVEL' ). CATCH cx_number_ranges INTO DATA(lx_nr). " Fehler behandeln APPEND VALUE #( %cid = <fs_travel>-%cid %fail-cause = if_abap_behv=>cause-unspecific ) TO failed-travel. CONTINUE. ENDTRY.
" Defaults setzen <fs_travel>-CreatedBy = sy-uname. <fs_travel>-CreatedAt = cl_abap_context_info=>get_system_date( ). <fs_travel>-Status = 'O'.
" In DB schreiben (MANUELL!) INSERT ztravel FROM @( CORRESPONDING #( <fs_travel> ) ).
IF sy-subrc = 0. " Erfolgreiches Mapping zurückgeben APPEND VALUE #( %cid = <fs_travel>-%cid TravelId = <fs_travel>-TravelId ) TO mapped-travel. ELSE. " Fehler APPEND VALUE #( %cid = <fs_travel>-%cid %fail-cause = if_abap_behv=>cause-unspecific ) TO failed-travel. ENDIF. ENDLOOP. ENDMETHOD.
METHOD update. " Zu aktualisierende Felder extrahieren LOOP AT entities INTO DATA(ls_entity). " %control prüft, welche Felder geändert werden sollen IF ls_entity-%control-Status = if_abap_behv=>mk-on. " Status wurde geändert → DB-Update UPDATE ztravel SET status = @ls_entity-Status, last_changed_by = @sy-uname, last_changed_at = @cl_abap_context_info=>get_system_date( ) WHERE travel_id = @ls_entity-TravelId.
IF sy-subrc <> 0. APPEND VALUE #( %tky = ls_entity-%tky %fail-cause = if_abap_behv=>cause-not_found ) TO failed-travel. ENDIF. ENDIF.
" Weitere Felder analog... IF ls_entity-%control-Description = if_abap_behv=>mk-on. UPDATE ztravel SET description = @ls_entity-Description WHERE travel_id = @ls_entity-TravelId. ENDIF. ENDLOOP. ENDMETHOD.
METHOD delete. " Löschen aus DB DELETE FROM ztravel WHERE travel_id IN ( SELECT TravelId FROM @keys AS k ).
IF sy-dbcnt < lines( keys ). " Nicht alle gelöscht → Fehler LOOP AT keys INTO DATA(ls_key). SELECT SINGLE @abap_true FROM ztravel WHERE travel_id = @ls_key-TravelId INTO @DATA(lv_exists).
IF lv_exists = abap_true. " Existiert noch → konnte nicht gelöscht werden APPEND VALUE #( %tky = ls_key-%tky %fail-cause = if_abap_behv=>cause-locked ) TO failed-travel. ENDIF. ENDLOOP. ENDIF. ENDMETHOD.
METHOD read. " Daten aus DB lesen SELECT * FROM ztravel FOR ALL ENTRIES IN @keys WHERE travel_id = @keys-TravelId INTO TABLE @DATA(lt_db_travel).
" In RAP-Struktur konvertieren result = CORRESPONDING #( lt_db_travel MAPPING TO ENTITY ). ENDMETHOD.
METHOD lock. " Lock-Objekt sperren (via ENQUEUE) LOOP AT keys INTO DATA(ls_key). CALL FUNCTION 'ENQUEUE_EZTRAVEL' EXPORTING mode_ztravel = 'E' mandt = sy-mandt travel_id = ls_key-TravelId EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.
IF sy-subrc <> 0. " Lock fehlgeschlagen APPEND VALUE #( %tky = ls_key-%tky %fail-cause = if_abap_behv=>cause-locked ) TO failed-travel. ENDIF. ENDLOOP. ENDMETHOD.
METHOD get_instance_features. " Feature Control (wie bei Managed) READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_travel).
result = VALUE #( FOR travel IN lt_travel ( %tky = travel-%tky %features-%action-acceptTravel = COND #( WHEN travel-Status = 'O' THEN if_abap_behv=>fc-o-enabled ELSE if_abap_behv=>fc-o-disabled ) ) ). ENDMETHOD.
METHOD validateDates. " Identisch zu Managed " ... (siehe Managed-Beispiel oben) ENDMETHOD.
METHOD setStatusNew. " Identisch zu Managed " ... (siehe Managed-Beispiel oben) ENDMETHOD.
METHOD acceptTravel. " Identisch zu Managed " ... (siehe Managed-Beispiel oben) ENDMETHOD.
ENDCLASS.Was Sie SELBST schreiben müssen:
- ✅
SELECT * FROM ztravel– Sie müssen lesen - ✅
INSERT ztravel FROM ...– Sie müssen schreiben - ✅
UPDATE ztravel SET ...– Sie müssen updaten - ✅
DELETE FROM ztravel– Sie müssen löschen - ✅ Lock-Handling – Sie müssen sperren/entsperren
- ✅ Nummernvergabe – Sie müssen Nummernkreise aufrufen
Vergleich: Managed vs. Unmanaged
| Aspekt | Managed | Unmanaged |
|---|---|---|
| CRUD-Implementierung | Automatisch durch Framework | Manuell implementieren |
| DB-Zugriffe | Framework | Sie selbst (SELECT, INSERT, etc.) |
| Nummernvergabe | field ( numbering : managed ) | Manuell via cl_numberrange_runtime |
| Lock-Handling | Automatisch | ENQUEUE/DEQUEUE manuell |
| Draft-Support | ✅ Ja (out-of-the-box) | ❌ Nein |
| Entwicklungsaufwand | 🟢 Gering (nur Business Logic) | 🔴 Hoch (alles selbst) |
| Flexibilität | 🔴 Eingeschränkt (Framework-Regeln) | 🟢 Maximum (volle Kontrolle) |
| Legacy-Integration | 🔴 Schwierig | 🟢 Einfach (BAPIs etc.) |
| Performance-Tuning | 🔴 Limitiert | 🟢 Volle Kontrolle |
| Best for | Neue Cloud-Apps | Legacy-Migration |
| Lernkurve | 🟢 Flach (weniger Code) | 🔴 Steil (viel Code) |
Hybrides Szenario: Managed save + Unmanaged side effects
Problem: Sie wollen Managed nutzen, aber brauchen Custom-Logik nach dem Save (z.B. externe API aufrufen).
Lösung: save_modified Hook in Managed Szenario:
managed implementation in class zbp_i_travel unique;strict ( 2 );with additional save; " ← Hook aktivieren
define behavior for ZI_Travel alias Travelpersistent table ztravel{ create; update; delete; // ...}Implementation:
CLASS lsc_travel DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS: " save_modified: NACH Framework-Save, VOR finalem COMMIT save_modified REDEFINITION,
" cleanup_finalize: NACH COMMIT (oder ROLLBACK) cleanup_finalize REDEFINITION.ENDCLASS.
CLASS lsc_travel IMPLEMENTATION.
METHOD save_modified. " Framework hat BEREITS in DB geschrieben (aber noch nicht committed) " Jetzt können Sie Side-Effects implementieren:
" Beispiel: Für jede neue Travel eine Email senden IF create-travel IS NOT INITIAL. LOOP AT create-travel INTO DATA(ls_created). " Email-API aufrufen (siehe /email-sending/) TRY. cl_email_sender=>send_notification( recipient = 'travel@company.com' subject = |Neue Reise { ls_created-TravelId } erstellt| body = |Reise von { ls_created-BeginDate } bis { ls_created-EndDate }| ). CATCH cx_send_req_bcs INTO DATA(lx_email). " Fehler loggen, aber Transaktion NICHT abbrechen cl_bali_log=>create( )->add_item( cl_bali_message_setter=>create_from_exception( lx_email ) )->save( ). ENDTRY. ENDLOOP. ENDIF.
" Beispiel: Externe API für Updates aufrufen IF update-travel IS NOT INITIAL. LOOP AT update-travel INTO DATA(ls_updated). " HTTP-Call zu externem System (siehe /http-client/) DATA(lo_http) = cl_web_http_client_manager=>create_by_http_destination( ... ). " ... HTTP Request senden ENDLOOP. ENDIF. ENDMETHOD.
METHOD cleanup_finalize. " NACH COMMIT oder ROLLBACK " Hier können Sie Cleanup-Logik implementieren " (z.B. temporäre Dateien löschen, Connections schließen) ENDMETHOD.
ENDCLASS.Wann nutzen?
- Managed für Standard-CRUD + DB-Operationen
save_modifiedfür Side-Effects (Email, externe APIs, Logging)- Volle Framework-Features (Draft, Numbering, etc.) bleiben erhalten
Entscheidungsbaum
┌─────────────────────────────────────────────────┐│ Neue Anwendung (Greenfield)? │└──┬────────────────────────────────────────┬─────┘ │ Ja │ Nein ▼ ▼┌─────────────────────────────┐ ┌─────────────────────────────┐│ Standard-CRUD ausreichend? │ │ Legacy-System integrieren? │└──┬──────────────────────┬───┘ └──┬──────────────────────┬───┘ │ Ja │ Nein │ Ja │ Nein ▼ ▼ ▼ ▼┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────────┐│ MANAGED │ │ Komplexe DB- │ │UNMANAGED │ │ Spezielle ││ │ │ Operationen? │ │ │ │ Transaktion? ││ ✅ │ └──┬───────┬───┘ │ ✅ │ └──┬───────┬───┘└──────────┘ │ Ja │ Nein └──────────┘ │ Ja │ Nein ▼ ▼ ▼ ▼ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌──────────┐ │UNMANAGED │ │ MANAGED + │ │UNMANAGED │ │ MANAGED │ │ │ │save_modified│ │ │ │ │ │ ✅ │ │ ✅ │ │ ✅ │ │ ✅ │ └──────────┘ └────────────┘ └──────────┘ └──────────┘Migration: Von Unmanaged zu Managed
Szenario: Sie haben ein Unmanaged BO und wollen zu Managed migrieren.
Schritte:
- Behavior Definition anpassen:
" Vorher:unmanaged implementation in class zbp_i_travel unique;
" Nachher:managed implementation in class zbp_i_travel unique;
" Zusätzlich:define behavior for ZI_Travel alias Travelpersistent table ztravel " ← Hinzufügen// draft table zdraft_travel ← Optional: Draft aktivierenlock master{ create; update; delete;
field ( numbering : managed ) TravelId; " ← Statt manueller Nummernvergabe // ...}- Behavior Implementation aufräumen:
" Vorher (Unmanaged): Alle MethodenCLASS lhc_travel DEFINITION ... METHODS: create FOR MODIFY ..., update FOR MODIFY ..., delete FOR MODIFY ..., read FOR READ ..., lock FOR LOCK ..., validateDates FOR VALIDATE ..., // etc.
" Nachher (Managed): Nur Business Logic behaltenCLASS lhc_travel DEFINITION ... METHODS: " ❌ create, update, delete, read, lock → LÖSCHEN! " ✅ Nur Business Logic: validateDates FOR VALIDATE ..., setStatusNew FOR DETERMINE ..., acceptTravel FOR MODIFY ...- DB-Zugriffe entfernen:
" ❌ Vorher (Unmanaged):METHOD create. INSERT ztravel FROM @( CORRESPONDING #( entities ) ). " ...ENDMETHOD.
" ✅ Nachher (Managed): Methode komplett löschen!" Framework macht INSERT automatisch- Nummernvergabe:
" ❌ Vorher (Unmanaged):<fs_travel>-TravelId = cl_numberrange_runtime=>get_next_number( ... ).
" ✅ Nachher (Managed):" In BDEF: field ( numbering : managed ) TravelId;" → Framework vergibt automatisch aus `persistent table ztravel`-Key-SequenzWichtige Hinweise / Best Practice
- Default = Managed: Nutzen Sie Managed, außer Sie haben einen guten Grund für Unmanaged
- Draft benötigt Managed: Draft-Funktionalität ist NUR in Managed verfügbar
- Unmanaged für Legacy: Wenn Sie BAPIs/FuBas einbinden müssen → Unmanaged
- Hybrid möglich:
managed+with additional savefür Side-Effects - Performance: Managed ist NICHT langsamer – Framework ist optimiert
- Testing: Managed ist einfacher zu testen (weniger Code = weniger Fehler)
- Lock-Objekte: In Unmanaged müssen Sie Lock-Objekte (SE11: ENQUEUE_*) selbst erstellen
- Nummernkreise: In Unmanaged müssen Sie Nummernkreise (SNRO) selbst verwalten
- Transaktionen: Unmanaged gibt volle Kontrolle über
COMMIT WORKundROLLBACK - Migration: Von Unmanaged → Managed ist aufwändiger als andersherum
- Dokumentation: Begründen Sie Unmanaged-Entscheidung (für künftige Entwickler)
- IN LOCAL MODE: In BEIDEN Szenarien für Behavior-Implementation-Code verwenden
- EML nutzen: Auch in Unmanaged sollten Sie EML für BO-Zugriffe nutzen (siehe EML Guide)
Weitere Ressourcen
- RAP Basics: /rap-basics/
- EML Guide: /eml-entity-manipulation-language/
- ABAP Cloud: /abap-cloud-definition/