Late vs Early Numbering in RAP: Die richtige Strategie wählen

kategorie
RAP
Veröffentlicht
autor
Johannes

Numbering in RAP definiert, wann und wie Primärschlüssel für neue Business-Object-Instanzen vergeben werden. Die Wahl zwischen Early und Late Numbering hat direkte Auswirkungen auf die Architektur Ihrer Anwendung.

Grundkonzept

StrategieZeitpunktSchlüssel verfügbarAnwendungsfall
Early NumberingBei CREATESofort nach AnlageStandard für die meisten Szenarien
Late NumberingBei SAVEErst nach SpeicherungExterne Number Ranges, Legacy-Systeme

Wann welche Strategie?

Early Numbering wählen, wenn:

  • Der Schlüssel sofort nach der Anlage benötigt wird
  • Child-Entities direkt referenziert werden müssen
  • Die Nummer aus einer internen Number Range kommt
  • Sie ein Managed Szenario ohne Legacy-Abhängigkeiten haben

Late Numbering wählen, wenn:

  • Die Nummer von einem externen System vergeben wird
  • Eine externe Number Range verwendet wird (z.B. Belegnummern)
  • Die Nummernvergabe erst beim Speichern erfolgen darf
  • Draft-Szenarien mit späteren Nummernzuweisungen benötigt werden

Early Numbering

Bei Early Numbering wird der Schlüssel direkt beim CREATE vergeben. Das Framework übernimmt die Nummernvergabe automatisch oder Sie implementieren sie selbst.

Managed Early Numbering

Das einfachste Szenario: Das Framework vergibt automatisch UUIDs oder Sie nutzen 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 mit Number Range

Für fortlaufende Nummern aus einer 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;
}

Implementation der 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

Bei Late Numbering wird der Schlüssel erst beim Speichern vergeben. Bis dahin arbeitet das System mit temporären IDs (%pid - preliminary ID).

Anwendungsfälle für Late Numbering

  • Externe Number Ranges die nur beim Commit verfügbar sind
  • Legacy-Systeme die Nummern bei der Speicherung vergeben
  • Belegnummern die erst bei finaler Buchung entstehen
  • Draft-Szenarien wo die finale Nummer erst bei Aktivierung vergeben wird

Late Numbering Behavior Definition

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;
}

Implementation der 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 mit Draft

Bei Draft-Szenarien mit Late Numbering arbeitet der Draft mit einer vorläufigen ID. Die finale Nummer wird erst bei Aktivierung vergeben:

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

Das Framework übernimmt die komplette Nummernvergabe:

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

Vorteile:

  • Kein Code erforderlich
  • Garantiert eindeutige UUIDs
  • Sofort verfügbar (Early)

Nachteile:

  • Nur UUIDs möglich
  • Keine sprechenden Nummern

Unmanaged Numbering (Early)

Sie implementieren die Nummernvergabe selbst bei CREATE:

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

Vorteile:

  • Volle Kontrolle über Nummernformat
  • Number Ranges möglich
  • Schlüssel sofort verfügbar

Nachteile:

  • Mehr Implementierungsaufwand
  • Sie müssen Eindeutigkeit sicherstellen

Unmanaged Numbering (Late)

Sie implementieren die Nummernvergabe beim SAVE:

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

Vorteile:

  • Externe Number Ranges möglich
  • Nummern erst bei Commit
  • Lückenlose Nummernfolge möglich

Nachteile:

  • Schlüssel während der Transaktion nicht bekannt
  • Komplexere Referenzierung von Child-Entities
  • %pid-Handling erforderlich

Umgang mit %pid (Preliminary ID)

Bei Late Numbering verwendet das Framework eine vorläufige ID (%pid), bis die finale Nummer vergeben wird:

" 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 mit Late Numbering

Bei Parent-Child-Beziehungen mit 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.

Entscheidungsmatrix

KriteriumEarly + ManagedEarly + UnmanagedLate
UUID als Schlüssel✅ Ideal
Number Range (intern)✅ Ideal⚠️ Möglich
Number Range (extern)✅ Ideal
Schlüssel sofort verfügbar
Child-ReferenzierungEinfachEinfachVia %pid
ImplementierungsaufwandMinimalMittelHöher
Legacy-Integration⚠️✅ Ideal

Best Practices

  1. Managed Numbering als Standard - Nutzen Sie UUID und numbering : managed wenn möglich
  2. Early Numbering für Number Ranges - Wenn Sie sprechende Nummern brauchen, aber die Nummer sofort verfügbar sein soll
  3. Late Numbering nur wenn nötig - Nur bei externen Systemen oder speziellen Anforderungen
  4. %cid konsequent nutzen - Besonders wichtig bei Late Numbering für Parent-Child-Referenzen
  5. Fehlerbehandlung - Number Ranges können Fehler werfen (Overflow, Lock)
  6. Testbarkeit - Late Numbering erschwert Unit Tests, da Schlüssel erst spät bekannt sind

Weiterführende Themen