Skip to content

Lesson 18 — RAP Compositions: Parent/Child Business Objects ​

Until now, the Reservation Business Object has had one node:

text
Reservation

Today it becomes a composite Business Object:

text
Reservation
└── Participants

A 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 parent

At runtime:

text
Reservation
├─ root behavior
├─ authorization master
├─ lock master
├─ total ETag
├─ draft table
└─ Participants
   ├─ child behavior
   ├─ authorization dependent
   ├─ lock dependent
   ├─ own ETag
   └─ own draft table

Lesson information ​

Teacher-led timeAbout 3 hours
Self-practice timeAbout 3 hours
EnvironmentSAP BTP ABAP Trial
PrerequisiteLesson 17
Root entityZR_RESERVATION_###
Child entityZR_RESERVATION_PART_###
Child active tableZRESPART_###
Child draft tableZDRESPART_###
Child projectionZC_RESERVATION_PART_###
EML test classZCL_RESV_COMP_###
Main outcomeBuild 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 draft on BO-internal composition associations;
  • create children with EML CREATE BY;
  • read children with EML read-by-association;
  • explain %target, %cid, %cid_ref, and LINK;
  • 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 entities

Example:

text
Sales Order
└── Items

Our BO becomes:

text
Reservation
└── Participants

One BO instance contains:

text
exactly one Reservation
zero or more Participants

2. Composition vs Normal Association ​

A normal association connects entities.

A composition expresses:

text
whole
+
dependent part

For this lesson:

text
Reservation = whole
Participant = part

The Participant belongs to the Reservation lifecycle.


3. Parent and Child Relationship ​

The parent defines:

text
composition to child

The child defines:

text
association to parent

Together:

text
Reservation
    │
    │ _Participants
    ↓
Participant
    │
    └── _Reservation ──→ Reservation

4. 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_###_O4

6. 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_at

Recommended 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
ParticipantUuid

as the child technical key.

Use:

text
ReservationUuid

as 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 parent

for the child's direct parent relationship.

The ON condition must identify the parent through its complete key.

Our root key is:

text
Uuid

so:

abap
$projection.ReservationUuid = _Reservation.Uuid

is sufficient.

Expose:

text
_Reservation

exactly 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 _Participants

Expose:

text
_Participants

inside 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 access

aligned with:

text
Reservation ownership

12. 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 consistent

13. Why the Child Has No Standard create; ​

In managed RAP:

text
root
→ standard create

child
→ create by association

Therefore 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 _Reservation

Only the root is the lock master.

A modification to Participant delegates locking to:

text
its Reservation

This keeps one composition tree inside one lock scope.


16. Authorization Dependency ​

Use:

abap
authorization dependent by _Reservation

Only the root is the authorization master.

For standard child:

text
create by association
update
delete

authorization is evaluated through the parent Reservation.

The owner policy remains:

text
ResponsibleUser = current user

17. Child ETag ​

Use:

abap
etag master LocalLastChangedAt

for 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
LastChangedAt

as:

text
total ETag

The total ETag represents the complete draft BO.

When active data in any BO node changes:

text
Reservation change
or
Participant change

the 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+1

Generate 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 child

21. Activate the Base BO ​

A safe sequence:

text
ZRESPART_###
ZR_RESERVATION_PART_###
child Access Control
modified ZR_RESERVATION_###
ZDRESPART_###
modified base BDEF
related behavior artifacts

Use:

text
Ctrl+F2
Ctrl+F3
Ctrl+Shift+F3

as needed.


EML: Create by Association ​

22. Create the Test Consumer ​

Create:

text
ZCL_RESV_COMP_###

Implement:

text
IF_OO_ADT_CLASSRUN

This is an external consumer.

Do not use:

text
IN LOCAL MODE

23. Find an Owned Reservation ​

Use a protected read against:

text
ZR_RESERVATION_###

Find an owned Reservation.

Prefer:

text
ReservationNo = 99100001

