CDS Annotations Part 4: Metadata Extensions — Separating UI from the Data Model
Separate UI from the data model with metadata extensions: syntax, allowed annotations, layers and precedence, and how to organize extensions for several apps on the same business object.
Explanation
📌 KEY POINTS
✅ A metadata extension (annotate entity … with) adds annotations to a view in a separate object.
✅ The view must allow it: @Metadata.allowExtensions: true.
✅ Layers decide precedence: #CUSTOMER > #PARTNER > #INDUSTRY > #LOCALIZATION > #CORE.
⚠️ Not every annotation is allowed in a metadata extension — semantics stay in the view.
━━━━━━━━━━━━━━━━━━━━
▶ WHERE WE ARE
Part 3 wrote UI annotations. This part explains the object that holds them — and why it keeps your data model clean.
━━━━━━━━━━━━━━━━━━━━
▶ 1. WHY METADATA EXTENSIONS
- The data model (fields, keys, associations, semantics) changes rarely.
- The UI (columns, positions, labels, sections) changes often.
Keeping UI annotations in a separate object means: UI changes do not touch the view, views stay readable, different teams can work in parallel, and changes to the UI need no re-activation of dependent views.
━━━━━━━━━━━━━━━━━━━━
▶ 2. SYNTAX
@Metadata.layer: #CUSTOMER
annotate entity ZC_PurchReq with
{
@UI.lineItem: [{ position: 10 }]
RequestID;
}
- One metadata extension per layer for a given entity; the file name is freely chosen (convention: same name as the view).
- You list elements by name, followed by a semicolon, with annotations in front.
- Header annotations (e.g. @UI.headerInfo) go before "annotate entity".
━━━━━━━━━━━━━━━━━━━━
▶ 3. ENABLING
The annotated view needs @Metadata.allowExtensions: true in its header. Without it, the extension is ignored (with a warning).
━━━━━━━━━━━━━━━━━━━━
▶ 4. LAYERS AND PRECEDENCE
Several extensions in different layers can annotate the same view. For the same annotation, the higher layer wins:
#CUSTOMER (highest) → #PARTNER → #INDUSTRY → #LOCALIZATION → #CORE (lowest).
Annotations in any metadata extension override those written directly in the view, which override propagated ones.
Customers and partners use this to adapt SAP-delivered UIs without modifying SAP objects.
━━━━━━━━━━━━━━━━━━━━
▶ 5. WHAT BELONGS IN AN EXTENSION
Allowed and typical: @UI.*, @EndUserText.*, @Consumption.* (e.g. value helps, filters), @Search.* on elements.
Not allowed or not sensible: annotations that define the data model's meaning or behavior — e.g. @Semantics amount/currency references, @ObjectModel text associations, @AccessControl. Those stay in the view.
💡 TIP: ADT marks annotations that are not permitted in a metadata extension with an error; the ABAP keyword documentation lists "MDE-enabled" annotations.
━━━━━━━━━━━━━━━━━━━━
▶ 6. ORGANIZING SEVERAL APPS
- Same BO, two apps (buyer app, approver app) → two projections (ZC_PurchReq, ZC_PurchReqApprove), each with its own metadata extension.
- Do not try to serve two layouts from one projection.
━━━━━━━━━━━━━━━━━━━━
▶ STEP BY STEP
- Add @Metadata.allowExtensions: true to ZC_PurchReq and ZC_PurchReqItem.
- Right-click the projection → New → Metadata Extension; choose layer #CUSTOMER.
- Move all @UI annotations from the view into the extension.
- Activate; check the preview — the UI must look the same.
✅ CHECKPOINT: your UI lives outside the data model. Part 5 shows how to debug annotations that "do not work".
Code example
// ===== 1. Projection view: allow extensions, keep only model annotations =====@EndUserText.label: 'Purchase Request'@AccessControl.authorizationCheck: #CHECK@Metadata.allowExtensions: true // REQUIRED for metadata extensionsdefine root view entity ZC_PurchReq provider contract transactional_query as projection on ZR_PurchReq{ key RequestUUID, RequestID, Description, Status, @Semantics.amount.currencyCode: 'CurrencyCode' // semantics stay in the view TotalAmount, CurrencyCode, _Item : redirected to composition child ZC_PurchReqItem} // ===== 2. Metadata extension (layer CUSTOMER): UI only =====@Metadata.layer: #CUSTOMER@UI.headerInfo: { typeName: 'Purchase Request', typeNamePlural: 'Purchase Requests', title: { type: #STANDARD, value: 'RequestID' } }annotate entity ZC_PurchReq with{ @UI.facet: [{ id: 'General', purpose: #STANDARD, type: #IDENTIFICATION_REFERENCE, label: 'General', position: 10 }] @UI.hidden: true RequestUUID; @UI.lineItem: [{ position: 10 }] @UI.selectionField: [{ position: 10 }] @UI.identification: [{ position: 10 }] RequestID; @UI.lineItem: [{ position: 20 }] @UI.identification: [{ position: 20 }] @EndUserText.label: 'Request Title' // labels are allowed here Description; @UI.lineItem: [{ position: 30 }] @UI.textArrangement: #TEXT_ONLY Status;} // ===== Precedence for the same annotation on one element =====// metadata extension #CUSTOMER > #PARTNER > #INDUSTRY > #LOCALIZATION > #CORE// > annotation written in the view > propagated from lower views // ===== NOT allowed in the extension (keep in the view) =====// @Semantics.amount.currencyCode ... @AccessControl ... @ObjectModel.text.association ...Real project scenario
A company used an SAP-delivered Fiori app but needed different column order and two extra filters. The first idea was to copy the SAP CDS views into Z-views — which would have meant maintaining copies forever and losing SAP updates.
Instead, a metadata extension in layer #CUSTOMER on the released consumption view changed positions and added selection fields. SAP upgrades kept delivering improvements to the underlying view, and the customer UI adjustments survived without modification. The same pattern was later used for their own RAP apps.
Common mistakes
- Forgetting @Metadata.allowExtensions: true.
- Putting semantic or access annotations into the extension.
- Two extensions in the same layer for the same view.
- Not knowing that a higher layer overrides lower ones and the view.
- One projection trying to serve two different app layouts.
- Copying SAP views instead of extending their metadata.
Best practices
- Data model and semantics in views; UI in metadata extensions.
- Use #CUSTOMER for own development unless your organization defines otherwise.
- One projection + one metadata extension per app.
- Name the extension like the view it annotates.
- Verify with the preview and the Active Annotations view.
Interview angle
Questions: "What is a metadata extension?", "What does @Metadata.allowExtensions do?", "Which layer wins?", "Which annotations are not allowed in MDEs?"
Strong answer: an MDE (annotate entity … with) adds annotations to a CDS entity in a separate object; the entity must allow extensions; layers #CORE < #LOCALIZATION < #INDUSTRY < #PARTNER < #CUSTOMER determine precedence, and MDE annotations override view annotations. UI, labels, consumption and search annotations fit; semantics, access control and model definitions stay in the view. Mention adapting SAP apps without copying views.
Practice this lesson
Quick check
1. Three Fiori apps need different layouts for the same RAP business object. What is the best structure?
2. Which statement about CDS annotation propagation is correct?
3. An object page shows 45 fields in one section. Which annotations structure it into sections?
Interview questions
1. What is a metadata extension, how are layers resolved, and which annotations can it not contain?
Answer it in your own words first, then compare.
2. Three apps reuse one interface view that contains all UI annotations. Changing one app breaks the others. How do you reorganize?
Answer it in your own words first, then compare.
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
- CDS Annotations Part 1: What Annotations Are and Where They Apply
- CDS Annotations Part 2: Semantic Annotations for Currency, Units and Texts
- CDS Annotations Part 3: Fiori UI Annotations — Line Items, Facets and Selection Fields
- CDS Annotations Part 5: Troubleshooting Precedence, Metadata and UI Behavior