Appearance
Lesson 18 — RAP Compositions: Parent/Child Business Objects
Until now, the Reservation Business Object has had one node:
text
ReservationToday it becomes a composite Business Object:
text
Reservation
└── ParticipantsA Participant belongs to exactly one Reservation and cannot exist independently of it.
The architecture becomes:
text
ZR_RESERVATION_###
│
│ composition [0..*]
↓
ZR_RESERVATION_PART_###
│
└── association to parentAt runtime:
text
Reservation
├─ root behavior
├─ authorization master
├─ lock master
├─ total ETag
├─ draft table
└─ Participants
├─ child behavior
├─ authorization dependent
├─ lock dependent
├─ own ETag
└─ own draft tableLesson information
| Teacher-led time | About 3 hours |
| Self-practice time | About 3 hours |
| Environment | SAP BTP ABAP Trial |
| Prerequisite | Lesson 17 |
| Root entity | ZR_RESERVATION_### |
| Child entity | ZR_RESERVATION_PART_### |
| Child active table | ZRESPART_### |
| Child draft table | ZDRESPART_### |
| Child projection | ZC_RESERVATION_PART_### |
| EML test class | ZCL_RESV_COMP_### |
| Main outcome | Build and expose a draft-enabled two-node RAP BO |
Replace:
text
###with your own student/group suffix.
Learning objectives
After this lesson, you should be able to:
- explain a composite RAP BO;
- distinguish composition from a normal association;
- use
composition [0..*] of; - use
association to parent; - model child persistence;
- add a second entity to one BDEF;
- create child instances through a parent association;
- explain why managed children do not use normal root-style
create;; - use
lock dependent by; - use
authorization dependent by; - give the child its own ETag;
- generate child draft persistence;
- use
with drafton BO-internal composition associations; - create children with EML
CREATE BY; - read children with EML read-by-association;
- explain
%target,%cid,%cid_ref, andLINK; - perform a deep create;
- redirect composition associations in projection views;
- expose child behavior in the projection BDEF;
- expose the child projection in the Service Definition;
- display child data in a Fiori Elements facet;
- explain how root total ETag covers the complete composition tree.
1. Composite BO Structure
A RAP BO may contain:
text
one root
+
one or more child entitiesExample:
text
Sales Order
└── ItemsOur BO becomes:
text
Reservation
└── ParticipantsOne BO instance contains:
text
exactly one Reservation
zero or more Participants2. Composition vs Normal Association
A normal association connects entities.
A composition expresses:
text
whole
+
dependent partFor this lesson:
text
Reservation = whole
Participant = partThe Participant belongs to the Reservation lifecycle.
3. Parent and Child Relationship
The parent defines:
text
composition to childThe child defines:
text
association to parentTogether:
text
Reservation
│
│ _Participants
↓
Participant
│
└── _Reservation ──→ Reservation4. Cardinality
A Reservation may contain:
text
0..*Participants.
Therefore:
abap
composition [0..*]The child belongs to exactly one parent.
5. Objects Added Today
Create:
text
ZRESPART_###
ZR_RESERVATION_PART_###
ZDRESPART_###
ZC_RESERVATION_PART_###
Access Control for ZR_RESERVATION_PART_###
Access Control for ZC_RESERVATION_PART_###
Metadata Extension for ZC_RESERVATION_PART_###
ZCL_RESV_COMP_###Modify:
text
ZR_RESERVATION_###
base BDEF
ZC_RESERVATION_###
projection BDEF
root Metadata Extension
ZUI_RESERVATION_###Reuse:
text
ZUI_RESV_###_O46. Create Participant Persistence
Create:
text
ZRESPART_###Use the same application-table conventions as the existing Reservation table.
Required fields:
text
client
participant_uuid
reservation_uuid
participant_no
participant_name
participant_email
participant_role
local_last_changed_atRecommended types:
abap
key client : abap.clnt not null;
key participant_uuid : sysuuid_x16 not null;
reservation_uuid : sysuuid_x16 not null;
participant_no : abap.numc(4);
participant_name : abap.char(60);
participant_email : abap.char(100);
participant_role : abap.char(40);
local_last_changed_at : abp_locinst_lastchange_tstmpl;7. Child Identity
Use:
text
ParticipantUuidas the child technical key.
Use:
text
ReservationUuidas the parent reference.
Do not use the parent UUID as the child key because one Reservation can have many Participants.
8. Create the Child CDS Entity
Create:
text
ZR_RESERVATION_PART_###Use:
abap
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Reservation Participant'
define view entity ZR_Reservation_Part_###
as select from zrespart_### as Participant
association to parent ZR_Reservation_### as _Reservation
on $projection.ReservationUuid = _Reservation.Uuid
{
_Reservation,
key Participant.participant_uuid as ParticipantUuid,
Participant.reservation_uuid as ReservationUuid,
Participant.participant_no as ParticipantNo,
Participant.participant_name as ParticipantName,
Participant.participant_email as ParticipantEmail,
Participant.participant_role as ParticipantRole,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
Participant.local_last_changed_at as LocalLastChangedAt
}9. association to parent
Use:
abap
association to parentfor the child's direct parent relationship.
The ON condition must identify the parent through its complete key.
Our root key is:
text
Uuidso:
abap
$projection.ReservationUuid = _Reservation.Uuidis sufficient.
Expose:
text
_Reservationexactly once in the child select list.
10. Add the Root Composition
Open:
text
ZR_RESERVATION_###Add before the select list:
abap
composition [0..*] of ZR_Reservation_Part_### as _ParticipantsExpose:
text
_Participantsinside the root select list.
Conceptually:
abap
define root view entity ZR_Reservation_###
as select from zresv_### as Reservation
composition [0..*] of ZR_Reservation_Part_### as _Participants
{
key Reservation.uuid as Uuid,
...
_Participants
}The root remains:
text
ZR_RESERVATION_###The child is not declared with root.
11. Protect Child Reads
The child will later be exposed as its own OData entity set.
Create Access Control for:
text
ZR_RESERVATION_PART_###Use the parent ownership rule:
abap
@EndUserText.label: 'Reservation Participant Access'
@MappingRole: true
define role ZR_Reservation_Part_###
{
grant select on ZR_Reservation_Part_###
where ( _Reservation.ResponsibleUser ) = aspect user;
}This keeps:
text
Participant read accessaligned with:
text
Reservation ownership12. Add Composition Behavior to the Root
Open the existing base BDEF.
Inside the Reservation root behavior add:
abap
association _Participants { create; with draft; }This means:
text
read related Participants
create Participants through Reservation
keep active/draft state consistent13. Why the Child Has No Standard create;
In managed RAP:
text
root
→ standard create
child
→ create by associationTherefore the child behavior declares:
text
update;
delete;but not normal:
abap
create;Child creation is enabled by:
abap
association _Participants { create; with draft; }on the parent.
14. Add Child Behavior
After the root behavior block, add:
abap
define behavior for ZR_Reservation_Part_### alias Participant
persistent table zrespart_###
draft table zdrespart_###
authorization dependent by _Reservation
lock dependent by _Reservation
etag master LocalLastChangedAt
{
update;
delete;
field ( numbering : managed, readonly )
ParticipantUuid;
field ( readonly )
ReservationUuid,
LocalLastChangedAt;
field ( mandatory:create, readonly:update )
ParticipantNo;
association _Reservation { with draft; }
mapping for zrespart_###
{
ParticipantUuid = participant_uuid;
ReservationUuid = reservation_uuid;
ParticipantNo = participant_no;
ParticipantName = participant_name;
ParticipantEmail = participant_email;
ParticipantRole = participant_role;
LocalLastChangedAt = local_last_changed_at;
}
}15. Lock Dependency
Use:
abap
lock dependent by _ReservationOnly the root is the lock master.
A modification to Participant delegates locking to:
text
its ReservationThis keeps one composition tree inside one lock scope.
16. Authorization Dependency
Use:
abap
authorization dependent by _ReservationOnly the root is the authorization master.
For standard child:
text
create by association
update
deleteauthorization is evaluated through the parent Reservation.
The owner policy remains:
text
ResponsibleUser = current user17. Child ETag
Use:
abap
etag master LocalLastChangedAtfor Participant.
The child can have its own optimistic-concurrency token while still being lock- and authorization-dependent on the root.
18. Root Total ETag and the Composition Tree
The root already uses:
text
LastChangedAtas:
text
total ETagThe total ETag represents the complete draft BO.
When active data in any BO node changes:
text
Reservation change
or
Participant changethe managed runtime updates the root total ETag when the field is correctly annotated and mapped.
19. Generate Child Draft Persistence
The child BDEF declares:
abap
draft table zdrespart_###Place the cursor on the table name.
Use:
text
Ctrl+1Generate the draft table.
Activate:
text
ZDRESPART_###Do not manually edit its technical draft administration fields.
20. Draft-Enabled BO Associations
Use:
abap
association _Participants { create; with draft; }on the root.
Use:
abap
association _Reservation { with draft; }on the child.
The intended navigation is:
text
active parent → active child
draft parent → draft child21. Activate the Base BO
A safe sequence:
text
ZRESPART_###
ZR_RESERVATION_PART_###
child Access Control
modified ZR_RESERVATION_###
ZDRESPART_###
modified base BDEF
related behavior artifactsUse:
text
Ctrl+F2
Ctrl+F3
Ctrl+Shift+F3as needed.
EML: Create by Association
22. Create the Test Consumer
Create:
text
ZCL_RESV_COMP_###Implement:
text
IF_OO_ADT_CLASSRUNThis is an external consumer.
Do not use:
text
IN LOCAL MODE23. Find an Owned Reservation
Use a protected read against:
text
ZR_RESERVATION_###Find an owned Reservation.
Prefer:
text
ReservationNo = 99100001if it still exists from the draft exercise.
Retrieve its:
text
Uuid24. Create a Participant by Association
Use:
abap
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
CREATE BY \_Participants
FIELDS (
ParticipantNo
ParticipantName
ParticipantEmail
ParticipantRole
)
WITH VALUE #(
(
Uuid = reservation_uuid
%target = VALUE #(
(
%cid = 'PART-001'
ParticipantNo = '0001'
ParticipantName = 'Taylor Reed'
ParticipantEmail = 'taylor@example.com'
ParticipantRole = 'Attendee'
)
)
)
)
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).25. %target
The outer row identifies:
text
the parent%target contains:
text
the children to create for that parentExample:
text
Reservation A
└── %target
├── Participant 1
└── Participant 226. Child %cid
ParticipantUuid uses managed numbering.
The consumer therefore supplies:
text
%cidnot the final UUID.
Inspect:
text
MAPPED-Participantto retrieve the generated child key.
27. Commit the Child
After checking interaction responses:
abap
COMMIT ENTITIES
RESPONSE OF ZR_Reservation_###
FAILED DATA(failed_commit)
REPORTED DATA(reported_commit).Verify the child exists in:
text
ZRESPART_###EML: Read by Association
28. Read the Children
Use:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
BY \_Participants
ALL FIELDS
WITH VALUE #(
( Uuid = reservation_uuid )
)
RESULT DATA(participants)
LINK DATA(participant_links)
FAILED DATA(failed_read).29. RESULT and LINK
RESULT contains:
text
Participant dataLINK contains:
text
source parent key
→
target child keyLINK becomes useful when one request reads children for several parents.
EML: Deep Create
30. Parent and Children in One Request
A composition supports a deep create.
Create:
text
one new Reservation
+
two Participantsinside one:
text
MODIFY ENTITIESThe parent UUID is not yet known.
Use:
text
parent %cid
child %cid_ref31. Deep Create Pattern
Use a pattern equivalent to:
abap
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
CREATE FIELDS (
ReservationNo
CustomerName
CustomerEmail
WorkshopCode
ReservationDate
Fee
CurrencyCode
Notes
)
WITH VALUE #(
(
%cid = 'ROOT-001'
ReservationNo = '99180001'
CustomerName = 'Composition Student'
CustomerEmail = 'composition@example.com'
WorkshopCode = 'RAP501'
ReservationDate = cl_abap_context_info=>get_system_date( )
Fee = '450.00'
CurrencyCode = 'EUR'
Notes = 'Deep create'
)
)
CREATE BY \_Participants
FIELDS (
ParticipantNo
ParticipantName
ParticipantEmail
ParticipantRole
)
WITH VALUE #(
(
%cid_ref = 'ROOT-001'
%target = VALUE #(
(
%cid = 'CHILD-001'
ParticipantNo = '0001'
ParticipantName = 'Morgan Lee'
ParticipantEmail = 'morgan@example.com'
ParticipantRole = 'Lead'
)
(
%cid = 'CHILD-002'
ParticipantNo = '0002'
ParticipantName = 'Sam Rivera'
ParticipantEmail = 'sam@example.com'
ParticipantRole = 'Attendee'
)
)
)
)
MAPPED DATA(mapped_deep)
FAILED DATA(failed_deep)
REPORTED DATA(reported_deep).32. %cid_ref
This:
text
%cid_ref = ROOT-001connects the child create request to the parent create row whose:
text
%cid = ROOT-001RAP resolves the final managed parent UUID.
33. Deep Create Proof
Inspect:
text
mapped_deep-Reservation
mapped_deep-ParticipantThen commit.
Verify:
text
one Reservation
two Participantswith the correct parent relationship.
Projection Layer
34. Create Child Projection
Create:
text
ZC_RESERVATION_PART_###Use:
abap
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Reservation Participant Projection'
@Metadata.allowExtensions: true
define view entity ZC_Reservation_Part_###
as projection on ZR_Reservation_Part_###
{
key ParticipantUuid,
ReservationUuid,
ParticipantNo,
ParticipantName,
ParticipantEmail,
ParticipantRole,
LocalLastChangedAt,
_Reservation : redirected to parent ZC_Reservation_###
}35. Redirect to Projected Parent
The base child points to:
text
ZR_RESERVATION_###The service projection must point to:
text
ZC_RESERVATION_###Use:
abap
redirected to parent36. Redirect the Root Composition
Open:
text
ZC_RESERVATION_###Expose:
abap
_Participants
: redirected to composition child ZC_Reservation_Part_###Now the service-specific hierarchy is:
text
ZC_RESERVATION_###
└── ZC_RESERVATION_PART_###37. Protect Child Projection Reads
Create Access Control for:
text
ZC_RESERVATION_PART_###Use:
abap
@EndUserText.label: 'Reservation Participant Projection Access'
@MappingRole: true
define role ZC_Reservation_Part_###
{
grant select on ZC_Reservation_Part_###
where inheriting conditions from entity ZR_Reservation_Part_###;
}Projection Behavior
38. Extend Root Projection Behavior
Inside the root projection behavior add:
abap
use association _Participants { create; with draft; }39. Add Child Projection Behavior
In the same projection BDEF add:
abap
define behavior for ZC_Reservation_Part_### alias Participant
use etag
{
use update;
use delete;
use association _Reservation { with draft; }
}There is no child:
abap
use create;Creation is through the root composition.
Service Exposure
40. Add the Child to the Service Definition
Open:
text
ZUI_RESERVATION_###Use a structure equivalent to:
abap
@ObjectModel.leadingEntity.name: 'ZC_RESERVATION_###'
define service ZUI_RESERVATION_###
{
expose ZC_Reservation_### as Reservations;
expose ZC_Reservation_Part_### as Participants;
expose I_CurrencyStdVH as Currencies;
}For a multi-entity BO projection, expose the child explicitly.
41. Reuse the Existing Binding
Keep:
text
ZUI_RESV_###_O4Activate the modified Service Definition.
Reload the existing binding:
text
F5if necessary.
Do not create a second service binding.
Fiori Elements Metadata
42. Add a Child Facet to the Root
Open the metadata extension for:
text
ZC_RESERVATION_###Add or extend:
abap
@UI.facet: [
{
id: 'Reservation',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'Reservation',
position: 10
},
{
id: 'Participants',
purpose: #STANDARD,
type: #LINEITEM_REFERENCE,
label: 'Participants',
position: 20,
targetElement: '_Participants'
}
]43. Create Child Metadata
Create a Metadata Extension for:
text
ZC_RESERVATION_PART_###Use:
abap
@Metadata.layer: #CORE
annotate entity ZC_Reservation_Part_### with
{
@UI.lineItem: [ { position: 10, label: 'Participant No.' } ]
ParticipantNo;
@UI.lineItem: [ { position: 20, label: 'Name' } ]
ParticipantName;
@UI.lineItem: [ { position: 30, label: 'Email' } ]
ParticipantEmail;
@UI.lineItem: [ { position: 40, label: 'Role' } ]
ParticipantRole;
}Draft UI Test
44. Open an Owned Reservation
Launch:
text
ZUI_RESV_###_O4Preview:
text
ReservationsOpen a Reservation owned by the current user.
The object page should now contain:
text
Reservation
Participantsfacets.
45. Create a Participant in Draft
Choose:
text
EditIn the Participants facet, create:
text
ParticipantNo = 0001
ParticipantName = Jordan Kim
ParticipantEmail = jordan@example.com
ParticipantRole = AttendeeDo not enter:
text
ParticipantUuid
ReservationUuid46. Inspect Child Draft Persistence
Before Save:
text
ZRESPART_###
→ no new active Participant
ZDRESPART_###
→ new Participant draftThe child participates in the same draft lifecycle as the parent.
47. Save the Composition
Choose:
text
SaveExpected:
text
root draft activates
child draft activates
active Participant row appears
child draft row disappearsOne Save activates the complete draft BO.
48. Discard a Child Change
Edit the Reservation again.
Change or delete a Participant.
Before Save, inspect active/draft persistence.
Choose:
text
Cancel / DiscardExpected:
text
active Participant unchanged
pending child draft change removed49. Verify Root Total ETag
Capture root:
text
LastChangedAtChange and save only Participant data.
Read the root timestamp again.
Expected:
text
LastChangedAt changedThe total ETag covers the complete BO composition tree.
Common Mistakes
50. Normal Association Instead of Composition
Wrong:
text
ordinary association from root to childCorrect:
abap
composition [0..*] of ...Composition models lifecycle dependency.
51. Missing To-Parent Association
The composition requires the reverse child relation.
Use:
abap
association to parent52. Child Declared as Root
A BO has one root.
Child:
text
define view entitynot:
text
define root view entity53. Standard Child create;
Managed child create is performed through:
text
CREATE BY associationDo not model the child as independently createable.
54. Missing with draft
The BO is draft-enabled.
Use:
text
with drafton both BO-internal association behaviors.
55. Child as Lock Master
Only the root is lock master.
Use:
abap
lock dependent by _Reservation56. Duplicated Child Authorization
Do not reimplement the same owner check for the child.
Use:
abap
authorization dependent by _Reservation57. Projection Associations Not Redirected
Projection targets must be projection entities.
Use:
text
redirected to composition child
redirected to parent58. Child Missing from Service Definition
Expose:
text
Participantsexplicitly.
Otherwise the composition hierarchy is incomplete in the OData service.
59. Child Missing Read Protection
Protect:
text
base child CDS
projection child CDSbecause the child can be read through its own service entity set.
Certification Alignment
60. Practical Composition Skills
A practical RAP task can require you to create or repair:
text
composition
to-parent association
child persistence
child behavior
create-by-association
lock dependency
authorization dependency
child ETag
draft table
projection redirection
service exposure
Fiori child facet
EML deep createThe main skill is tracing the complete parent/child contract through every RAP layer.
Knowledge Check
61. Questions
1. What makes a composition different from a normal association?
2. Why must a child define an association to parent?
3. Why is child create exposed through the parent association?
4. What does lock dependent by mean?
5. What does authorization dependent by mean?
6. Why can the child still be an ETag master?
7. What does the root total ETag cover?
8. What is %target?
9. What problem does %cid_ref solve?
10. What information does LINK return?
11. Why do projection associations need redirection?
12. Why must the child projection be exposed explicitly in the Service Definition?
13. Why does the child need DCL even though the parent has DCL?
14. Why is a line-item facet appropriate for [0..*] Participants?
Teacher-Led Completion Checklist
62. Checklist
End-of-Day Mental Model
63. Final Model
text
Reservation BO
│
┌──────────┴──────────┐
│ │
ROOT CHILD
Reservation Participant
│ │
lock master lock dependent
authorization master authorization dependent
total ETag own ETag
│ │
└──── composition ────┘Data model:
text
ZR_RESERVATION_###
│
↓ composition
ZR_RESERVATION_PART_###
│
└── association to parentEML:
text
Reservation
↓
CREATE BY _Participants
↓
ParticipantProjection:
text
ZC_RESERVATION_###
│ redirected to composition child
↓
ZC_RESERVATION_PART_###
│ redirected to parent
└────────────────────────→ root projectionYou now have a:
text
managed
authorized
draft-enabled
service-ready
multi-node RAP Business ObjectThe next checkpoint will ask students to prove that architecture with much less guidance.