Late vs Early Numbering dans RAP : Choisir la bonne stratégie

Catégorie
RAP
Publié
Auteur
Johannes

Le Numbering dans RAP définit quand et comment les clés primaires sont attribuées pour les nouvelles instances de Business Object. Le choix entre Early et Late Numbering a un impact direct sur l’architecture de votre application.

Concept de base

StratégieMomentClé disponibleCas d’usage
Early NumberingÀ CREATEImmédiatement après créationStandard pour la plupart des scénarios
Late NumberingÀ SAVESeulement après sauvegardeNumber Ranges externes, systèmes legacy

Quelle stratégie choisir ?

Choisir Early Numbering si :

  • La clé est nécessaire immédiatement après la création
  • Les Child-Entities doivent être référencées directement
  • Le numéro provient d’un Number Range interne
  • Vous avez un scénario Managed sans dépendances legacy

Choisir Late Numbering si :

  • Le numéro est attribué par un système externe
  • Un Number Range externe est utilisé (ex. numéros de documents)
  • L’attribution de numéro ne doit avoir lieu qu’à la sauvegarde
  • Des scénarios Draft avec attribution ultérieure de numéros sont nécessaires

Early Numbering

Avec Early Numbering, la clé est attribuée directement lors du CREATE. Le framework prend en charge l’attribution de numéro automatiquement ou vous l’implémentez vous-même.

Managed Early Numbering

Le scénario le plus simple : Le framework attribue automatiquement des UUID ou vous utilisez des Number Ranges.

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
etag master LastChangedAt
{
create;
update;
delete;
// Automatische Nummernvergabe durch das Framework (UUID)
field ( numbering : managed ) TravelUUID;
// Alternative: Schlüssel ist readonly, wird in Determination gesetzt
field ( readonly ) TravelId;
}

Early Numbering avec Number Range

Pour des numéros séquentiels provenant d’un Number Range :

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
etag master LastChangedAt
early numbering
{
create;
update;
delete;
// Readonly, da früh vergeben
field ( readonly ) TravelId;
}

Implémentation de Early Numbering

CLASS lhc_travel DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS earlynumbering_create FOR NUMBERING
IMPORTING entities FOR CREATE Travel.
ENDCLASS.
CLASS lhc_travel IMPLEMENTATION.
METHOD earlynumbering_create.
DATA: lv_travel_id TYPE /dmo/travel_id.
" Number Range Object und Intervall
DATA(lv_nr_object) = 'ZTRAVEL'.
DATA(lv_nr_interval) = '01'.
LOOP AT entities INTO DATA(ls_entity) WHERE TravelId IS INITIAL.
TRY.
" Nummer aus Number Range holen
cl_numberrange_runtime=>number_get(
EXPORTING
nr_range_nr = lv_nr_interval
object = lv_nr_object
IMPORTING
number = DATA(lv_number)
).
lv_travel_id = lv_number.
" Mapping: %cid -> generierter Schlüssel
APPEND VALUE #(
%cid = ls_entity-%cid
%key = ls_entity-%key
%is_draft = ls_entity-%is_draft
TravelId = lv_travel_id
) TO mapped-travel.
CATCH cx_number_ranges INTO DATA(lx_nr).
" Fehler bei Nummernvergabe
APPEND VALUE #(
%cid = ls_entity-%cid
%key = ls_entity-%key
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = lx_nr->get_text( )
)
) TO reported-travel.
APPEND VALUE #(
%cid = ls_entity-%cid
%key = ls_entity-%key
) TO failed-travel.
ENDTRY.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Late Numbering

Avec Late Numbering, la clé est attribuée seulement lors de la sauvegarde. Jusqu’alors, le système travaille avec des ID temporaires (%pid - preliminary ID).

Cas d’usage pour Late Numbering

  • Number Ranges externes qui ne sont disponibles qu’au commit
  • Systèmes legacy qui attribuent les numéros lors de la sauvegarde
  • Numéros de documents qui ne sont créés qu’à la comptabilisation finale
  • Scénarios Draft où le numéro final n’est attribué qu’à l’activation

Behavior Definition Late Numbering

managed implementation in class zbp_i_travel unique;
strict ( 2 );
define behavior for ZI_Travel alias Travel
persistent table ztravel
lock master
authorization master ( instance )
etag master LastChangedAt
late numbering
{
create;
update;
delete;
// Schlüssel wird erst bei SAVE vergeben
field ( readonly ) TravelId;
}

Implémentation de Late Numbering

CLASS lsc_travel DEFINITION INHERITING FROM cl_abap_behavior_saver.
PROTECTED SECTION.
METHODS adjust_numbers REDEFINITION.
ENDCLASS.
CLASS lsc_travel IMPLEMENTATION.
METHOD adjust_numbers.
DATA: lv_travel_id TYPE /dmo/travel_id.
" Number Range Object und Intervall
DATA(lv_nr_object) = 'ZTRAVEL'.
DATA(lv_nr_interval) = '01'.
" Alle neuen Einträge ohne finale Nummer
LOOP AT mapped-travel ASSIGNING FIELD-SYMBOL(<ls_travel>)
WHERE %is_draft = if_abap_behv=>mk-off.
IF <ls_travel>-TravelId IS INITIAL.
TRY.
" Nummer aus Number Range holen
cl_numberrange_runtime=>number_get(
EXPORTING
nr_range_nr = lv_nr_interval
object = lv_nr_object
IMPORTING
number = DATA(lv_number)
).
" Finale Nummer zuweisen
<ls_travel>-TravelId = lv_number.
CATCH cx_number_ranges INTO DATA(lx_nr).
" Fehlerbehandlung
RAISE SHORTDUMP lx_nr.
ENDTRY.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Late Numbering avec Draft

