Not all business data fits neatly into flat tables. Product catalogs, org charts, and account structures often rely on parent-child relationships that standard data models can't represent well.
For example, in a grocery retail scenario, products may need to be grouped across multiple levels such as department, category, subcategory, and item, so that business users can maintain product structures consistently in one application.
This is a good fit for the ABAP RESTful application programming model (RAP), because RAP provides a structured way to model transactional business objects, define parent-child compositions, enable draft handling, expose OData V4 services, and generate an SAP Fiori elements-based user experience with less custom code.
Consider a common grocery item such as “Gala Apple 1kg.” The following example shows how its hierarchy can be modeled and managed in the system.
|
Hier Type |
Hier Value |
Parent |
Computed Hierarchy Path |
|
DEPT |
Food |
— |
Food |
|
CAT |
Fresh Produce |
Food |
Food → Fresh Produce |
|
SUBCAT |
Fruits |
Fresh Produce |
Food → Fresh Produce → Fruits |
|
ITEM |
Apples |
Fruits |
Food → Fresh Produce → Fruits → Apples |
Because each hierarchy node can be a child of a higher-level node and a parent of lower-level nodes, the model is recursive. A standard flat data model cannot represent this structure effectively. The solution requires a self-referencing table and a framework that can safely manage the transactional complexity of editing hierarchical data.
This post will explain how RAP can be applied to this grocery store product hierarchy use case—a relatable and practical scenario that demonstrates the key concepts without adding unnecessary domain complexity.
The application that we’ll use follows the standard RAP-layered architecture. Each layer has a clear responsibility, which makes the solution easier to maintain, test, and extend while aligning with SAP clean core principles for custom development.
|
Layer |
Artifact |
Purpose |
|
Persistence |
ZPRODUCT_HDR ZPRODUCT_HIER |
Database storage for products and hierarchy nodes |
|
Base CDS |
ZI_Product_B ZI_ProductHierarchy_B |
Semantic field aliases + hierarchy path computation |
|
Interface CDS |
ZI_Product_I ZI_ProductHierarchy_I |
RAP root entity + composition + associations |
|
Consumption CDS |
ZC_Product ZC_ProductHierarchy |
SAP Fiori-ready projection + association redirects |
|
Behavior Def. |
BDEF on ZI_Product_I |
CRUD, draft lifecycle, managed numbering |
|
Service Layer |
ZUI_PRODUCT_O4 (Def. + Binding) |
OData V4 exposure for SAP Fiori elements |
|
UI Annotations |
MDE on ZC_Product MDE on ZC_ProductHierarchy |
SAP Fiori elements layout metadata |
In RAP, the interface CDS views define the business object boundary. The consumption CDS views shape data for the UI. The behavior definition controls what operations are allowed and how data is persisted. These three concerns are always kept in separate artifacts.
The solution is built on two database tables. The product header table stores the product master record, while the hierarchy table stores hierarchy nodes with a self-referencing foreign key that establishes the parent–child relationship between nodes.
Note that draft tables (suffix _D) for both tables are generated automatically by the ABAP system when the behavior definition is activated. They do not need to be created manually.
CDS views play a central role in defining the hierarchical structure. The following sample illustrates how product hierarchies can be modeled and managed effectively.
Interface views define the structure of the RAP business object. The root entity declares the composition to the child entity, while the child entity declares back-associations and self-associations for parent and child navigation.
define root view entity ZI_Product_I
as select from ZI_Product_B
composition [0..*] of ZI_ProductHierarchy_I as _Hierarchy
association [0..*] to ZI_Product_B as _ProductType
on $projection.ProductType = _ProductType.ProductType
{
key ProductID,
ProductName,
@ObjectModel.foreignKey.association: '_ProductType'
ProductType,
ProductImageUrl,
LastChangedAt,
LocalLastChangedAt,
_Hierarchy,
_ProductType
}
The composition [0..*] declaration makes ZI_ProductHierarchy_I a child entity of this root. All hierarchy nodes for a product are managed as part of the same transactional business object and share the same lock.
The child view exposes hierarchy node data and declares three associations: a parent back-association to the root product, and two self-associations for upward (_Parent) and downward (_Children) navigation within the hierarchy.
define view entity ZI_ProductHierarchy_I
as select from ZI_ProductHierarchy_B
association to parent ZI_Product_I as _Product
on $projection.ProductID = _Product.ProductID
association [0..1] to ZI_ProductHierarchy_I as _Parent
on $projection.ParentHierID = _Parent.HierID
and $projection.ProductID = _Parent.ProductID
association [0..*] to ZI_ProductHierarchy_I as _Children
on $projection.HierID = _Children.ParentHierID
and $projection.ProductID = _Children.ProductID
{
key HierID,
ProductID,
ParentHierID,
HierType,
HierValue,
HierarchyPath,
LastChangedAt,
LocalLastChangedAt,
_Product,
_Parent,
_Children
}
Consumption views project from interface views and redirect associations to their consumption counterparts. This completes the RAP composition chain and makes the entities SAP Fiori-ready.
@Metadata.allowExtensions: true
@UI.headerInfo: {
typeName: 'Product',
typeNamePlural: 'Products',
title: { value: 'ProductName' },
description: { value: 'ProductType' },
imageUrl: 'ProductImageUrl'
}
define root view entity ZC_Product
provider contract transactional_query
as projection on ZI_Product_I
{
key ProductID,
ProductName,
ProductType,
@Semantics.imageUrl: true
ProductImageUrl,
_Hierarchy : redirected to composition child ZC_ProductHierarchy
}
@Metadata.allowExtensions: true
define view entity ZC_ProductHierarchy
as projection on ZI_ProductHierarchy_I
{
@ObjectModel.text.element: [ 'HierValue' ]
key HierID,
ProductID,
@ObjectModel.text.element: [ 'ParentHierValue' ]
ParentHierID,
_Parent.HierValue as ParentHierValue,
HierType,
HierValue,
HierarchyPath,
_Product : redirected to parent ZC_Product,
_Parent : redirected to ZC_ProductHierarchy,
_Children : redirected to ZC_ProductHierarchy
}
The behavior definition specifies the transactional capabilities of the RAP business object. This solution uses a managed implementation with draft support. The two entities—Product as the root and Hierarchy Node as the child—serve different roles and therefore require different behavior settings.
|
Aspect |
Product Entity |
Hierarchy Entity |
|
Entity |
ZI_Product_I (Product) |
ZI_ProductHierarchy_I (Hierarchy Node) |
|
Role |
Lock master — owns the draft lifecycle |
Lock dependent — inherits lock from Product |
|
Persistent Table |
ZPRODUCT_HDR |
ZPRODUCT_HIER |
|
Draft Table |
ZPRODUCT_HDR_D (auto-generated) |
ZPRODUCT_HIER_D (auto-generated) |
|
Operations |
create, update, delete |
update, delete (create via composition) |
|
Draft Actions |
Edit, Resume, Prepare, Activate optimized, Discard |
Inherited from root; with draft on _Product |
|
Key Numbering |
product_id — user-provided |
hier_id — managed (UUID auto-generated by RAP) |
|
Association |
_Hierarchy { create; with draft; } |
_Product, _Parent, _Children |
|
Lock Strategy |
total etag on LastChangedAt |
etag master on LocalLastChangedAt |
The most important design decisions in the behavior definition are:
The service definition and metadata annotation extensions complete the application stack. The service definition specifies which entities are exposed through OData V4, while the annotation extensions define how SAP Fiori elements renders those entities in the list report and object page.
Here’s an example of a list page:
And here’s an example of an object page:
The service definition exposes four entities. Two are transactional and two serve value help purposes only:
|
Entity |
Role |
Purpose |
|
ZC_Product |
Transactional (Root) |
Main entry point for the SAP Fiori application. Exposes the product list report and object page. |
|
ZC_ProductHierarchy |
Transactional (Child) |
Exposes hierarchy nodes within the product object page via the composition association. |
|
ZI_Product_B |
Value Help |
Provides the ProductType value help in the product creation and edit forms. |
|
ZI_ParentHierVH |
Value Help |
Provides the parent hierarchy node selection, scoped by ProductID to prevent cross-product assignments. |
A best practice is to expose only the entities the UI actually needs. Unnecessary entity exposure increases the attack surface of the service and can degrade OData metadata performance in large landscapes.
Metadata extension files apply UI annotations to the consumption views without modifying the CDS views themselves. The table below shows some sample annotations used in the application.
|
Annotation |
Applied To |
Value/Setting |
Effect in SAP Fiori Elements |
| @UI.facet | Both views |
#IDENTIFICATION_REFERENCE #LINEITEM_REFERENCE |
Groups fields into object page sections. |
| @UI.lineItem | Both views | position: 10, 20, 30 | Controls which fields appear as columns in the list report table and the hierarchy items embedded table. |
| @UI.selectionField | Both views | position: 10, 20, 30 | Adds fields to the filter bar above the list report. |
| @UI.identification | Both views | position: 10, 20, 30 | Places fields in the General Information section on the object page. |
| @UI.textArrangement |
ProductType ParentHierID |
#TEXT_ONLY | Hides the raw key value and shows only the descriptive text in the UI field. |
| @Consumption.valueHelpDefinition |
ProductType ParentHierID |
ZI_Product_B ZI_ParentHierVH |
Shows a value help dialog to the field. Parent hierarchy help uses additionalBinding to scope by ProductID. |
| @UI.hidden | ParentHierValue | true | Keeps the derived text field invisible in the UI while still available for text resolution via @ObjectModel.text.element. |
| @Semantics.imageUrl | ProductImageUrl | Tells SAP Fiori elements to render the field value as an image URL and display the image thumbnail in the list. |
The following points highlight the most important aspects of this solution.
This post was originally published 7/2026.