RAP Composition: Root and Child Entities
Composition is the RAP construct that binds a root entity to one or more child entities so they behave as a single transactional business object. The child has no independent lifecycle: its lock, authorization check, ETag, draft state and save sequence all come from the root. Use it for header/item structures such as travel and booking, not for shared master data.
This page covers how RAP models header-item style business objects through composition, the difference between a composition child and a plain to-many association, and the runtime consequences that catch people out: locking, cascading delete, numbering, and draft handling all flow through the root. It also covers the decision points an architect should raise before declaring something a composition rather than an association.
Published 16 Sept 2026· 1,479 words
What it is
A composition is the CDS association, marked as a composition, that links a root entity to a child entity in the behavior definition, and it is declared on the root's side as owning the child. The structural fact that explains most of the confusion: a composition child has no life of its own. It is not a separately lockable, separately authorized, separately savable object. Its lock is the root's lock, its authorization check defaults to the root's authorization object, its ETag comes from the root, and its create, update and delete are executed inside the root's save sequence, not as independent transactions. People coming from a classic table-and-BAPI mindset instinctively reach for a separate service or a separate action to touch the child, and that instinct is wrong for anything modelled as a composition child. If a child genuinely needs an independent existence, it should not be a composition child, it should be its own root reached by an ordinary association.
When to use it
Use composition for header/item and header/schedule-line style structures where the child cannot meaningfully exist without the header and must always be maintained inside the same transaction: sales document and items, travel and bookings, purchase requisition and items. Use it when deleting the root should delete the children, when the same lock must cover both, and when the object page in Fiori Elements needs a header with editable sub-tables. Do not use composition when the child is master data shared across many parents, such as a business partner referenced from an order, or when the child needs to be queried and changed outside the context of any particular root. Do not use it either as a substitute for a reporting fan-out; a read-only expand for display purposes is better served by a plain to-many association, which carries none of the transactional weight.
How it fits the stack
Composition sits in the behavior definition layer, built on top of the CDS view entities that model root and child, and it is what the service definition exposes as a deep-insertable, nested OData entity set with dollar-expand. Below it sits the persistence: separate database tables for root and child, tied together by the composition's foreign key, and a draft table pair if draft handling is active. Above it sits the save sequence, which the RAP runtime orchestrates across early and late numbering phases, and the Fiori Elements object page, which renders the child as a facet table. Composition replaces what used to be hand-coded header-item consistency logic inside a BAPI or function module: manual FOR ALL ENTRIES joins, manual enqueue of both header and item lock objects, and manual cascading delete logic. In RAP those are runtime-provided once the composition is declared correctly.
A worked example
Take a travel-and-booking style object: root entity for Travel, child entity for Booking, connected by a composition association exposed in the root's behavior definition as owning the booking node with create, update and delete operations. A user opens the object page for a travel, adds two bookings, and saves once. The OData deep insert sends one payload with the travel header and a nested array of bookings. The RAP runtime creates the root first, assigns a number if early numbering is used, then processes the child creates inside the same LUW, assigning booking IDs through late numbering since the parent key may not be final until the draft is activated. A determination on the root recalculates the total booking amount whenever a child is inserted or changed, triggered by the association between the nodes. When the travel is deleted, the booking rows are removed as part of the same operation because they are modelled as composition children, not as independently persisted rows with only a foreign key relationship.
How to choose
- Composition versus association: choose composition only when the child's lifecycle is fully owned by the root and deletion of the root must cascade; choose a plain association when the child is reused, independently queried, or maintained through its own object.
- Standalone-exposed child versus embedded-only child: RAP allows a composition child to also be exposed as directly modifiable through its own operations, but this bypasses the root's lock unless carefully controlled. Ask whether there is a real business need for direct child access before enabling it, since most header/item scenarios do not need it.
- Draft depth: a deep composition with multiple nested levels multiplies the number of draft tables and the complexity of the save sequence. Ask how many nesting levels are truly needed before modelling three or four levels deep just because the data model allows it.
- Authorization scope: composition children inherit the root's authorization check by default. If a child genuinely needs its own authorization object, that is extra design against the grain of the pattern and should be justified explicitly, not added by default.
- Numbering strategy per node: decide early versus late numbering for each entity in the composition individually, since the child's key is frequently not knowable until the root is finalized, which forces late numbering on the child even when the root uses early numbering.
- Volume, not row count in the IDE: test the composition with realistic child counts, tens or hundreds of items rather than the two or three rows used in a demo, since the RAP runtime instantiates the full object tree per request and cost scales with total node count, not with the root count alone.
Common pitfalls
- Trying to modify a composition child through a service or action that does not go through the root; this either fails outright with a locking or authorization error, or worse, succeeds and leaves the root's ETag and derived fields inconsistent with the child change.
- Assuming cascading delete happens automatically without checking that the composition and its delete operation are actually declared on the child; an incompletely modelled composition can leave orphaned child rows after the root is deleted.
- Deep hierarchies of three or more levels that work fine with a handful of test rows in the IDE but become a measurable performance cliff under production child counts, because every request loads and instantiates the entire tree.
- Draft tables for every node in the composition multiply the number of database objects and complicate any later attempt to hand off to an unmanaged save implementation, since the draft synchronization logic has to account for every node, not just the root.
- Forgetting that the child's changes must propagate a last-changed timestamp or ETag update to the root; without this, the Fiori Elements object page shows an optimistic-lock failure or a stale header total after a booking is added.
- Modelling a purely read-only reporting fan-out as a composition instead of a to-many association, which drags unnecessary lock and authorization semantics into a view that only needed to be displayed, not maintained.
ECC, S/4HANA and clean core
Composition is a standard, clean-core-aligned building block for custom business objects on S/4HANA; it is the recommended way to model header/item structures instead of hand-rolled header-item consistency logic in a custom function module or BAPI. There is no equivalent construct in classic ECC development; the pattern it replaces there is manual enqueue of a header lock object plus a separate item lock object, and manual delete cascading written by hand. There is no automated migration path from an ECC header-item BAPI to a RAP composition; it requires a fresh behavior definition designed around the actual business rule for what must be atomic.
Whose problem this is
The developer implements the CDS composition association and the behavior definition and implementation class for both root and child. The architect decides where the composition boundary lies, which entities belong inside one business object versus being linked by a plain association, and reviews the lock and performance implications of the chosen depth. The functional consultant supplies the business rule for what must always be saved together.
Related SAP objects
Reviewed pages this object connects to in the ERPClimb knowledge graph.
Source: ERPClimb — https://erpclimb.com/sap-technical-topics/rap-business-object-compositionERPClimb is an independent platform and is not affiliated with SAP SE. Reference pages are written and reviewed by SAP consultants for learning and troubleshooting.