RAP Basics Part 2: Data Model Design — Tables, Keys, Administrative Fields and Interface Views
Design the persistence and interface layer of a RAP BO: UUID versus semantic keys, the administrative fields RAP needs for ETags and draft (created/changed by/at, local and total last changed), client handling, and building the interface root view entity on top of the table.
Explanation
📌 KEY POINTS
✅ A RAP BO starts with its own database tables (one per entity); the table design decides keys, numbering and concurrency handling.
✅ UUID keys (sysuuid_x16) allow managed early numbering — the framework fills them; semantic keys (order number) need your own numbering logic.
✅ Administrative fields with @Semantics annotations let the framework fill created/changed by/at automatically and support ETag and draft total ETag.
⚠️ Without local_last_changed_at (instance ETag) and last_changed_at (total ETag for draft), concurrent changes can silently overwrite each other.
💡 Keep a human-readable ID (TravelID) next to the UUID key — users never see the UUID.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Part 1 showed the layers. Now we design the bottom layer: tables and interface views.
━━━━━━━━━━━━━━━━━━━━
▶ 1. ONE TABLE PER ENTITY
- Root table (ZTRAVEL_A) and child tables (ZBOOKING_A), client-dependent (field client first).
- Child table repeats the parent key (parent_uuid) to support composition.
- Draft tables (ZTRAVEL_D) are generated later from the BDEF — do not design them by hand.
━━━━━━━━━━━━━━━━━━━━
▶ 2. KEY STRATEGY
- UUID key: technical, 16-byte raw, filled by "numbering : managed" — simplest, no number range needed.
- Semantic key: business number from a number range; requires early numbering (method) or late numbering (at save).
- Common pattern: UUID as key + semantic ID filled by a determination on save.
━━━━━━━━━━━━━━━━━━━━
▶ 3. ADMINISTRATIVE FIELDS
- local_created_by / local_created_at, local_last_changed_by, local_last_changed_at, last_changed_at.
- local_last_changed_at: instance ETag — changes on every change of this instance.
- last_changed_at: total ETag — used by draft to detect changes to the whole BO.
- Annotate them in the interface view; the managed runtime fills them.
━━━━━━━━━━━━━━━━━━━━
▶ 4. THE INTERFACE VIEW
- define root view entity ZI_Travel as select from ztravel_a — the root carries "root".
- Child: define view entity ZI_Booking … with association to parent (Part 5).
- Give CamelCase aliases; this becomes the BO's field names used in BDEF and EML.
- Authorization check annotation #CHECK and a DCL (Part 11).
━━━━━━━━━━━━━━━━━━━━
▶ 5. DATA TYPES
- Use data elements with labels; amounts with currency reference (@Semantics.amount.currencyCode).
- Status fields as single-character codes with a value-help view.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Create ZTRAVEL_A with client, travel_uuid (key), travel_id, business fields, admin fields.
- Create ZI_Travel as root view entity with aliases and @Semantics annotations.
- Activate and run a data preview.
- Plan how travel_id will be filled (determination on save).
✅ CHECKPOINT: you can explain why a UUID key plus a readable ID is the most common RAP pattern.
Code example
// ===== STEP 1: persistence table (ADT table editor) =====@EndUserText.label : 'Travel - persistence'@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE@AbapCatalog.tableCategory : #TRANSPARENT@AbapCatalog.deliveryClass : #A@AbapCatalog.dataMaintenance : #RESTRICTEDdefine table ztravel_a { key client : abap.clnt not null; key travel_uuid : sysuuid_x16 not null; travel_id : abap.numc(8); agency_id : abap.numc(6); customer_id : abap.numc(6); @Semantics.amount.currencyCode : 'ztravel_a.currency_code' total_price : abap.curr(16,2); currency_code : abap.cuky; overall_status : abap.char(1); local_created_by : abp_creation_user; local_created_at : abp_creation_tstmpl; local_last_changed_by : abp_locinst_lastchange_user; local_last_changed_at : abp_locinst_lastchange_tstmpl; last_changed_at : abp_lastchange_tstmpl;} // ===== STEP 2: interface root view entity =====@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Travel - interface'define root view entity ZI_Travel as select from ztravel_a{ key travel_uuid as TravelUUID, travel_id as TravelID, agency_id as AgencyID, customer_id as CustomerID, @Semantics.amount.currencyCode: 'CurrencyCode' total_price as TotalPrice, currency_code as CurrencyCode, overall_status as OverallStatus, // ===== STEP 3: administrative fields - filled by the managed runtime ===== @Semantics.user.createdBy: true local_created_by as LocalCreatedBy, @Semantics.systemDateTime.createdAt: true local_created_at as LocalCreatedAt, @Semantics.user.localInstanceLastChangedBy: true local_last_changed_by as LocalLastChangedBy, @Semantics.systemDateTime.localInstanceLastChangedAt: true local_last_changed_at as LocalLastChangedAt, @Semantics.systemDateTime.lastChangedAt: true last_changed_at as LastChangedAt}Real project scenario
An animal-health client's first RAP BO for trade promotions used the promotion number from a number range as the table key, drawn in early numbering. During testing with draft, users abandoned many drafts, burning thousands of numbers and creating gaps that finance auditors questioned. The architect redesigned with a UUID key (managed numbering) and moved the promotion number to a determination on save, drawing the number only when the promotion was actually saved. Gaps disappeared, and the admin fields with @Semantics annotations gave the audit team created/changed information without any code.
Common mistakes
- Using a semantic number-range key with draft, burning numbers for abandoned drafts.
- Missing local_last_changed_at / last_changed_at, so ETag handling and draft total ETag do not work.
- Forgetting @Semantics annotations, so the managed runtime does not fill admin fields.
- Designing draft tables by hand instead of generating them from the BDEF.
- Showing the UUID to users instead of a readable ID.
Best practices
- Default to UUID keys with managed numbering; add a readable ID filled on save.
- Always include the five standard admin fields with the ABP data elements.
- Keep one table per entity and repeat the parent key in child tables.
- Use CamelCase aliases in interface views — they become the BO field names.
- Use currency/unit references for amounts and quantities from day one.
Interview angle
Questions: "UUID or semantic key in RAP — what do you choose?", "Which administrative fields does a RAP BO need and why?"
Strong answer: UUID keys with numbering : managed are the default because the framework fills them and draft does not waste numbers; a semantic ID is still stored and filled by a determination on save or late numbering. Names local_last_changed_at as instance ETag and last_changed_at as total ETag for draft, and explains that @Semantics annotations make the managed runtime fill created/changed fields.
Practice this lesson
Quick check
1. A draft-enabled RAP BO uses a number-range number as table key, drawn when the instance is created. What typical problem appears in production?
2. What does field ( readonly, numbering : managed ) TravelUUID; in a managed BDEF achieve?
3. In a draft-enabled RAP BO, which administrative field is used as the total ETag (lock master total etag …)?
Interview questions
1. A managed RAP BO on custom tables went live quickly, but users report that two people editing the same record overwrite each other's changes, and the "Changed by" column is always empty. Which part of the data model is missing?
Answer it in your own words first, then compare.
2. Your team designed a draft-enabled RAP BO for trade promotions with the promotion number (from a number range) as the table key, drawn in early numbering. Auditors now complain about large gaps in promotion numbers. What went wrong and how would you redesign the keys?
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
- RAP Basics Part 1: RAP Architecture — Layers, Artifacts and When RAP Fits
- RAP Basics Part 10: EML — Entity Manipulation Language
- RAP Basics Part 11: Draft, Locking and Authorization
- RAP Basics Part 3: Projection Layer and Metadata Extensions
- RAP Basics Part 4: Implementation Types — Managed, Unmanaged, Additional Save and Unmanaged Save
- RAP Basics Part 5: Root Entity, Child Entity and Composition
- RAP Basics Part 6: Behavior Definition — CRUD, Field Control and Features
- RAP Basics Part 7: Actions, Determinations and Validations — Design Decisions