Appearance
Lesson 13 Companion — RAP Business Rules and Messages
This companion supports:
text
Lesson 13 — RAP Validations, Determinations, and MessagesThe guided lesson turns the Reservation BO from:
text
managed CRUDinto:
text
managed CRUD
+
business rules
+
consumer-facing messages1. Core architecture
Before Lesson 13:
text
Consumer
↓
EML
↓
Managed RAP CRUD
↓
PersistenceAfter Lesson 13:
text
Consumer
↓
EML
↓
RAP BO
├─ standard managed operations
├─ determinations
├─ validations
└─ messages
↓
PersistenceThe rules belong to the Business Object provider.
2. Business rule ownership
The backend BO should own authoritative rules such as:
text
required business data
valid business dates
acceptable amounts
automatic defaults
allowed state transitionsA UI may also perform convenience checks.
But UI checks are not a substitute for backend enforcement.
3. Determination definition
A determination is declared in the Behavior Definition.
Lesson 13 uses:
abap
determination setInitialStatus on modify { create; }Meaning:
text
name = setInitialStatus
timing = on modify
trigger = create4. Determination timing
on modify
Runs during the interaction phase when the trigger is met.
This is appropriate for values that should become available while the RAP transaction is still being built.
Examples:
text
default status
derived subtotal
calculated classification
automatically copied dataon save
Determinations can also be modeled for save processing.
Lesson 13 does not need that variant.
5. Determinations should be idempotent
A determination may execute more than once in a RAP transaction.
Its result should remain stable if the same conditions are processed repeatedly.
The Lesson 13 determination is safe because it only sets:
text
Status = Nwhen:
text
Status IS INITIALOnce Status is filled, another execution does not change it again.
6. Validation definition
A validation is declared in the BDEF.
Example:
abap
validation validateCustomer on save
{ create; field CustomerName; }This identifies:
text
implementation = validateCustomer
timing = on save
triggers = create or CustomerName change7. Why validation is on save
A validation protects whether the current transactional state is allowed to reach persistence.
That is why the lesson tests it through:
text
COMMIT ENTITIESFlow:
text
MODIFY ENTITIES
↓
transactional buffer
↓
COMMIT ENTITIES
↓
validation
↓
save or reject8. Trigger fields
This declaration:
abap
validation validateFee on save
{ create; field Fee, CurrencyCode; }does not mean the method receives only those fields.
It means those operations/field changes are relevant triggers.
The implementation still reads the fields it needs from BO state.
9. Complete Lesson 13 BDEF
abap
managed implementation in class zbp_r_resv_### unique;
strict ( 2 );
define behavior for ZR_Reservation_### alias Reservation
persistent table zresv_###
lock master
authorization master ( none )
etag master LocalLastChangedAt
{
create;
update;
delete;
field ( numbering : managed, readonly ) Uuid;
field ( readonly )
LocalCreatedBy,
LocalCreatedAt,
LocalLastChangedBy,
LocalLastChangedAt,
LastChangedAt;
determination setInitialStatus on modify { create; }
validation validateCustomer on save
{ create; field CustomerName; }
validation validateDate on save
{ create; field ReservationDate; }
validation validateFee on save
{ create; field Fee, CurrencyCode; }
mapping for zresv_###
{
Uuid = uuid;
ReservationNo = reservation_no;
CustomerName = customer_name;
CustomerEmail = customer_email;
WorkshopCode = workshop_code;
ReservationDate = reservation_date;
Status = status;
Fee = fee;
CurrencyCode = currency_code;
Notes = notes;
LocalCreatedBy = local_created_by;
LocalCreatedAt = local_created_at;
LocalLastChangedBy = local_last_changed_by;
LocalLastChangedAt = local_last_changed_at;
LastChangedAt = last_changed_at;
}
}10. Handler class reference
The Behavior Pool local implementation class should contain signatures equivalent to:
abap
CLASS lhc_Reservation DEFINITION
INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS setInitialStatus
FOR DETERMINE ON MODIFY
IMPORTING keys FOR Reservation~setInitialStatus.
METHODS validateCustomer
FOR VALIDATE ON SAVE
IMPORTING keys FOR Reservation~validateCustomer.
METHODS validateDate
FOR VALIDATE ON SAVE
IMPORTING keys FOR Reservation~validateDate.
METHODS validateFee
FOR VALIDATE ON SAVE
IMPORTING keys FOR Reservation~validateFee.
ENDCLASS.Prefer ADT-generated method declarations.
The exact generated local class name may vary.
11. Complete determination reference
abap
METHOD setInitialStatus.
READ ENTITIES OF ZR_Reservation_### IN LOCAL MODE
ENTITY Reservation
FIELDS ( Status )
WITH CORRESPONDING #( keys )
RESULT DATA(reservations).
DATA reservations_to_update
TYPE TABLE FOR UPDATE ZR_Reservation_###.
LOOP AT reservations INTO DATA(reservation)
WHERE Status IS INITIAL.
APPEND VALUE #(
%tky = reservation-%tky
Status = 'N'
) TO reservations_to_update.
ENDLOOP.
IF reservations_to_update IS NOT INITIAL.
MODIFY ENTITIES OF ZR_Reservation_### IN LOCAL MODE
ENTITY Reservation
UPDATE FIELDS ( Status )
WITH reservations_to_update.
ENDIF.
ENDMETHOD.12. Why TYPE TABLE FOR UPDATE?
RAP exposes BDEF-derived types for BO operations.
This declaration:
abap
TYPE TABLE FOR UPDATE ZR_Reservation_###creates an internal table shaped for an update request against that BO.
It is preferable to inventing an unrelated local structure.
13. Why %tky is used
Inside a RAP transaction:
text
%tkyrepresents the transactional key for an instance.
It is the correct identity to carry from the read result into provider-side update and response structures.
For today's active single-node UUID BO, it ultimately corresponds to the instance identity, but %tky is the durable RAP concept.
14. IN LOCAL MODE
Provider implementation uses:
abap
IN LOCAL MODEto access the BO from inside its own implementation.
This is available only for access to the pool's own BO. It bypasses access, authorization, feature-control, and precheck restrictions where applicable. It does not switch off determinations or validations, so trigger design must still avoid repeated changes. See SAP's EML scenarios and local-mode effects.
Do not treat local mode as:
text
faster EMLor:
text
normal consumer syntaxIt is provider-side syntax.
15. Customer validation reference
abap
METHOD validateCustomer.
READ ENTITIES OF ZR_Reservation_### IN LOCAL MODE
ENTITY Reservation
FIELDS ( CustomerName )
WITH CORRESPONDING #( keys )
RESULT DATA(reservations).
LOOP AT reservations INTO DATA(reservation)
WHERE CustomerName IS INITIAL.
APPEND VALUE #(
%tky = reservation-%tky
) TO failed-reservation.
APPEND VALUE #(
%tky = reservation-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Customer name is required'
)
%element-CustomerName = if_abap_behv=>mk-on
) TO reported-reservation.
ENDLOOP.
ENDMETHOD.16. Date validation reference
abap
METHOD validateDate.
READ ENTITIES OF ZR_Reservation_### IN LOCAL MODE
ENTITY Reservation
FIELDS ( ReservationDate )
WITH CORRESPONDING #( keys )
RESULT DATA(reservations).
DATA(today) = cl_abap_context_info=>get_system_date( ).
LOOP AT reservations INTO DATA(reservation)
WHERE ReservationDate < today.
APPEND VALUE #(
%tky = reservation-%tky
) TO failed-reservation.
APPEND VALUE #(
%tky = reservation-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Reservation date cannot be in the past'
)
%element-ReservationDate = if_abap_behv=>mk-on
) TO reported-reservation.
ENDLOOP.
ENDMETHOD.17. Fee validation reference
A compact reference:
abap
METHOD validateFee.
READ ENTITIES OF ZR_Reservation_### IN LOCAL MODE
ENTITY Reservation
FIELDS ( Fee CurrencyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(reservations).
LOOP AT reservations INTO DATA(reservation).
DATA(instance_failed) = abap_false.
IF reservation-Fee <= 0.
instance_failed = abap_true.
APPEND VALUE #(
%tky = reservation-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Fee must be greater than zero'
)
%element-Fee = if_abap_behv=>mk-on
) TO reported-reservation.
ENDIF.
IF reservation-CurrencyCode IS INITIAL.
instance_failed = abap_true.
APPEND VALUE #(
%tky = reservation-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Currency code is required'
)
%element-CurrencyCode = if_abap_behv=>mk-on
) TO reported-reservation.
ENDIF.
IF instance_failed = abap_true.
APPEND VALUE #(
%tky = reservation-%tky
) TO failed-reservation.
ENDIF.
ENDLOOP.
ENDMETHOD.This version adds only one FAILED row per Reservation while still allowing multiple messages.
18. Why FAILED is authoritative
FAILED is the response structure that communicates unsuccessful instance processing.
Use it programmatically.
Do not write consumer logic such as:
text
if message text contains "required"
then assume failureMessage text is for explanation.
Failure state belongs to the failed response.
19. Why REPORTED still matters
REPORTED provides context such as:
text
error text
warning text
field target
instance relation
state/transition informationWithout it, a consumer may know:
text
something failedwithout knowing:
text
what the user should fix20. %element
Example:
abap
%element-Fee = if_abap_behv=>mk-onassociates the message with:
text
FeeThis is especially valuable for service/UI consumers.
It enables a client to connect a backend error to a concrete input field.
21. Message severity
Lesson 13 uses:
abap
if_abap_behv_message=>severity-errorbecause all demonstrated validation violations block persistence.
Other scenarios may report:
text
information
warning
successwithout failing the instance.
Do not use error severity merely for visual emphasis.
22. Transition messages in this lesson
Because the lesson does not set:
text
%state_areathe simple examples behave as transition-style messages.
This keeps the first message lesson focused.
State messages become more important for persistent UI state and draft scenarios.
23. State messages — reference only
A state message is associated with an instance and a state area.
The state area allows newer validation results to replace older messages for the same logical validation state.
This is useful for long-lived UI interactions.
Do not add state-message complexity unless students already understand:
text
FAILED
REPORTED
%MSG
%ELEMENT24. Why validation should not modify data
A validation must inspect state without modifying it:
text
read
check
report
failEML modify statements are not allowed inside validations. Do not repair a value with direct SQL either. SAP documents this restriction in Validations.
If the value is genuinely derivable, use a determination.
Keeping those roles distinct makes behavior easier to reason about.
25. Why determination should not fail normal input
A determination is primarily about deriving data.
If a determination discovers a condition that makes the instance invalid, consider whether:
text
a validationis the clearer model for rejecting the business state.
Do not overload one mechanism with every kind of rule.
26. Trigger design reference
Good trigger:
abap
validation validateDate on save
{ create; field ReservationDate; }Less precise design:
text
run every validation after every update regardless of relevant fieldsTrigger conditions communicate intent and can reduce unnecessary work.
Here, a date must be today or later when a Reservation is created or its date is changed. Existing Reservations can become historical; changing only Notes does not trigger validateDate.
27. Runtime sequence for valid create
text
MODIFY ENTITIES CREATE
↓
managed UUID numbering
↓
setInitialStatus on modify
↓
Status = N
↓
READ before commit sees N
↓
COMMIT ENTITIES
↓
validateCustomer
validateDate
validateFee
↓
all valid
↓
managed save
↓
row persisted28. Runtime sequence for invalid create
text
MODIFY ENTITIES CREATE
↓
managed UUID assigned
↓
Status determination runs
↓
instance exists in buffer
↓
COMMIT ENTITIES
↓
on-save validations
↓
invalid fields found
↓
FAILED + REPORTED
↓
no successful persistenceThis sequence is the conceptual center of Lesson 13.
The save fails for the current transaction as a whole. These tests commit the valid instance before starting the invalid create so that the earlier successful save remains intact.
29. Interaction response vs save response
The Lesson 12 distinction now becomes concrete.
Interaction phase
text
MODIFY ENTITIESmay return:
text
MAPPED
FAILED
REPORTEDfor interaction processing.
Save phase
text
COMMIT ENTITIES RESPONSE OF ...returns save-sequence responses.
An on-save validation can therefore reject work that reached the transaction successfully during MODIFY ENTITIES.
30. Why a generated UUID may exist for an invalid create
Managed UUID numbering is early numbering.
Therefore a create can receive:
text
generated UUIDbefore the final save validation succeeds.
A technical key being generated does not prove the instance was persisted.
This is a useful certification distinction.
31. Safe repeatable cleanup
For Lesson 13 reserved numbers:
text
94000001
94000002a repeatable test can first discover old instances using a read-only query:
abap
SELECT FROM ZR_Reservation_###
FIELDS Uuid
WHERE ReservationNo = '94000001'
OR ReservationNo = '94000002'
INTO TABLE @DATA(old_rows).Then delete only those UUIDs through EML:
abap
IF old_rows IS NOT INITIAL.
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
DELETE FROM VALUE #(
FOR row IN old_rows
( Uuid = row-Uuid )
)
FAILED DATA(failed_cleanup)
REPORTED DATA(reported_cleanup).
IF failed_cleanup IS NOT INITIAL.
out->write( failed_cleanup ).
out->write( reported_cleanup ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.
COMMIT ENTITIES
RESPONSE OF ZR_Reservation_###
FAILED DATA(failed_cleanup_commit)
REPORTED DATA(reported_cleanup_commit).
DATA(cleanup_commit_subrc) = sy-subrc.
IF cleanup_commit_subrc <> 0 OR failed_cleanup_commit IS NOT INITIAL.
out->write( failed_cleanup_commit ).
out->write( reported_cleanup_commit ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.
ENDIF.The lookup is read-only.
The modification still goes through the BO.
32. Why the cleanup query may read the root CDS
The exercise is resetting known training data.
The read-only lookup does not bypass business behavior.
The actual delete remains:
text
EMLThis keeps the architecture safe while making repeated class runs practical.
33. Defensive mapped-key lookup
Instead of:
abap
mapped_valid-reservation[ 1 ]-Uuidprefer:
abap
DATA(uuid_valid) =
mapped_valid-reservation[ %cid = 'VALID' ]-Uuid.This explicitly correlates:
text
consumer ID
→ generated technical key34. Guard mapped access
Before table-expression access, verify:
abap
IF failed_valid_modify IS NOT INITIAL.
...
ENDIF.For a highly defensive implementation, also use:
text
line_exists( )or:
text
OPTIONALbefore assuming a mapped row exists.
35. Reading message text in a console consumer
For learning/debugging, inspect the complete REPORTED response first.
If you want friendly console text, loop through reported entity messages and read the bound message object.
For the invalid-create save response:
abap
LOOP AT reported_invalid_commit-reservation INTO DATA(reported_row).
IF reported_row-%msg IS BOUND.
out->write( reported_row-%msg->if_message~get_text( ) ).
ENDIF.
ENDLOOP.Also output the complete response to inspect %tky and %element. Do not depend on validation or message order; SAP does not fix the execution order among validations.
Do not build business logic by parsing the resulting text.
36. Why direct message text is acceptable here
new_message_with_text is useful for a focused technical exercise.
For production-ready internationalized applications, prefer a design based on:
text
message classes
translatable texts
stable message identifiersLesson 13 teaches RAP message transport, not translation architecture.
37. Common activation error — handler missing
Symptom:
text
determination/validation declared
but implementation method missingFix:
text
place cursor on declaration
Ctrl+1
generate/add missing methodDo not remove the declaration merely to make activation green.
38. Common activation error — wrong behavior binding
BDEF:
text
validateCustomerIncorrect behavior binding in the method signature:
abap
METHODS checkCustomer FOR VALIDATE ON SAVE
IMPORTING keys FOR Reservation~validateCust.The name after Reservation~ must match the validation declared in the BDEF:
abap
METHODS checkCustomer FOR VALIDATE ON SAVE
IMPORTING keys FOR Reservation~validateCustomer.The ABAP method name itself (checkCustomer here) can differ from the BDEF validation name. The FOR binding connects them, as shown in SAP's Validation Implementation reference.
Prefer ADT-generated signatures.
39. Common activation error — wrong entity after FOR
The handler signature must reference the correct behavior entity and behavior implementation name.
For example:
abap
IMPORTING keys FOR Reservation~validateCustomerDo not use the persistence table name there.
40. Common runtime error — recursion
A determination that updates a field which itself retriggers the same determination can cause repeated processing.
Lesson 13 avoids this because:
text
trigger = createand:
text
only update Status when initialThe determination is stable.
41. Common business-design error — overwrite consumer input
Bad defaulting logic:
text
always set Status = NBetter:
text
if Status is initial
→ set NDefaulting should preserve valid explicit input unless the business contract says otherwise.
42. Common validation error — checking persistence instead of buffer
Do not validate the current transaction by selecting the persistence table and ignoring buffered values.
Use:
text
READ ENTITIES ... IN LOCAL MODEto read the BO's current transactional state.
That is what the consumer is actually trying to save.
43. Common validation error — no field target
This works as a generic message:
text
Fee is invalidBut a better response also marks:
text
Feethrough %element.
Field targets become especially valuable when the same instance has multiple validation problems.
44. Common consumer error — inspect only sy-subrc
sy-subrc is useful after COMMIT ENTITIES.
But do not ignore:
text
FAILED
REPORTEDThose structures provide BO-specific details.
A certification-quality consumer inspects the RAP responses.
45. Common consumer error — not rolling back after failed test
After intentionally causing a save failure in a test class, explicitly reset the RAP transaction:
abap
ROLLBACK ENTITIES.That keeps the next test independent and understandable.
46. Complete guided business rules
| Rule | Mechanism | Timing | Target |
|---|---|---|---|
Default missing Status to N | Determination | on modify | Status |
| Customer Name required | Validation | on save | CustomerName |
| Date cannot be past | Validation | on save | ReservationDate |
| Fee must be > 0 | Validation | on save | Fee |
| Currency required | Validation | on save | CurrencyCode |
47. Why Status is a determination, not a validation
The application knows a reasonable default:
text
N = NewSo the consumer does not need to provide it.
This is derivation/defaulting.
48. Why Customer Name is a validation, not a determination
The backend cannot truthfully invent:
text
CustomerNamefor the consumer.
So missing Customer Name is invalid input.
49. Why past date is a validation
The date already has a value.
The problem is not:
text
missing derived dataThe problem is:
text
business value violates a ruleTherefore validate it.
50. Practice BO reference
The Support Issue practice uses:
text
ZISSUE_###
↓
ZR_ISSUE_###
↓
Managed Behavior
↓
ZBP_R_ISSUE_###Recommended Lesson 13 practice rules:
text
Determination
→ default missing Status to O
Validation
→ Title required
Validation
→ ReportedOn cannot be in the future
Validation
→ EstimatedCost cannot be negative
→ Currency required when EstimatedCost is nonzero51. Practice determination BDEF reference
abap
determination setInitialStatus on modify { create; }Status should become:
text
Owhen initial.
52. Practice validation BDEF references
abap
validation validateTitle on save
{ create; field Title; }
validation validateReportedOn on save
{ create; field ReportedOn; }
validation validateCost on save
{ create; field EstimatedCost, CurrencyCode; }53. Practice messages
Suitable messages include:
text
Title is required
Reported date cannot be in the future
Estimated cost cannot be negative
Currency code is required when cost is providedEach error should target the relevant field where practical.
54. Practice cost rule nuance
A useful rule is:
text
EstimatedCost < 0
→ invalid
EstimatedCost <> 0 AND CurrencyCode is initial
→ invalidThis allows:
text
0 cost + no currencyif your course scenario treats zero as "no estimate yet."
Students should state the business assumption they implement.
55. Certification relevance
Practical RAP work commonly requires students to distinguish:
text
behavior declaration
behavior implementation
interaction phase
save phase
determination
validation
FAILED
REPORTED
field-targeted messagesThe important skill is not remembering one exact sample.
It is tracing:
text
where the rule is declared
when it runs
where it is implemented
how failure reaches the consumer56. Instructor demonstration sequence
Recommended classroom sequence:
text
1. Run Lesson 12 create without new rules
2. Add determination only
3. Create without Status
4. READ before commit → see N
5. Add one customer validation
6. Create blank CustomerName
7. MODIFY response looks acceptable
8. COMMIT → failure
9. Add field-targeted message
10. Add date and fee validations
11. Trigger several messages at once
12. F8 → prove invalid row absentThis incremental approach makes the framework behavior visible.
57. Final instructor validation
Before Lesson 14, students should be able to explain:
text
determination
=
automatic derivation
validation
=
business gate before save
FAILED
=
programmatic failure response
REPORTED
=
message/context response
%ELEMENT
=
message field target
IN LOCAL MODE
=
provider-side BO accessThey should also be able to trigger a validation deliberately and diagnose the resulting EML responses.
58. Lesson 14 boundary
Do not add these yet:
text
actions
instance feature control
dynamic operation enablement
dynamic field controlLesson 13 answers:
Is the state valid, and what data can the BO derive automatically?
Lesson 14 answers:
Which business operations are available, and when should operations/fields be enabled?