Dans les scénarios Draft avec Late Numbering, le draft travaille avec un ID préliminaire. Le numéro final n’est attribué qu’à l’activation :

managed implementation in class zbp_i_travel unique;
strict ( 2 );
with draft;
define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table zdraft_travel
lock master total etag LastChangedAt
authorization master ( instance )
etag master LastChangedAt
late numbering
{
create;
update;
delete;
field ( readonly ) TravelId;
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;
}

Managed vs Unmanaged Numbering

Managed Numbering

Le framework prend en charge l’attribution complète des numéros :

define behavior for ZI_Travel alias Travel
{
// UUID wird automatisch generiert
field ( numbering : managed ) TravelUUID;
}

Avantages :

  • Aucun code requis
  • UUID garantis uniques
  • Disponible immédiatement (Early)

Inconvénients :

  • Uniquement des UUID possibles
  • Pas de numéros parlants

Unmanaged Numbering (Early)

Vous implémentez l’attribution de numéro vous-même à CREATE :

define behavior for ZI_Travel alias Travel
early numbering
{
field ( readonly ) TravelId;
}

Avantages :

  • Contrôle total sur le format de numéro
  • Number Ranges possibles
  • Clé disponible immédiatement

Inconvénients :

  • Plus d’effort d’implémentation
  • Vous devez assurer l’unicité

Unmanaged Numbering (Late)

Vous implémentez l’attribution de numéro à SAVE :

define behavior for ZI_Travel alias Travel
late numbering
{
field ( readonly ) TravelId;
}

Avantages :

  • Number Ranges externes possibles
  • Numéros seulement au commit
  • Séquence de numéros sans lacune possible

Inconvénients :

  • Clé inconnue pendant la transaction
  • Référencement de Child-Entities plus complexe
  • Gestion de %pid requise

Gestion de %pid (Preliminary ID)

Avec Late Numbering, le framework utilise un ID préliminaire (%pid) jusqu’à ce que le numéro final soit attribué :

" CREATE mit Late Numbering
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( AgencyId CustomerId )
WITH VALUE #( (
%cid = 'CID_1"
" TravelId ist noch nicht bekannt!
AgencyId = '000001"
CustomerId = '000010"
) )
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).
" Mapped enthält %pid statt finalem Schlüssel
" mapped-travel[ 1 ]-%pid ist gesetzt
" mapped-travel[ 1 ]-TravelId ist initial
" Erst nach COMMIT ENTITIES ist TravelId gesetzt
COMMIT ENTITIES.

Child-Entities avec Late Numbering

Pour les relations Parent-Child avec Late Numbering :

" Behavior Definition
define behavior for ZI_Travel alias Travel
late numbering
{
association _Booking { create; }
}
define behavior for ZI_Booking alias Booking
{
// Booking referenziert Travel über %pid
}
" EML: Create Parent und Child gleichzeitig
MODIFY ENTITIES OF zi_travel
ENTITY Travel
CREATE FIELDS ( AgencyId CustomerId )
WITH VALUE #( ( %cid = 'TRAVEL_1' AgencyId = '000001' CustomerId = '000010' ) )
CREATE BY \_Booking FIELDS ( BookingId FlightDate )
WITH VALUE #( (
%cid_ref = 'TRAVEL_1' " Referenz auf Parent via %cid
%target = VALUE #( (
%cid = 'BOOKING_1"
BookingId = '0001"
FlightDate = sy-datum
) )
) )
MAPPED mapped
FAILED failed
REPORTED reported.

Matrice de décision

CritèreEarly + ManagedEarly + UnmanagedLate
UUID comme clé✅ Idéal
Number Range (interne)✅ Idéal⚠️ Possible
Number Range (externe)✅ Idéal
Clé disponible immédiatement
Référencement ChildSimpleSimpleVia %pid
Effort d’implémentationMinimalMoyenPlus élevé
Intégration legacy⚠️✅ Idéal

Bonnes pratiques

  1. Managed Numbering par défaut - Utilisez UUID et numbering : managed si possible
  2. Early Numbering pour Number Ranges - Si vous avez besoin de numéros parlants, mais que le numéro doit être disponible immédiatement
  3. Late Numbering uniquement si nécessaire - Seulement pour les systèmes externes ou besoins spéciaux
  4. Utiliser %cid de manière cohérente - Particulièrement important avec Late Numbering pour les références Parent-Child
  5. Gestion des erreurs - Les Number Ranges peuvent générer des erreurs (Overflow, Lock)
  6. Testabilité - Late Numbering complique les tests unitaires, car les clés ne sont connues que tardivement

Sujets avancés