if it still exists from the draft exercise.

Retrieve its:

text
Uuid

24. 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 parent

Example:

text
Reservation A
└── %target
    ├── Participant 1
    └── Participant 2

26. Child %cid ​

ParticipantUuid uses managed numbering.

The consumer therefore supplies:

text
%cid

not the final UUID.

Inspect:

text
MAPPED-Participant

to 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).

RESULT contains:

text
Participant data

LINK contains:

text
source parent key
→
target child key

LINK 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 Participants

inside one:

text
MODIFY ENTITIES

The parent UUID is not yet known.

Use:

text
parent %cid
child %cid_ref

31. 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-001

connects the child create request to the parent create row whose:

text
%cid = ROOT-001

RAP resolves the final managed parent UUID.


33. Deep Create Proof ​

Inspect:

text
mapped_deep-Reservation
mapped_deep-Participant

Then commit.

Verify:

text
one Reservation
two Participants

with 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 parent

36. 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_###_O4

Activate the modified Service Definition.

Reload the existing binding:

text
F5

if 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_###_O4

Preview:

text
Reservations

Open a Reservation owned by the current user.

The object page should now contain:

text
Reservation
Participants

facets.


45. Create a Participant in Draft ​

Choose:

text
Edit

In the Participants facet, create:

text
ParticipantNo    = 0001
ParticipantName  = Jordan Kim
ParticipantEmail = jordan@example.com
ParticipantRole  = Attendee

Do not enter:

text
ParticipantUuid
ReservationUuid

46. Inspect Child Draft Persistence ​

Before Save:

text
ZRESPART_###
→ no new active Participant

ZDRESPART_###
→ new Participant draft

The child participates in the same draft lifecycle as the parent.


47. Save the Composition ​

Choose:

text
Save

Expected:

text
root draft activates
child draft activates
active Participant row appears
child draft row disappears

One 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 / Discard

Expected:

text
active Participant unchanged
pending child draft change removed

49. Verify Root Total ETag ​

Capture root:

text
LastChangedAt

Change and save only Participant data.

Read the root timestamp again.

Expected:

text
LastChangedAt changed

The total ETag covers the complete BO composition tree.


Common Mistakes ​

50. Normal Association Instead of Composition ​

Wrong:

text
ordinary association from root to child

Correct:

abap
composition [0..*] of ...

Composition models lifecycle dependency.


51. Missing To-Parent Association ​

The composition requires the reverse child relation.

Use:

abap
association to parent

52. Child Declared as Root ​

A BO has one root.

Child:

text
define view entity

not:

text
define root view entity

53. Standard Child create; ​

Managed child create is performed through:

text
CREATE BY association

Do not model the child as independently createable.


54. Missing with draft ​

The BO is draft-enabled.

Use:

text
with draft

on both BO-internal association behaviors.


55. Child as Lock Master ​

Only the root is lock master.

Use:

abap
lock dependent by _Reservation

56. Duplicated Child Authorization ​

Do not reimplement the same owner check for the child.

Use:

abap
authorization dependent by _Reservation

57. Projection Associations Not Redirected ​

Projection targets must be projection entities.

Use:

text
redirected to composition child
redirected to parent

58. Child Missing from Service Definition ​

Expose:

text
Participants

explicitly.

Otherwise the composition hierarchy is incomplete in the OData service.


59. Child Missing Read Protection ​

Protect:

text
base child CDS
projection child CDS

because 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 create

The 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? ​

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 parent

EML:

text
Reservation
    ↓
CREATE BY _Participants
    ↓
Participant

Projection:

text
ZC_RESERVATION_###
        │ redirected to composition child
        ↓
ZC_RESERVATION_PART_###
        │ redirected to parent
        └────────────────────────→ root projection

You now have a:

text
managed
authorized
draft-enabled
service-ready
multi-node RAP Business Object

The next checkpoint will ask students to prove that architecture with much less guidance.