RAP Build-Along Part 1: Build a Managed RAP App End to End
Build a draft-enabled managed RAP Travel application from persistence tables to CDS business object, behavior, Fiori Elements service, EML tests, authorization, debugging and transport—while understanding why every artifact exists.
What this complete series covers
A 7-part hands-on series that builds one managed, draft-enabled Travel + Booking RAP application from database tables to a Fiori Elements app and production transport.
- Who it is for
- ABAP developers moving to the ABAP RESTful Application Programming Model (RAP), and consultants preparing for RAP interviews.
- Prerequisites
- Basic ABAP (internal tables, classes) and ABAP Development Tools (ADT) in Eclipse. Basic CDS syntax helps but is explained.
- Final outcome
- A working managed RAP business object with draft, validations, actions, authorization, an OData V4 UI service and a Fiori Elements List Report / Object Page.
- Part 1: RAP Build-Along Part 1: Build a Managed RAP App End to End
Understand the full RAP artifact chain and set up package, transport and ADT
- Part 2: RAP Build-Along Part 2: Persistence Tables, Root and Child CDS Views
Create Travel/Booking tables and root/child CDS view entities with composition
- Part 3: RAP Build-Along Part 3: Behavior Definition — Managed, Draft, Locking and ETags
Write the managed, draft-enabled behavior definition with locking, ETags and numbering
- Part 4: RAP Build-Along Part 4: Behavior Implementation — Validations, Determinations and Actions
Implement determinations, validations, actions, messages, feature control and authorization
- Part 5: RAP Build-Along Part 5: Projection Views and Fiori Elements UI Annotations
Build projection views, projection behavior and Fiori Elements UI annotations
- Part 6: RAP Build-Along Part 6: OData V4 Service Binding and EML Testing
Expose an OData V4 service, check $metadata and test end to end with EML
- Part 7: RAP Build-Along Part 7: Clean Core, Security, Performance and Transport
Harden for production: DCL vs modify auth, clean core, ATC, performance, transport
- Part 8: RAP Build-Along Part 8: Managed vs Unmanaged — Choosing the Transaction Model for Your Travel App
Choose managed RAP when framework-owned persistence fits the business object and unmanaged RAP when existing APIs or custom save logic must remain authoritative.
- Part 9: RAP Build-Along Part 9: Production Troubleshooting — Metadata, Behavior, Messages and Save
Troubleshoot RAP by separating metadata/exposure, authorization/features, modify processing, validation and the save sequence rather than debugging the UI first.
System and release assumptions: Written for SAP BTP ABAP environment and recent SAP S/4HANA releases (ABAP Cloud). Syntax such as strict ( 2 ), provider contracts and Activate optimized is release-dependent and labeled in each part. Detailed deep-dives on individual RAP features are covered in the specialized RAP lessons of this topic.
Explanation
📌 KEY POINTS
✅ End-to-end chain: tables → CDS root/child → BDEF → behavior pool → projection → service → binding → Fiori.
✅ Managed RAP: the framework handles CRUD, buffer, locks and persistence.
✅ Business rules go into validations, determinations and actions.
💡 Build it once completely — every later topic deepens one step of this chain.
▶ SAP RAP TUTORIAL: BUILD A MANAGED RAP APP END TO END (PART 1)
This lesson builds the mental model and development sequence that a new RAP developer needs before studying individual artifacts in depth. The example is a managed Travel business object with a root Travel entity and composed child Booking rows. UUID keys make create scenarios and offline-safe key assignment straightforward; draft support gives users a modern edit-and-activate experience.
The names are illustrative. RAP syntax, available annotations, draft capabilities, service-binding labels, released APIs, and generated handler signatures differ across ABAP platform and SAP S/4HANA releases. Treat ADT syntax completion and the documentation for your installed release as authoritative.
━━━━━━━━━━━━━━━━━━━━
▶ 1. PREREQUISITES AND DEVELOPMENT ENVIRONMENT
Use ABAP Development Tools (ADT) in Eclipse. Create the artifacts in a transportable package, assign the correct transport request, and confirm the target landscape before writing code. Local packages are suitable only for disposable experiments.
Check the package's ABAP language version. In an ABAP Cloud language-version package, consume only APIs released for the applicable use contract. A technically visible SAP table, class, CDS entity, or function module is not automatically allowed. Use released business objects, CDS entities, classes, and extension points. A custom wrapper around an unreleased SAP object does not make the dependency clean-core compliant.
You need authorization to create and activate repository objects, publish or preview a service binding where the platform requires it, and test the resulting service. Decide naming, package ownership, transport ownership, and authorization concept before implementation.
━━━━━━━━━━━━━━━━━━━━
▶ 2. ARCHITECTURE MAP: WHAT CALLS WHAT
┏━━ CODE ━━━━━━━━━━━━
┃ Persistence tables (root + child + draft tables)
┃ -> CDS interface/root and child view entities
┃ -> Behavior definition (transaction contract)
┃ -> Behavior implementation (custom transactional rules)
┃ -> CDS projection views (consumer-specific shape)
┃ -> Projection behavior (consumer-specific operations)
┃ -> Metadata extension / UI annotations
┃ -> Service definition (service exposure contract)
┃ -> OData V4 UI service binding (runtime protocol)
┃ -> Fiori Elements preview or generated application
┃ -> EML/service tests, debugging, authorization checks, transport
┗━━━━━━━━━━━━━━━━━━━
Each layer has one job. Persistence stores state. CDS models data and relationships. Behavior declares transactional capabilities. Handler classes implement custom rules. Projection artifacts define the consumer contract. Metadata describes the UI. Service artifacts expose the contract; they do not contain business logic.
━━━━━━━━━━━━━━━━━━━━
▶ 3. CREATE PERSISTENCE TABLES
Create one active table for the root and one for the child. The child carries the root UUID so the composition can be persisted. Keep amounts and currency together. Include framework-friendly administrative timestamps for ETags and auditing. Draft-enabled managed BOs also require draft tables; generate or create them using the tooling supported by your release.
A simplified active-table design is shown in the code section. Important choices are:
travel_uuidandbooking_uuidare UUID keys.booking.travel_uuidis the persistent parent reference.total_pricehas a currency-code field.local_last_changed_atis suitable for an entity ETag.last_changed_atcan represent a total ETag where the release and draft design require one.- Technical fields are managed consistently; do not let clients overwrite them.
A database foreign key can strengthen integrity where supported, but it does not replace RAP composition semantics. Likewise, composition in CDS does not repair bad persisted data.
━━━━━━━━━━━━━━━━━━━━
▶ 4. BUILD CDS INTERFACE ROOT AND CHILD ENTITIES
The interface entities model the reusable business object. The root is declared with root view entity; the child is a normal view entity composed under it. The root publishes a composition to bookings, and the child exposes an association back to its parent.
Use correct cardinalities. A travel can have zero to many bookings; each booking belongs to exactly one travel in this model. Redirect the parent association and composition later in the projection layer.
Apply semantics deliberately:
- Link
TotalPricetoCurrencyCodewith amount/currency annotations. - Annotate dates and text fields according to their meaning.
- Do not put consumer-only layout decisions into reusable interface views.
- Avoid direct access to unreleased SAP persistence in clean-core packages.
- Keep the BO grain explicit: one root row is one travel request, one child row is one booking.
Activate the tables first, then the interface CDS entities. Activation errors at this stage usually indicate field-name, type, association, cardinality, or annotation problems—not behavior problems.
━━━━━━━━━━━━━━━━━━━━
▶ 5. DECLARE MANAGED BEHAVIOR
A managed behavior definition tells the RAP runtime which standard transactional work it owns and which custom hooks your application provides.
For the root, declare create, update, and delete. Permit create through the _Booking association so a consumer can add child rows in the root context. Use lock master on the root and make the child dependent on the parent lock. Make modify authorization master at the root and dependent at the child.
Use ETags for optimistic concurrency. A local ETag detects whether an entity changed since the client read it. Draft scenarios may also need a total ETag for draft consistency; exact clauses and field requirements are release-dependent.
With UUID keys, managed numbering is normally the cleanest option: the runtime assigns the key, and the key field is read-only to consumers. If a business-visible travel number is needed, assign a separate semantic number using a determination, number-range service, or a late-numbering design appropriate to the release. Do not confuse UUID technical keys with business document numbers.
Draft enablement adds edit, activate, discard, and prepare semantics. It requires correctly defined draft persistence and administrative fields. Draft does not remove the need for validation; activation must reject invalid data.
Field control belongs in behavior when it protects transactional integrity. Mark UUIDs and technical audit fields read-only. Mark mandatory business fields where the requirement is unconditional. Use feature control only when editability or action availability depends on instance state.
━━━━━━━━━━━━━━━━━━━━
▶ 6. IMPLEMENT BEHAVIOR LOGIC
Managed RAP supplies generic persistence for declared operations, but business rules still need handler methods.
━━━━━━━━━━━━━━━━━━━━
▶ DETERMINATION
A determination derives data after a trigger. On root creation, set an initial status such as O for Open. Determinations should be idempotent where possible because several changes can occur in one request.
━━━━━━━━━━━━━━━━━━━━
▶ VALIDATION
A validation checks consistency before save or during draft prepare. For example, EndDate must not be before BeginDate. A failed key is placed in FAILED; a user-facing message is placed in REPORTED. FAILED controls transactional failure, while REPORTED communicates messages. A message without the correct failed entry may not prevent persistence.
━━━━━━━━━━━━━━━━━━━━
▶ ACTION
An action expresses a business command such as confirm. Read the selected instances, reject invalid statuses, and update allowed instances through EML in local mode. Return the affected instance when the action contract declares $self.
Use %tky/technical keys and generated types rather than reconstructing keys manually. Prefer READ ENTITIES and MODIFY ENTITIES in the behavior pool so the framework tracks transactional changes.
Do not call COMMIT WORK inside a behavior handler, determination, validation, action, saver hook, or reusable BO method. RAP owns the save sequence for service requests. A premature commit breaks the logical unit of work and can produce partial persistence.
Instance authorization belongs in the behavior implementation when modification rights depend on the entity or its state. Global authorization can answer operation-wide checks. Keep authorization failures explicit in the RAP response.
━━━━━━━━━━━━━━━━━━━━
▶ 7. CREATE PROJECTION VIEWS
Projection views are consumer contracts over the stable interface BO. A mobile application, an integration API, and a Fiori Elements application may need different fields and operations even though they use the same underlying business object.
The root projection redirects its composition to the child projection; the child redirects its parent association to the root projection. Expose only fields the consumer needs. This prevents persistence and internal helper fields from accidentally becoming a public contract.
Projection views are not duplicate persistence models. They shape and rename the existing BO for a service scenario.
━━━━━━━━━━━━━━━━━━━━
▶ 8. DECLARE PROJECTION BEHAVIOR
The projection behavior uses the underlying interface behavior but exposes only intended operations. For the UI service, expose create, update, delete, child creation, required draft actions, and the confirm action. An internal action or field can remain available in the interface BO yet be absent from this consumer contract.
This is defense in depth and contract design—not a substitute for authorization. A hidden operation is not the same as an authorized operation.
━━━━━━━━━━━━━━━━━━━━
▶ 9. ADD METADATA EXTENSION AND UI ANNOTATIONS
Keep Fiori Elements layout annotations in a metadata extension when possible. This separates reusable data semantics from one UI's presentation.
For a List Report/Object Page, define:
- Header information and a meaningful type name.
- Selection fields such as travel number, agency, dates, and status.
- Line items for the list report.
- Identification fields and field groups for the object page.
- A facet for general information.
- A line-item facet for
_Bookingso users can maintain child rows. - Value helps where backed by released, authorized sources.
Annotate actions so Confirm appears in the intended context. Metadata affects rendering and discoverability; it does not implement the action.
━━━━━━━━━━━━━━━━━━━━
▶ 10. CREATE THE SERVICE DEFINITION
The service definition exposes the root and child projection entities as a coherent business graph. It identifies what belongs to this service but contains no validation, save, authorization, or business-processing logic.
Keep the service focused. Exposing every CDS entity in the package increases the contract and can create expensive navigation paths.
━━━━━━━━━━━━━━━━━━━━
▶ 11. CREATE AN ODATA V4 UI SERVICE BINDING
Create a service binding in ADT for the service definition. Prefer an OData V4 UI binding for the transactional Fiori Elements scenario when supported by the target release. Activate it, then publish or make it locally available according to the installed platform's lifecycle.
Validate:
- The service binding is active and references the intended service definition.
- The entity sets and navigation properties appear.
$metadatais reachable and contains the expected fields, actions, compositions, ETags, and draft annotations.- The preview opens with an authorized test user.
- A representative read, create, child create, validation failure, action, and activation work.
Do not follow obsolete SEGW generation as the primary path for a RAP service. Runtime publication and transport behavior vary by platform, so verify the target release rather than copying steps from another system.
━━━━━━━━━━━━━━━━━━━━
▶ 12. VALIDATE THE GENERATED FIORI ELEMENTS APPLICATION
The generated List Report/Object Page should let an authorized user:
- List and filter travels.
- Open a travel object page.
- Create a new travel with a framework-assigned UUID.
- Enter edit mode and work in a draft.
- Add, edit, and remove booking child rows as allowed.
- See validation messages attached to the correct entity or field.
- Trigger the
Confirmaction only when permitted. - Save/activate the draft and see the active result.
- Detect stale changes through ETag handling rather than silently overwriting them.
Test with a realistic non-developer business role. A preview that works only for a broadly authorized developer does not prove the application is deployable.
━━━━━━━━━━━━━━━━━━━━
▶ 13. TEST THE BO WITH EML
Entity Manipulation Language tests the business object below the HTTP/UI layer. Use READ ENTITIES to verify state and MODIFY ENTITIES to create, update, delete, or execute actions through the RAP transactional contract.
In a standalone report or an outer application transaction, COMMIT ENTITIES belongs at the outer transaction boundary after all related modifications succeed. In a behavior handler, do not issue it; the RAP runtime controls the transaction.
Always inspect FAILED and REPORTED. A technically successful ABAP statement can still contain rejected instances or business messages.
━━━━━━━━━━━━━━━━━━━━
▶ 14. AUTHORIZATION AND DCL: DIFFERENT RESPONSIBILITIES
CDS Data Control Language primarily restricts read access to CDS data. It answers which rows a user may see when the access-control path is applied.
RAP behavior authorization controls transactional operations such as update, delete, action execution, or child creation. A user being able to read a travel through DCL does not imply permission to modify it. Conversely, behavior authorization must not assume a hidden row is safe merely because the UI did not display it.
Design both layers:
| Concern | Primary RAP mechanism |
|---|---|
| Which rows may be read | CDS DCL/access control |
| May this user create at all? | Global behavior authorization |
| May this user change this travel? | Instance behavior authorization |
| Is an action enabled for current state? | Feature control plus authorization |
| Who may change persisted data outside this BO? | Prevent bypass; authorize every supported entry point |
━━━━━━━━━━━━━━━━━━━━
▶ 15. DEBUGGING FLOW
Debug from the outside inward:
- Binding and metadata: Is the active binding pointing to the expected service? Does metadata show the entity/action?
- Projection contract: Is the field, association, or action exposed by both projection CDS and projection behavior?
- Handler breakpoint: Set an external breakpoint in the generated behavior implementation method for the service user.
- Determination/validation: Check trigger conditions, keys, local-mode reads,
FAILED, andREPORTED. - Authorization: Inspect global/instance authorization results and the user's assigned business roles.
- Save sequence: Confirm validations pass and no code issues
COMMIT WORKprematurely. For advanced save problems, debug saver methods supported by the release. - Concurrency: Verify ETag fields change correctly and the client sends the expected conditional request.
- OData diagnostics: Use the service-binding test tools and platform-appropriate gateway/OData error logs. Exact tools differ between ABAP environment, embedded S/4HANA, and releases.
A missing action in metadata is not fixed in the action handler. A handler breakpoint that never triggers usually points to exposure, request, authorization, or binding issues first.
━━━━━━━━━━━━━━━━━━━━
▶ 16. DEV TO QAS TO PRD LIFECYCLE
Capture repository artifacts in the correct package and transport: tables, draft tables, CDS entities, behavior definition and implementation, projection artifacts, metadata extension, service definition, binding, DCL, message class, and authorization artifacts as applicable.
Activate dependent artifacts in a coherent order in DEV and run automated EML and service tests. Import through QAS, assign realistic roles, validate metadata and one complete draft transaction, then promote through normal change control.
Do not manually recreate differently named bindings in QAS and PRD unless the platform explicitly requires environment-specific setup. Repository binding artifacts should be transported consistently; runtime publication or activation steps may still be environment-specific. Record those steps in deployment instructions.
Never assume a published DEV binding proves production readiness. Check released API availability, software component versions, authorization roles, communication setup, and service activation in each target.
━━━━━━━━━━━━━━━━━━━━
▶ 17. MANAGED VERSUS UNMANAGED RAP
| Decision | Managed RAP | Unmanaged RAP |
|---|---|---|
| Standard CRUD persistence | Framework handles it | Developer implements it |
| Best fit | New custom tables and conventional BOs | Existing legacy save APIs or nonstandard persistence |
| Draft | Strong framework support when modeled correctly | Possible but requires more implementation discipline |
| Transaction control | Framework coordinates save | Developer maps BO operations to legacy transaction logic |
| Development effort | Lower for standard transactional apps | Higher; more hooks and tests |
| Clean-core path | Excellent with released APIs | Valid only when underlying APIs are released/allowed |
| Choose for this bootcamp | Yes | No; mention as contrast |
Do not choose unmanaged merely because custom logic exists. Managed RAP supports determinations, validations, actions, authorization, and save extensions. Choose unmanaged when the persistence lifecycle genuinely must be delegated to an existing API or cannot fit managed persistence.
━━━━━━━━━━━━━━━━━━━━
▶ 18. COMMON TROUBLESHOOTING CHECKLIST
- Tables and CDS entities activate before behavior artifacts.
- Root and child UUID fields use compatible types.
- Composition and parent association cardinalities agree.
- Projection redirects composition/parent associations correctly.
- Draft tables and administrative fields match release requirements.
- ETag fields are updated by the framework or implementation as intended.
- Child create is allowed in both interface and projection behavior.
- The action is exposed in projection behavior and metadata.
- Validation writes both
FAILEDandREPORTEDcorrectly. - No handler executes
COMMIT WORKorCOMMIT ENTITIES. - DCL and behavior authorization are tested separately.
- The service definition exposes projections, not persistence tables.
- The active OData V4 binding references the expected service definition.
- Metadata is refreshed after activation; stale browser metadata is ruled out.
- QAS roles and service activation match the deployment plan.
━━━━━━━━━━━━━━━━━━━━
▶ 19. RAP ARTIFACT CREATION ORDER CHEAT SHEET
- Package, transport, language version, released-API check.
- Active root and child persistence tables.
- Draft tables where required by the release/tooling.
- Root and child interface CDS view entities.
- Managed interface behavior definition.
- Generated behavior pool and custom implementation.
- Root and child projection CDS view entities.
- Projection behavior definition.
- Metadata extension and UI annotations.
- Service definition.
- OData V4 UI service binding.
- DCL and business-role authorization completion.
- EML tests, metadata checks, preview, negative tests.
- Transport, QAS validation, production activation/publication steps.
━━━━━━━━━━━━━━━━━━━━
▶ 20. MINI-PROJECT CHALLENGE
Extend the Travel BO with a BookingFee, CurrencyCode, and derived TotalPrice. Add a determination that recalculates the total after child changes, a validation that rejects bookings outside the travel dates, and a Cancel action that is available only for Open travels. Add instance authorization so only the responsible agency may update a travel, while auditors retain read access through DCL. Write EML tests for successful creation, invalid dates, unauthorized confirmation, draft activation, and stale ETag handling.
━━━━━━━━━━━━━━━━━━━━
▶ INTERVIEW-READY EXPLANATION
“RAP starts with persistence and CDS interface entities that model a root business object and its compositions. The behavior definition declares the transactional contract—CRUD, draft, locking, ETags, authorization, determinations, validations, and actions—while the behavior pool implements only the custom rules. Projection CDS and projection behavior create a consumer-specific contract. Metadata extensions describe the Fiori Elements layout. A service definition exposes the projection graph, and an OData V4 binding supplies the runtime protocol; neither contains business logic. I test the BO directly with EML, inspect FAILED and REPORTED, leave commit control at the outer transaction boundary, enforce read access with DCL and modify access with RAP authorization, then transport and validate the complete artifact set through QAS to PRD.”
Code example
## Representative artifact set > **Release note:** These compact examples show the intended relationships. Generated draft clauses, handler signatures, annotation availability, and exact BDL syntax can vary by ABAP release. Use ADT quick fixes/templates for your installed system. ### 1. Active persistence tables ```abap@EndUserText.label : 'RAP Travel'@AbapCatalog.enhancement.category : #NOT_EXTENSIBLEdefine table zrap_travel { key client : abap.clnt not null; key travel_uuid : sysuuid_x16 not null; travel_id : abap.char(10); agency_id : abap.char(10); customer_id : abap.char(10); begin_date : abap.dats; end_date : abap.dats; status : abap.char(1); total_price : abap.curr(15,2); currency_code : abap.cuky; created_by : abp_creation_user; 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;} @EndUserText.label : 'RAP Booking'@AbapCatalog.enhancement.category : #NOT_EXTENSIBLEdefine table zrap_booking { key client : abap.clnt not null; key booking_uuid : sysuuid_x16 not null; travel_uuid : sysuuid_x16 not null; booking_id : abap.char(10); booking_date : abap.dats; carrier_id : abap.char(3); connection_id : abap.char(4); flight_price : abap.curr(15,2); currency_code : abap.cuky; local_last_changed_at : abp_locinst_lastchange_tstmpl;}``` Create compatible draft tables such as `zrap_trav_d` and `zrap_book_d` with the draft administrative include/fields required by the installed release. Prefer ADT generation where available. ### 2. Interface CDS entities ```abap@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Travel Interface'define root view entity ZI_RAP_Travel as select from zrap_travel composition [0..*] of ZI_RAP_Booking as _Booking{ key travel_uuid as TravelUUID, travel_id as TravelID, agency_id as AgencyID, customer_id as CustomerID, begin_date as BeginDate, end_date as EndDate, status as Status, @Semantics.amount.currencyCode: 'CurrencyCode' total_price as TotalPrice, @Semantics.currencyCode: true currency_code as CurrencyCode, created_by as CreatedBy, created_at as CreatedAt, local_last_changed_by as LocalLastChangedBy, local_last_changed_at as LocalLastChangedAt, last_changed_at as LastChangedAt, _Booking} @AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Booking Interface'define view entity ZI_RAP_Booking as select from zrap_booking association to parent ZI_RAP_Travel as _Travel on $projection.TravelUUID = _Travel.TravelUUID{ key booking_uuid as BookingUUID, travel_uuid as TravelUUID, booking_id as BookingID, booking_date as BookingDate, carrier_id as CarrierID, connection_id as ConnectionID, @Semantics.amount.currencyCode: 'CurrencyCode' flight_price as FlightPrice, @Semantics.currencyCode: true currency_code as CurrencyCode, local_last_changed_at as LocalLastChangedAt, _Travel}``` ### 3. Managed behavior definition ```abapmanaged implementation in class zbp_i_rap_travel unique;strict ( 2 );with draft; define behavior for ZI_RAP_Travel alias Travelpersistent table zrap_traveldraft table zrap_trav_dlock masterauthorization master ( instance )etag master LocalLastChangedAt{ create; update; delete; field ( readonly, numbering : managed ) TravelUUID; field ( readonly ) CreatedBy, CreatedAt, LocalLastChangedBy, LocalLastChangedAt, LastChangedAt; field ( mandatory ) AgencyID, BeginDate, EndDate, CurrencyCode; association _Booking { create; with draft; } determination setInitialStatus on modify { create; } validation validateDates on save { create; update; field BeginDate, EndDate; } action confirm result [1] $self; draft action Edit; draft action Activate; draft action Discard; draft determine action Prepare { validation validateDates; } mapping for zrap_travel { TravelUUID = travel_uuid; TravelID = travel_id; AgencyID = agency_id; CustomerID = customer_id; BeginDate = begin_date; EndDate = end_date; Status = status; TotalPrice = total_price; CurrencyCode = currency_code; CreatedBy = created_by; CreatedAt = created_at; LocalLastChangedBy = local_last_changed_by; LocalLastChangedAt = local_last_changed_at; LastChangedAt = last_changed_at; }} define behavior for ZI_RAP_Booking alias Bookingpersistent table zrap_bookingdraft table zrap_book_dlock dependent by _Travelauthorization dependent by _Traveletag master LocalLastChangedAt{ update; delete; field ( readonly, numbering : managed ) BookingUUID; field ( readonly ) TravelUUID, LocalLastChangedAt; association _Travel { with draft; } mapping for zrap_booking { BookingUUID = booking_uuid; TravelUUID = travel_uuid; BookingID = booking_id; BookingDate = booking_date; CarrierID = carrier_id; ConnectionID = connection_id; FlightPrice = flight_price; CurrencyCode = currency_code; LocalLastChangedAt = local_last_changed_at; }}``` Some releases use additional total-ETag or draft clauses. Add them according to the ADT-generated draft template rather than guessing. ### 4. Behavior implementation excerpts ```abapMETHOD setInitialStatus. MODIFY ENTITIES OF zi_rap_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR key IN keys ( %tky = key-%tky Status = 'O' ) ) REPORTED DATA(update_reported). reported = CORRESPONDING #( DEEP update_reported ).ENDMETHOD. METHOD validateDates. READ ENTITIES OF zi_rap_travel IN LOCAL MODE ENTITY Travel FIELDS ( BeginDate EndDate ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). LOOP AT travels INTO DATA(travel). IF travel-EndDate < travel-BeginDate. APPEND VALUE #( %tky = travel-%tky ) TO failed-travel. APPEND VALUE #( %tky = travel-%tky %element-EndDate = if_abap_behv=>mk-on %msg = new_message_with_text( severity = if_abap_behv_message=>severity-error text = 'End date must not be before begin date' ) ) TO reported-travel. ENDIF. ENDLOOP.ENDMETHOD. METHOD confirm. READ ENTITIES OF zi_rap_travel IN LOCAL MODE ENTITY Travel FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(travels). MODIFY ENTITIES OF zi_rap_travel IN LOCAL MODE ENTITY Travel UPDATE FIELDS ( Status ) WITH VALUE #( FOR travel IN travels ( %tky = travel-%tky Status = 'C' ) ) FAILED failed REPORTED reported. READ ENTITIES OF zi_rap_travel IN LOCAL MODE ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys ) RESULT DATA(confirmed). result = VALUE #( FOR row IN confirmed ( %tky = row-%tky %param = row ) ).ENDMETHOD.``` Production action logic should reject already-confirmed or unauthorized instances and report a specific message. No handler calls `COMMIT WORK`. ### 5. Projection entities and behavior ```abap@AccessControl.authorizationCheck: #CHECK@Metadata.allowExtensions: true@EndUserText.label: 'Travel UI Projection'define root view entity ZC_RAP_Travel provider contract transactional_query as projection on ZI_RAP_Travel{ key TravelUUID, TravelID, AgencyID, CustomerID, BeginDate, EndDate, Status, TotalPrice, CurrencyCode, LocalLastChangedAt, redirect composition child to ZC_RAP_Booking as _Booking} @AccessControl.authorizationCheck: #CHECK@Metadata.allowExtensions: truedefine view entity ZC_RAP_Booking as projection on ZI_RAP_Booking{ key BookingUUID, TravelUUID, BookingID, BookingDate, CarrierID, ConnectionID, FlightPrice, CurrencyCode, redirect association to parent ZC_RAP_Travel as _Travel} projection;strict ( 2 );use draft; define behavior for ZC_RAP_Travel alias Travel { use create; use update; use delete; use action confirm; use action Edit; use action Activate; use action Discard; use action Prepare; use association _Booking { create; with draft; }} define behavior for ZC_RAP_Booking alias Booking { use update; use delete; use association _Travel { with draft; }}``` ### 6. Metadata extension ```abap@Metadata.layer: #COREannotate entity ZC_RAP_Travel with{ @UI.facet: [ { id: 'General', type: #IDENTIFICATION_REFERENCE, label: 'Travel', position: 10 }, { id: 'Bookings', type: #LINEITEM_REFERENCE, label: 'Bookings', position: 20, targetElement: '_Booking' } ] @UI.selectionField: [{ position: 10 }] @UI.lineItem: [{ position: 10 }] @UI.identification: [{ position: 10 }] TravelID; @UI.lineItem: [{ position: 20 }] @UI.identification: [{ position: 20 }] AgencyID; @UI.lineItem: [{ position: 30 }] @UI.identification: [{ position: 30 }] BeginDate; @UI.lineItem: [{ position: 40 }] @UI.identification: [{ position: 40 }] EndDate; @UI.lineItem: [{ position: 50 }] Status; @UI.lineItem: [{ type: #FOR_ACTION, dataAction: 'confirm', label: 'Confirm', position: 60 }] TravelUUID;}``` ### 7. Service definition ```abap@EndUserText.label: 'Travel UI Service'define service ZUI_RAP_TRAVEL { expose ZC_RAP_Travel as Travels; expose ZC_RAP_Booking as Bookings;}``` Create and activate an OData V4 UI binding for `ZUI_RAP_TRAVEL`, publish/make it available according to the target release, then inspect `$metadata` and preview it. ### 8. EML integration test sketch ```abapDATA travel_cid TYPE abp_behv_cid VALUE 'TRAVEL_1'. MODIFY ENTITIES OF zi_rap_travel ENTITY Travel CREATE FIELDS ( AgencyID CustomerID BeginDate EndDate CurrencyCode ) WITH VALUE #( ( %cid = travel_cid AgencyID = '070001' CustomerID = '000001' BeginDate = cl_abap_context_info=>get_system_date( ) EndDate = cl_abap_context_info=>get_system_date( ) + 7 CurrencyCode = 'EUR' ) ) CREATE BY _Booking FIELDS ( BookingDate CarrierID ConnectionID FlightPrice CurrencyCode ) WITH VALUE #( ( %cid_ref = travel_cid %target = VALUE #( ( %cid = 'BOOKING_1' BookingDate = cl_abap_context_info=>get_system_date( ) CarrierID = 'LH' ConnectionID = '0400' FlightPrice = '350.00' CurrencyCode = 'EUR' ) ) ) ) MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported). IF failed IS INITIAL. COMMIT ENTITIES RESPONSE OF zi_rap_travel FAILED DATA(commit_failed) REPORTED DATA(commit_reported).ENDIF. READ ENTITIES OF zi_rap_travel ENTITY Travel ALL FIELDS WITH VALUE #( ( %key-TravelUUID = mapped-travel[ 1 ]-TravelUUID ) ) RESULT DATA(saved_travel) FAILED DATA(read_failed) REPORTED DATA(read_reported).``` `COMMIT ENTITIES` is correct here because this standalone integration program owns the outer transaction. It would be incorrect inside the behavior handler methods shown earlier. Real project scenario
Project scenario: travel approval application
A travel-services team replaces spreadsheets with a transactional application. An employee creates a travel request, adds one or more flight bookings, corrects validation errors, saves the draft, and activates it. An authorized coordinator confirms the request. Auditors may read all requests but cannot modify them.
The implementation team creates custom root and child tables because the application owns this new persistence. UUIDs are technical keys; TravelID remains the readable business identifier. A managed RAP BO supplies standard create, update, delete, locking, ETag, and draft behavior. Determinations initialize status and recalculate totals. Validations reject invalid date ranges and bookings outside the travel dates. The Confirm action enforces status and authorization rules.
The UI projection exposes only employee-facing fields and actions. A separate reporting projection can later expose read-only analytical fields without changing the transactional interface BO. Metadata extension drives the List Report/Object Page, including a booking table facet. The service definition exposes the projections, while the OData V4 binding provides the UI protocol.
Before release, the team tests EML below the service layer, then tests metadata, draft editing, child creation, validation messages, confirmation, ETag conflicts, DCL reads, and RAP modify authorization through the service. They transport the complete artifact set to QAS, assign realistic roles, repeat one end-to-end transaction, and record any environment-specific service activation step before PRD deployment.
Common mistakes
- Starting with UI annotations before defining the root-child business-object grain.
- Exposing active persistence tables directly instead of stable interface and projection entities.
- Assuming composition alone creates database integrity or correct child ownership.
- Using inconsistent UUID types between root, child, CDS, draft tables, and EML payloads.
- Forgetting draft-table or total-ETag requirements for the installed release.
- Treating a business document number as the technical UUID key.
- Allowing child create in interface behavior but omitting it from projection behavior.
- Implementing business rules in a service definition or service binding; these artifacts expose a contract and protocol only.
- Putting consumer-specific Fiori layout annotations into reusable interface entities without a reason.
- Returning a message in
REPORTEDbut not marking the invalid instance inFAILED. - Calling
COMMIT WORKorCOMMIT ENTITIESinside a behavior handler. - Assuming DCL read filtering replaces RAP authorization for update, delete, or actions.
- Checking action visibility but not enforcing authorization in the transaction.
- Using unreleased SAP tables or APIs from an ABAP Cloud package because they are technically visible.
- Selecting unmanaged RAP merely because the BO has custom validations and actions.
- Following obsolete SEGW steps instead of the RAP service-definition and binding flow.
- Testing only with a developer user whose broad authorization hides production defects.
- Recreating service bindings manually in every system and introducing metadata drift.
- Debugging handler code before confirming the operation exists in active OData metadata.
- Ignoring
FAILED,REPORTED, ETag conflicts, and negative tests in EML automation.
Best practices
- Confirm the target ABAP release, language version, and released APIs in ADT before copying syntax.
- Model root, child, ownership, cardinality, keys, currency/unit semantics, and transaction boundaries before building the UI.
- Prefer UUID technical keys for new managed BOs; keep business identifiers separate.
- Use managed RAP for conventional custom persistence and choose unmanaged only for a real legacy/API-driven save requirement.
- Keep interface entities reusable and projection entities consumer-specific.
- Put transactional rules in behavior and handler logic—not service artifacts.
- Use draft, locks, and ETags deliberately; test stale-update and concurrent-edit behavior.
- Implement validations with both
FAILEDand preciseREPORTEDmessages. - Keep determinations idempotent and avoid hidden side effects.
- Use EML for BO-internal and integration tests; inspect mapped, failed, and reported responses.
- Leave commit control to RAP handlers and place
COMMIT ENTITIESonly at an outer transaction boundary. - Enforce read access with DCL and modify/action access with RAP authorization; test both independently.
- Keep Fiori Elements annotations in metadata extensions where practical.
- Validate active OData metadata before debugging UI rendering or handler execution.
- Consume released SAP APIs and document any release-dependent feature or deployment step.
- Transport the complete artifact set consistently and test with realistic QAS business roles.
Interview angle
A strong interview answer explains RAP as a layered transactional model, not an OData generator. Walk in execution order: persistence; root/child interface CDS with composition; behavior contract; handler implementation; projection CDS and projection behavior; metadata extension; service definition; OData V4 binding; Fiori Elements or API consumer; EML, authorization, debugging, and transport.
Explain why each layer exists. Managed RAP owns standard persistence and save coordination; custom handlers implement determinations, validations, actions, messages, and authorization. Projection artifacts restrict a stable BO for a consumer. Metadata describes presentation. Service definition and binding expose the projection but contain no business logic.
Expect follow-ups on draft, ETags, locks, UUID numbering, FAILED versus REPORTED, local-mode EML, and commit control. State clearly that handlers never issue COMMIT WORK; COMMIT ENTITIES belongs to the outer transaction owner. Distinguish DCL read restrictions from RAP modify authorization. Mention that unmanaged RAP is appropriate when existing persistence/save APIs genuinely require custom orchestration, not simply because rules are complex.
A concise closing answer is: “I model and secure the BO first, let managed RAP handle conventional persistence, expose a deliberately restricted projection through OData V4, test the transactional contract with EML and service-level negative cases, and transport the complete artifact chain through QAS before production activation.”
Practice this lesson
Quick check
1. In a managed RAP app, which part do developers typically NOT implement?
2. What is the correct order of the RAP stack from data to consumer?
3. A new app stores data in new custom tables with no legacy logic. Which implementation type is recommended?
Interview questions
1. What key design decisions do you make when starting a new RAP business object?
Answer it in your own words first, then compare.
2. Explain the RAP architecture from database table to Fiori app.
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 2: Data Model Design — Tables, Keys, Administrative Fields and Interface Views
- 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