Managed vs. Unmanaged est la décision architecturale centrale dans RAP (RESTful ABAP Programming). Elle détermine qui gère le contrôle transactionnel et les opérations CRUD : le framework RAP (Managed) ou vous-même (Unmanaged).
La question fondamentale
" Qui exécute CREATE, UPDATE, DELETE ?
" Managed : "SAP, fais-le !"managed implementation in class zbp_i_travel unique;
" Unmanaged : "Je le fais moi-même !"unmanaged implementation in class zbp_i_travel unique;Scénario Managed : Le framework fait le travail
Quand utiliser Managed ?
✅ Parfait pour :
- Nouvelles applications (développement Greenfield)
- Processus métier standards sans logique legacy complexe
- Applications Fiori transactionnelles avec opérations CRUD
- Quand vous avez besoin rapidement d’un BO fonctionnel
- Fonctionnalité Draft (sauvegarde intermédiaire)
❌ Non adapté pour :
- Intégration avec du code legacy (modules fonction, BAPIs)
- Logique transactionnelle complexe en dehors de RAP
- Quand vous avez besoin d’un contrôle total sur les accès DB
- Migration de Dynpro/Web Dynpro avec comportement existant
Managed : Behavior Definition
managed implementation in class zbp_i_travel unique;strict ( 2 );with draft; " Draft uniquement disponible en Managed !
define behavior for ZI_Travel alias Travelpersistent table ztravel " Le framework écrit ici automatiquementdraft table zdraft_travel " Pour les données Draftlock master " Le framework gère les verroustotal etag LastChangedAt " Optimistic Locking via ETagauthorization master ( instance ){ // CRUD : Seulement DÉCLARER, pas d'implémentation nécessaire ! create; update; delete;
// Champs : Le framework s'occupe du mapping field ( readonly ) TravelId; field ( readonly ) CreatedBy, CreatedAt, LastChangedBy, LastChangedAt; field ( numbering : managed ) TravelId; " Attribution automatique de numéro !
// Logique métier : C'est ici que vous implémentez validation validateDates on save { field BeginDate, EndDate; } determination setStatusNew on modify { create; } action acceptTravel result [1] $self;
// Draft Actions : Le framework les fournit automatiquement draft action Edit; draft action Activate optimized; draft action Discard; draft action Resume;
// Associations association _Bookings { create; with draft; }}
define behavior for ZI_Booking alias Bookingpersistent table zbookingdraft table zdraft_bookinglock dependent by _Travel " Verrou du 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: " ✅ Implémentez UNIQUEMENT la logique métier !
" Validation : Exécutée lors de la sauvegarde validateDates FOR VALIDATE ON SAVE IMPORTING keys FOR Travel~validateDates,
" Determination : Définir des valeurs automatiques setStatusNew FOR DETERMINE ON MODIFY IMPORTING keys FOR Travel~setStatusNew,
" Action : Opération métier acceptTravel FOR MODIFY IMPORTING keys FOR ACTION Travel~acceptTravel RESULT result.
" ❌ NE PAS implémenter : get_global_authorizations, read, create, update, delete " → Le framework fait ça automatiquement !ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD validateDates. " Le framework a DÉJÀ lu les données → valider seulement 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). " Vérifier la règle métier IF ls_travel-EndDate < ls_travel-BeginDate. " Remplir la structure framework pour l'erreur 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 = 'La date de fin doit être après la date de début" ) ) TO reported-travel. ENDIF. ENDLOOP. ENDMETHOD.
METHOD setStatusNew. " Le framework a créé l'entité → définir les valeurs par défaut READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_travel).
" Mettre uniquement les nouveaux (Status initial) à 'O' (Open) 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 = Opération métier (pas seulement un 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.
" Retourner le résultat (result [1] $self) READ ENTITIES OF zi_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT result. ENDMETHOD.
ENDCLASS.Ce que vous n’avez PAS à écrire :
- ❌
SELECT * FROM ztravel– Le framework le fait - ❌
INSERT ztravel FROM ...– Le framework le fait - ❌
UPDATE ztravel SET ...– Le framework le fait - ❌
DELETE FROM ztravel– Le framework le fait - ❌ Gestion des verrous – Le framework le fait
- ❌ Attribution de numéros – Le framework le fait (avec
numbering : managed)
Scénario Unmanaged : Vous avez le contrôle total
Quand utiliser Unmanaged ?
✅ Parfait pour :
- Intégration Legacy (BAPIs, modules fonction à intégrer)
- Logique transactionnelle complexe (commits multi-étapes)
- Migration d’applications Dynpro/Web-Dynpro existantes
- Quand vous avez besoin d’opérations DB spéciales (par ex. Native SQL)
- Mécanismes de verrouillage personnalisés
❌ Non adapté pour :
- Prototypage rapide
- CRUD standard sans particularités
- Fonctionnalité Draft (non disponible en 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 : Tout doit être implémenté ! create; update; delete;
// Read DOIT aussi être implémenté (contrairement à Managed !)
// Logique métier comme en Managed validation validateDates on save { field BeginDate, EndDate; } determination setStatusNew on modify { create; } action acceptTravel result [1] $self;
// Gestion des verrous VOUS devez l'implémenter lock ( lock_key );}Unmanaged : Behavior Implementation
CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler. PRIVATE SECTION. METHODS: " ✅ Tout implémenter SOI-MÊME :
" Create : Créer de nouvelles entités create FOR MODIFY IMPORTING entities FOR CREATE Travel,
" Update : Modifier des entités existantes update FOR MODIFY IMPORTING entities FOR UPDATE Travel,
" Delete : Supprimer des entités delete FOR MODIFY IMPORTING keys FOR DELETE Travel,
" Read : Lire des entités read FOR READ IMPORTING keys FOR READ Travel RESULT result,
" Lock : Gérer les verrous 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,
" Logique métier (comme en 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. " Extraire les données du paramètre entities DATA lt_travel TYPE TABLE FOR CREATE zi_travel. lt_travel = entities.
" Attribution de numéro (MANUEL, car unmanaged !) LOOP AT lt_travel ASSIGNING FIELD-SYMBOL(<fs_travel>). " Obtenir le numéro de la série de numéros TRY. <fs_travel>-TravelId = cl_numberrange_runtime=>get_next_number( nr_range_nr = '01" object = 'ZTRAVEL" ). CATCH cx_number_ranges INTO DATA(lx_nr). " Gérer l'erreur APPEND VALUE #( %cid = <fs_travel>-%cid %fail-cause = if_abap_behv=>cause-unspecific ) TO failed-travel. CONTINUE. ENDTRY.
" Définir les valeurs par défaut <fs_travel>-CreatedBy = sy-uname. <fs_travel>-CreatedAt = cl_abap_context_info=>get_system_date( ). <fs_travel>-Status = 'O'.
" Écrire dans DB (MANUEL !) INSERT ztravel FROM @( CORRESPONDING #( <fs_travel> ) ).
IF sy-subrc = 0. " Retourner le mapping réussi APPEND VALUE #( %cid = <fs_travel>-%cid TravelId = <fs_travel>-TravelId ) TO mapped-travel. ELSE. " Erreur APPEND VALUE #( %cid = <fs_travel>-%cid %fail-cause = if_abap_behv=>cause-unspecific ) TO failed-travel. ENDIF. ENDLOOP. ENDMETHOD.
METHOD update. " Extraire les champs à mettre à jour LOOP AT entities INTO DATA(ls_entity). " %control vérifie quels champs doivent être modifiés IF ls_entity-%control-Status = if_abap_behv=>mk-on. " Status a été modifié → 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.
" Autres champs de manière analogue... 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. " Supprimer de la DB DELETE FROM ztravel WHERE travel_id IN ( SELECT TravelId FROM @keys AS k ).
IF sy-dbcnt < lines( keys ). " Pas tous supprimés → Erreur 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. " Existe encore → n'a pas pu être supprimé APPEND VALUE #( %tky = ls_key-%tky %fail-cause = if_abap_behv=>cause-locked ) TO failed-travel. ENDIF. ENDLOOP. ENDIF. ENDMETHOD.
METHOD read. " Lire les données de la DB SELECT * FROM ztravel FOR ALL ENTRIES IN @keys WHERE travel_id = @keys-TravelId INTO TABLE @DATA(lt_db_travel).
" Convertir en structure RAP result = CORRESPONDING #( lt_db_travel MAPPING TO ENTITY ). ENDMETHOD.
METHOD lock. " Verrouiller l'objet de verrouillage (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. " Verrouillage échoué 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 (comme en 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. " Identique à Managed " ... (voir exemple Managed ci-dessus) ENDMETHOD.
METHOD setStatusNew. " Identique à Managed " ... (voir exemple Managed ci-dessus) ENDMETHOD.
METHOD acceptTravel. " Identique à Managed " ... (voir exemple Managed ci-dessus) ENDMETHOD.
ENDCLASS.Ce que vous devez écrire VOUS-MÊME :
- ✅
SELECT * FROM ztravel– Vous devez lire - ✅
INSERT ztravel FROM ...– Vous devez écrire - ✅
UPDATE ztravel SET ...– Vous devez mettre à jour - ✅
DELETE FROM ztravel– Vous devez supprimer - ✅ Gestion des verrous – Vous devez verrouiller/déverrouiller
- ✅ Attribution de numéros – Vous devez appeler les séries de numéros
Comparaison : Managed vs. Unmanaged
| Aspect | Managed | Unmanaged |
|---|---|---|
| Implémentation CRUD | Automatique par le framework | Implémentation manuelle |
| Accès DB | Framework | Vous-même (SELECT, INSERT, etc.) |
| Attribution de numéros | field ( numbering : managed ) | Manuel via cl_numberrange_runtime |
| Gestion des verrous | Automatique | ENQUEUE/DEQUEUE manuel |
| Support Draft | ✅ Oui (out-of-the-box) | ❌ Non |
| Effort de développement | 🟢 Faible (logique métier uniquement) | 🔴 Élevé (tout soi-même) |
| Flexibilité | 🔴 Limitée (règles du framework) | 🟢 Maximum (contrôle total) |
| Intégration Legacy | 🔴 Difficile | 🟢 Facile (BAPIs etc.) |
| Optimisation des performances | 🔴 Limitée | 🟢 Contrôle total |
| Idéal pour | Nouvelles apps Cloud | Migration Legacy |
| Courbe d’apprentissage | 🟢 Douce (moins de code) | 🔴 Abrupte (beaucoup de code) |
Scénario hybride : Managed save + Unmanaged side effects
Problème : Vous voulez utiliser Managed, mais avez besoin d’une logique personnalisée après la sauvegarde (par ex. appeler une API externe).
Solution : Hook save_modified dans le scénario Managed :
managed implementation in class zbp_i_travel unique;strict ( 2 );with additional save; " ← Activer le hook
define behavior for ZI_Travel alias Travelpersistent table ztravel{ create; update; delete; // ...}Implémentation :
CLASS lsc_travel DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS: " save_modified : APRÈS le save du framework, AVANT le commit final save_modified REDEFINITION,
" cleanup_finalize : APRÈS le COMMIT (ou ROLLBACK) cleanup_finalize REDEFINITION.ENDCLASS.
CLASS lsc_travel IMPLEMENTATION.
METHOD save_modified. " Le framework a DÉJÀ écrit dans la DB (mais pas encore commité) " Maintenant vous pouvez implémenter des side-effects :
" Exemple : Pour chaque nouveau Travel envoyer un email IF create-travel IS NOT INITIAL. LOOP AT create-travel INTO DATA(ls_created). " Appeler l'API Email (voir /email-sending/) TRY. cl_email_sender=>send_notification( subject = |Nouveau voyage { ls_created-TravelId } créé| body = |Voyage du { ls_created-BeginDate } au { ls_created-EndDate }| ). CATCH cx_send_req_bcs INTO DATA(lx_email). " Journaliser l'erreur, mais NE PAS interrompre la transaction cl_bali_log=>create( )->add_item( cl_bali_message_setter=>create_from_exception( lx_email ) )->save( ). ENDTRY. ENDLOOP. ENDIF.
" Exemple : Appeler une API externe pour les mises à jour IF update-travel IS NOT INITIAL. LOOP AT update-travel INTO DATA(ls_updated). " Appel HTTP vers un système externe (voir /http-client/) DATA(lo_http) = cl_web_http_client_manager=>create_by_http_destination( ... ). " ... Envoyer la requête HTTP ENDLOOP. ENDIF. ENDMETHOD.
METHOD cleanup_finalize. " APRÈS COMMIT ou ROLLBACK " Ici vous pouvez implémenter la logique de nettoyage " (par ex. supprimer des fichiers temporaires, fermer des connexions) ENDMETHOD.
ENDCLASS.Quand utiliser ?
- Managed pour les opérations CRUD standard + DB
save_modifiedpour les side-effects (Email, APIs externes, Logging)- Toutes les fonctionnalités du framework (Draft, Numbering, etc.) restent disponibles
Arbre de décision
┌─────────────────────────────────────────────────┐│ Nouvelle application (Greenfield) ? │└──┬────────────────────────────────────────┬─────┘ │ Oui │ Non ▼ ▼┌─────────────────────────────┐ ┌─────────────────────────────┐│ CRUD standard suffit ? │ │ Intégrer système legacy ? │└──┬──────────────────────┬───┘ └──┬──────────────────────┬───┘ │ Oui │ Non │ Oui │ Non ▼ ▼ ▼ ▼┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────────┐│ MANAGED │ │ Opérations │ │UNMANAGED │ │ Transaction ││ │ │ DB complexes?│ │ │ │ spéciale ? ││ ✅ │ └──┬───────┬───┘ │ ✅ │ └──┬───────┬───┘└──────────┘ │ Oui │ Non └──────────┘ │ Oui │ Non ▼ ▼ ▼ ▼ ┌──────────┐ ┌────────────┐ ┌──────────┐ ┌──────────┐ │UNMANAGED │ │ MANAGED + │ │UNMANAGED │ │ MANAGED │ │ │ │save_modified│ │ │ │ │ │ ✅ │ │ ✅ │ │ ✅ │ │ ✅ │ └──────────┘ └────────────┘ └──────────┘ └──────────┘Migration : De Unmanaged vers Managed
Scénario : Vous avez un BO Unmanaged et voulez migrer vers Managed.
Étapes :
- Adapter la Behavior Definition :
" Avant :unmanaged implementation in class zbp_i_travel unique;
" Après :managed implementation in class zbp_i_travel unique;
" En plus :define behavior for ZI_Travel alias Travelpersistent table ztravel " ← Ajouter// draft table zdraft_travel ← Optionnel : Activer Draftlock master{ create; update; delete;
field ( numbering : managed ) TravelId; " ← Au lieu de l'attribution manuelle // ...}- Nettoyer la Behavior Implementation :
" Avant (Unmanaged) : Toutes les méthodesCLASS lhc_travel DEFINITION ... METHODS: create FOR MODIFY ..., update FOR MODIFY ..., delete FOR MODIFY ..., read FOR READ ..., lock FOR LOCK ..., validateDates FOR VALIDATE ..., // etc.
" Après (Managed) : Garder uniquement la logique métierCLASS lhc_travel DEFINITION ... METHODS: " ❌ create, update, delete, read, lock → SUPPRIMER ! " ✅ Seulement la logique métier : validateDates FOR VALIDATE ..., setStatusNew FOR DETERMINE ..., acceptTravel FOR MODIFY ...- Supprimer les accès DB :
" ❌ Avant (Unmanaged) :METHOD create. INSERT ztravel FROM @( CORRESPONDING #( entities ) ). " ...ENDMETHOD.
" ✅ Après (Managed) : Supprimer complètement la méthode !" Le framework fait l'INSERT automatiquement- Attribution de numéros :
" ❌ Avant (Unmanaged) :<fs_travel>-TravelId = cl_numberrange_runtime=>get_next_number( ... ).
" ✅ Après (Managed) :" Dans BDEF : field ( numbering : managed ) TravelId;" → Le framework attribue automatiquement depuis la séquence de clés `persistent table ztravel`Remarques importantes / Bonnes pratiques
- Par défaut = Managed : Utilisez Managed, sauf si vous avez une bonne raison pour Unmanaged
- Draft nécessite Managed : La fonctionnalité Draft est UNIQUEMENT disponible en Managed
- Unmanaged pour Legacy : Si vous devez intégrer des BAPIs/FuBas → Unmanaged
- Hybride possible :
managed+with additional savepour les side-effects - Performance : Managed n’est PAS plus lent – le framework est optimisé
- Testing : Managed est plus facile à tester (moins de code = moins d’erreurs)
- Objets de verrouillage : En Unmanaged, vous devez créer vous-même les objets de verrouillage (SE11 : ENQUEUE_*)
- Séries de numéros : En Unmanaged, vous devez gérer vous-même les séries de numéros (SNRO)
- Transactions : Unmanaged donne un contrôle total sur
COMMIT WORKetROLLBACK - Migration : De Unmanaged → Managed est plus laborieux que l’inverse
- Documentation : Justifiez la décision Unmanaged (pour les futurs développeurs)
- IN LOCAL MODE : À utiliser dans les DEUX scénarios pour le code d’implémentation Behavior
- Utiliser EML : Même en Unmanaged, vous devriez utiliser EML pour les accès BO (voir EML Guide)
Ressources supplémentaires
- RAP Basics : /rap-basics/
- EML Guide : /eml-entity-manipulation-language/
- ABAP Cloud : /abap-cloud-definition/