Draft Handling Part 1: Draft Architecture — Active Data and Draft Persistence
Understand how RAP draft works under the hood: active and draft instances, draft tables, %is draft and IsActiveEntity, draft administrative data, and what the framework does for you.
What this complete series covers
Learn Draft Handling in a clear, practical sequence — from the basic idea to real project use, troubleshooting, and interview-ready understanding.
- Who it is for
- Best for SAP consultants, developers, and learners who want a practical understanding of Draft Handling.
- Prerequisites
- Basic ABAP and ADT in Eclipse are recommended. The series explains the RAP/CDS/OData concepts as they are introduced.
- Final outcome
- By the end, you should be able to explain Draft Handling clearly, follow the main end-to-end flow, apply it in a project scenario, and troubleshoot the most common issues.
- Part 1: Draft Handling Part 1: Draft Architecture — Active Data and Draft Persistence
Understand why RAP draft exists: it persists intermediate user edits in draft storage while active business data remains separate, allowing long-running Fiori interactions over stateless HTTP sessions.
- Part 2: Draft Handling Part 2: Draft Lifecycle — Create, Edit, Resume, Activate, Discard
Trace the full RAP draft lifecycle from creation or edit of active data through persisted changes, resume, validation, activation, and discard so support teams can diagnose where a
- Part 3: Draft Handling Part 3: Draft Locking, ETags and Concurrency
Protect draft editing with ownership/locking while using ETag or total-ETag concepts to detect active-data changes, so long-running drafts do not silently overwrite newer committed business state.
- Part 4: Draft Handling Part 4: Validations, Determinations and Side Effects in Draft
Design draft behavior so derived values, validation timing, and UI side effects keep the editing experience responsive without allowing invalid drafts to become invalid active business data.
- Part 5: Draft Handling Part 5: Troubleshooting Draft Incidents
Diagnose draft incidents by separating draft creation/edit, ownership/locking, validation, activation, and active-data concurrency, and never repair them with direct draft-table manipulation.
System and release assumptions: Examples target recent SAP S/4HANA / ABAP Cloud capabilities. RAP, CDS, OData, and Fiori Elements syntax and tooling can differ by ABAP platform release.
Explanation
📌 KEY POINTS
✅ Draft = a saved, unfinished copy of the data that survives browser refreshes and breaks.
✅ Every instance exists as active (IsActiveEntity = true) and/or draft (IsActiveEntity = false).
✅ Draft data lives in separate draft tables that the framework manages.
⚠️ Your code must use %tky (with %is_draft) — never only the key.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT THIS SERIES COVERS
- Part 1 — draft architecture (this lesson)
- Part 2 — draft lifecycle: create, edit, resume, activate, discard
- Part 3 — draft locking, ETags and concurrency
- Part 4 — validations, determinations and side effects in draft
- Part 5 — troubleshooting draft incidents
Prerequisite: Managed RAP Part 4 (enabling draft).
━━━━━━━━━━━━━━━━━━━━
▶ WHY DRAFT EXISTS
Web apps are stateless: each request is independent. But users edit complex objects over minutes or hours, get interrupted, switch devices. Without draft, unsaved input would be lost and the data could only be checked when the user finally clicks Save. Draft stores every change immediately in a draft copy, so:
- nothing is lost,
- determinations can react while the user types (on modify),
- the object is protected from other editors,
- the active data changes only when the user activates (saves).
━━━━━━━━━━━━━━━━━━━━
▶ TWO VERSIONS OF ONE INSTANCE
- Active instance — the real business data in your persistent tables (zpr_head).
- Draft instance — the work-in-progress copy in the draft table (zpr_head_d).
Both share the same key; they are distinguished by the draft indicator:
- in ABAP/EML: %is_draft = if_abap_behv=>mk-on (draft) or mk-off (active), part of %tky;
- in OData: the key contains IsActiveEntity=true/false.
A new object starts as draft only; an existing object gets a draft when the user clicks Edit.
━━━━━━━━━━━━━━━━━━━━
▶ DRAFT TABLES
- One draft table per entity, generated by ADT from the BDEF quick fix ("draft table zpr_head_d").
- It contains all fields of the entity plus an include with draft administrative fields (the "draft admin include"), e.g. who created the draft and when it was last changed.
- The framework reads and writes draft tables; your code never does.
💡 TIP: after adding a field to the CDS entity, regenerate the draft table — otherwise activation fails with field-mismatch errors.
━━━━━━━━━━━━━━━━━━━━
▶ DRAFT ADMINISTRATIVE DATA
The framework keeps metadata per draft: owner, created at, last changed at, in-process-by user. Fiori uses it to show "Draft", "Locked by …" and "Unsaved changes by …". You do not maintain it yourself.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT THE FRAMEWORK DOES FOR YOU
- Stores every change of a draft instance in the draft tables.
- Handles the draft actions Edit, Activate, Discard, Resume, Prepare.
- Copies draft to active on activation (running the save sequence) and deletes the draft afterwards.
- Manages the exclusive lock on the active instance while a draft exists (Part 3).
- Routes EML and OData requests to draft or active data based on %is_draft / IsActiveEntity.
━━━━━━━━━━━━━━━━━━━━
▶ WHAT YOU DO
- Declare draft in the BDEF (checklist in Behavior Definitions Part 4).
- Use %tky everywhere in handler code.
- Choose on modify vs on save for determinations, and list validations in Prepare (Part 4).
✅ CHECKPOINT: you can explain what happens in the database when a user edits a purchase request with draft.
Code example
// ===== Draft enablement (base BDEF) - summary =====managed implementation in class zbp_r_purchreq unique;strict ( 2 );with draft; // draft for the whole BO define behavior for ZR_PurchReq alias PurchaseRequestpersistent table zpr_head // ACTIVE datadraft table zpr_head_d // DRAFT data (generated)lock mastertotal etag LastChangedAtauthorization master ( global, instance )etag master LocalLastChangedAt{ create; update; delete; association _Item { create; with draft; } draft action Edit; draft action Activate optimized; draft action Discard; draft action Resume; draft determine action Prepare { }} " ===== Draft vs active in EML: the difference is %is_draft inside %tky =====READ ENTITIES OF zr_purchreq ENTITY PurchaseRequest ALL FIELDS WITH VALUE #( ( RequestUUID = uuid %is_draft = if_abap_behv=>mk-on ) ) " draft copy RESULT DATA(draft_version). READ ENTITIES OF zr_purchreq ENTITY PurchaseRequest ALL FIELDS WITH VALUE #( ( RequestUUID = uuid %is_draft = if_abap_behv=>mk-off ) ) " active data RESULT DATA(active_version). " ===== The same in OData =====" GET .../PurchaseRequest(RequestUUID=<uuid>,IsActiveEntity=false) -> draft" GET .../PurchaseRequest(RequestUUID=<uuid>,IsActiveEntity=true) -> active " ===== What the tables contain while a user edits request 4711 =====" zpr_head : 4711, 'Office chairs', status N (unchanged)" zpr_head_d : 4711, 'Ergonomic office chairs', status N + draft admin fields" -> after Activate: zpr_head updated, zpr_head_d row deletedReal project scenario
Users of a non-draft purchase request app lost their input several times a week: long item lists were entered, then a session timeout or accidental browser refresh discarded everything. Validation errors also appeared only at the very end, after twenty minutes of typing.
Enabling draft changed the experience: every field change was stored immediately, users could leave and continue later from any device, calculated totals appeared while typing, and errors surfaced when they clicked Save via Prepare instead of after a failed submit. The number of "lost data" tickets dropped to zero.
Common mistakes
- Thinking draft is only a Fiori UI feature — it is a backend persistence concept.
- Writing to or reading from draft tables directly.
- Using key fields instead of %tky, so code works on the wrong version.
- Forgetting to regenerate draft tables after adding fields.
- Enabling draft in API services where clients do not need it.
Best practices
- Enable draft for UI scenarios with complex or long edits.
- Always use %tky; test every handler with draft and active instances.
- Let the framework manage draft tables and admin data.
- Regenerate draft tables whenever the entity structure changes.
- Keep APIs without draft (separate projection).
Interview angle
Questions: "What is draft in RAP and why is it needed?", "How are draft and active instances distinguished?", "Where is draft data stored?"
Strong answer: draft stores unfinished changes persistently for stateless web apps; each instance can exist as active and draft, distinguished by %is_draft in %tky (IsActiveEntity in OData); draft data lives in framework-managed draft tables with an admin include; activation runs the save sequence, copies draft to active and deletes the draft. Code must use %tky.
Practice this lesson
Quick check
1. Where is the draft table of a RAP entity declared?
2. Users of a complex maintenance app lose unsaved input after session timeouts. Which RAP feature addresses this?
3. Which keyword must be added to a RAP behavior definition to enable draft handling for a business object?
Interview questions
1. What is draft in RAP, why is it needed, and how are draft and active instances distinguished?
Answer it in your own words first, then compare.
2. When would you enable draft for a RAP BO, and when not?
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.