CDS Associations
RAP / CDS / ODataintermediate

CDS Associations Part 4: Associations vs Compositions in a RAP Business Object

Key takeaway

Understand the CDS side of a RAP business object tree: composition vs association, "association to parent", and how projections redirect compositions, parents and normal associations.

Explanation

📌 KEY POINTS
✅ Association = "related to"; composition = "owned by, lives and dies with the parent".

✅ Root declares composition [0..*] of Child; child declares association to parent.

✅ Projections must redirect: composition child, parent, and normal associations.

⚠️ Only use composition inside one business object — never to another BO.

━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE

Parts 1–3 used associations for reading. RAP business objects add a second kind of relationship: the composition. This lesson shows the CDS syntax; RAP Business Objects Part 1 covers the design decision.

━━━━━━━━━━━━━━━━━━━━
▶ 1. THE DIFFERENCE

  • Association: two entities are related, each has its own lifecycle. A sales order refers to a customer; deleting the order does not delete the customer.
  • Composition: the child is part of the parent. A purchase request item exists only within its request; deleting the request deletes the items; locks, authorization and saves go through the root.

━━━━━━━━━━━━━━━━━━━━
▶ 2. THE ROOT SIDE

In the root view entity ("define root view entity"):

composition [0..*] of ZR_PurchReqItem as _Item

  • No ON condition — it is derived from the child's "association to parent".
  • Cardinality usually [0..*] or [1..*].

━━━━━━━━━━━━━━━━━━━━
▶ 3. THE CHILD SIDE

In the child view entity:

association to parent ZR_PurchReq as _Request

on $projection.RequestUUID = _Request.RequestUUID

  • The child must contain the parent's key field (RequestUUID).
  • A deeper child (e.g. schedule line under item) declares "association to parent" to the item and usually an extra association to the root for dependent locking.

━━━━━━━━━━━━━━━━━━━━
▶ 4. PROJECTIONS: REDIRECT EVERYTHING

Projection views (ZC_) sit on the R_ views. Associations of the base view point to base entities, so in the projection you redirect them to the projected entities:

  • _Item : redirected to composition child ZC_PurchReqItem
  • _Request : redirected to parent ZC_PurchReq
  • _Supplier : redirected to ZC_Supplier (only if you project the target too); otherwise expose the base association as is.

⚠️ WATCH OUT: if the projection of the child is missing or not redirected, activation fails or navigation disappears from the service.

━━━━━━━━━━━━━━━━━━━━
▶ 5. WHAT BELONGS WHERE

  • Composition only between entities of the same BO.
  • References to other BOs (supplier, product, another RAP BO) stay normal associations.
  • Value helps and texts are associations too, not compositions.

━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP

  1. In ZR_PurchReq, declare the composition to ZR_PurchReqItem.
  2. In ZR_PurchReqItem, declare association to parent.
  3. Activate both together (they depend on each other).
  4. In ZC_PurchReq and ZC_PurchReqItem, redirect the composition and the parent.
  5. Check the service $metadata: navigation _Item and _Request must appear.

✅ CHECKPOINT: you can build and project a composition tree correctly.

Code example

ABAP Code
// ===== Step 1: root with composition + normal association =====define root view entity ZR_PurchReq  as select from zpr_head  composition [0..*] of ZR_PurchReqItem as _Item          // owned children (no ON)  association [0..1] to I_Supplier       as _Supplier      // reference to another BO    on $projection.Supplier = _Supplier.Supplier{  key request_uuid as RequestUUID,      request_id   as RequestID,      supplier     as Supplier,      _Item,      _Supplier} // ===== Step 2: child with association to parent =====define view entity ZR_PurchReqItem  as select from zpr_item  association to parent ZR_PurchReq as _Request    on $projection.RequestUUID = _Request.RequestUUID     // parent key in the child{  key item_uuid   as ItemUUID,      parent_uuid as RequestUUID,      item_no     as ItemNo,      _Request} // ===== Step 4: projections redirect the tree =====define root view entity ZC_PurchReq  provider contract transactional_query  as projection on ZR_PurchReq{  key RequestUUID,      RequestID,      Supplier,      _Item : redirected to composition child ZC_PurchReqItem,      _Supplier                                   // not projected -> keep as is} define view entity ZC_PurchReqItem  as projection on ZR_PurchReqItem{  key ItemUUID,      RequestUUID,      ItemNo,      _Request : redirected to parent ZC_PurchReq} // Step 3: activate ZR_PurchReq + ZR_PurchReqItem together (mutual dependency)// Step 5: $metadata must show navigation properties _Item and _Request

Real project scenario

A developer modeled the relationship from a purchase request to its supplier as a composition "because the supplier belongs to the request". Activation failed with confusing messages, and after a workaround the BDEF tried to lock suppliers together with requests.
The review clarified: the supplier has its own lifecycle and belongs to another object, so it is a normal association. Only request items became a composition. The projection redirected the composition and the parent, kept the supplier association as is, and navigation worked in the Fiori app immediately.

Common mistakes

  • Using composition for references to other business objects.
  • Adding an ON condition to the composition (it comes from the child).
  • Forgetting the parent key field in the child entity.
  • Activating root and child separately and getting dependency errors.
  • Not redirecting composition and parent in projections.
  • Redirecting a normal association to a projection that does not exist.

Best practices

  • Composition only for parts with no own lifecycle, within one BO.
  • Child always has the parent key and "association to parent".
  • Activate root and children together.
  • Redirect composition children and parents in every projection layer.
  • Verify navigation in $metadata after activation.

Interview angle

Questions: "Association vs composition in CDS?", "How do you declare the parent-child relationship?", "What does 'redirected to composition child' do?"
Strong answer: association relates independent entities; composition defines ownership within a BO (lifecycle, locking, saving via the root). The root declares "composition of", the child "association to parent" with the ON condition on the parent key. Projections redirect compositions ("redirected to composition child"), parents ("redirected to parent") and optionally normal associations ("redirected to").

Practice this lesson

Quick check

1. Attachments, supplier and approval history were all modeled as compositions of a request. Which should remain a normal association?

2. For an association defined in the base entity of a RAP business object to appear as a navigable OData $expand target in a projection service, what must typically be done in the projection view?

3. In the context of RAP business object modeling, how does a COMPOSITION relationship differ from a plain ASSOCIATION between two CDS entities?

Interview questions

1. How are compositions declared in CDS for a RAP business object, and what must projections do with them?

Answer it in your own words first, then compare.

2. You inherit a CDS model with many associations. What do you review before extending it?

Answer it in your own words first, then compare.

Practice more with Interview AI →

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

Interview questions on this topic

Related SAP topics