Skip to content

Lesson 13 — RAP Validations, Determinations, and Messages ​

In Lesson 10, you modeled the Reservation Business Object.

In Lesson 11, you added managed behavior.

In Lesson 12, you consumed that behavior with EML.

You can now:

text
CREATE
READ
UPDATE
DELETE
COMMIT
ROLLBACK

But the Business Object still accepts almost any technically valid Reservation.

For example:

text
blank Customer Name
past Reservation Date
zero Fee
missing Currency

The database table can store technically valid values.

The managed runtime can perform standard CRUD.

But neither one automatically knows your business rules.

That changes today.

You will add:

text
Determination
→ automatically derives data

Validation
→ checks whether data is acceptable

Message
→ explains a business problem to the consumer

The architecture becomes:

text
EML Consumer
    ↓
RAP Business Object
    ├─ Determinations
    ├─ Validations
    └─ Messages
    ↓
Managed Persistence

This is where the Behavior Pool starts containing real application logic.


Lesson information ​

Teacher-led timeAbout 3 hours
Self-practice timeAbout 3 hours
EnvironmentSAP BTP ABAP Trial
PrerequisiteLesson 12
Business ObjectZR_RESERVATION_###
Behavior PoolZBP_R_RESV_###
Runtime test classZCL_RESV_RULES_###
Main outcomeEnforce and explain business rules
New behavior conceptsDeterminations, validations, messages

Replace:

text
###

with your own student/group suffix.


Learning objectives ​

After the teacher-led part, you should be able to:

  • explain the difference between technical validity and business validity;
  • explain what a RAP determination does;
  • explain what a RAP validation does;
  • distinguish on modify from on save;
  • define determinations and validations in a BDEF;
  • generate handler methods with ADT Quick Fix;
  • use READ ENTITIES ... IN LOCAL MODE inside a Behavior Pool;
  • use MODIFY ENTITIES ... IN LOCAL MODE inside a determination;
  • default a field automatically during the interaction phase;
  • mark an invalid instance in FAILED;
  • return an error message in REPORTED;
  • attach a message to a specific field with %element;
  • create messages with new_message_with_text;
  • explain why a message by itself does not reject an instance;
  • observe validation failure through COMMIT ENTITIES;
  • prove that a failed save does not persist invalid data;
  • repair invalid input and rerun the RAP transaction.

1. Technical rules are not enough ​

Your persistence already has technical definitions such as:

text
UUID
CHAR lengths
DATS
CURR
CUKY
NOT NULL
Domain fixed values

Those are important.

But consider:

text
CustomerName = ''
ReservationDate = yesterday
Fee = 0
CurrencyCode = ''

All of those values can still require application-level business decisions.

A database type cannot answer:

Is this Reservation acceptable for our application?

That belongs to business behavior.


2. Three new RAP concepts ​

Today introduces:

text
Determination
Validation
Message

Determination ​

Automatically derives or defaults data when a trigger occurs.

Validation ​

Checks whether an instance satisfies a business rule.

Message ​

Communicates information, warning, or error details back to a consumer.


3. Determination vs validation ​

Use this simple distinction:

text
Determination
→ "I can derive/fix this automatically."

Validation
→ "The consumer must provide acceptable data."

Example:

text
Status missing on create
→ determination can default it to N

But:

text
Customer Name missing
→ validation rejects the save

We do not silently invent a customer name.


4. The Reservation rules for today ​

Add one determination:

text
If a new Reservation has no Status
→ set Status = N

Add three validations:

text
Customer Name
→ must not be initial

Reservation Date
→ must be today or later

Fee / Currency
→ Fee must be greater than zero
→ Currency Code must not be initial

These are deliberately small rules.

The goal is to understand the RAP mechanism.


5. Trigger timing matters ​

RAP behavior can run at different times.

Today the important timings are:

text
on modify
on save

on modify ​

Runs during the interaction phase when its trigger condition is met.

on save ​

Runs as part of the save processing before persistence is finalized.

That difference will be visible in your EML test.


6. Our determination runs on modify ​

We want Status to appear automatically as soon as a create request enters the RAP transaction.

So define:

text
setInitialStatus
→ on modify
→ trigger = create

Expected flow:

text
EML CREATE without Status
        ↓
determination runs
        ↓
Status becomes N
        ↓
EML READ before COMMIT sees N

7. Our validations run on save ​

The validation rules should decide whether the transaction may be saved.

Expected flow:

text
EML CREATE
    ↓
interaction phase
    ↓
COMMIT ENTITIES
    ↓
validations run
    ↓
valid?
  ↙      ↘
yes      no
↓        ↓
save     FAILED + REPORTED

8. Open the existing Behavior Definition ​

Open the Behavior Definition for:

text
ZR_RESERVATION_###

Do not create a second BDEF.

You are extending the behavior contract you built in Lesson 11.


9. Add the determination declaration ​

Inside the behavior body, add:

abap
determination setInitialStatus on modify { create; }

Read it as:

text
Behavior implementation name:
setInitialStatus

Timing:
on modify

Trigger:
create

10. Add the customer validation ​

Add:

abap
validation validateCustomer on save
  { create; field CustomerName; }

This validation is relevant when:

text
an instance is created

or when:

text
CustomerName is changed

11. Add the date validation ​

Add:

abap
validation validateDate on save
  { create; field ReservationDate; }

The validation will reject a date earlier than the current system date.


12. Add the fee validation ​

Add:

abap
validation validateFee on save
  { create; field Fee, CurrencyCode; }

The method will enforce both parts of the monetary business rule.


13. BDEF after adding business rules ​

The behavior should now contain these declarations in addition to the Lesson 11 content:

abap
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; }

Do not remove:

text
create
update
delete
managed UUID numbering
read-only fields
mapping
lock master
ETag

The new rules extend the existing contract.


14. Complete BDEF reference ​

Your complete BDEF should now be equivalent to:

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;
  }
}

15. Generate the handler methods ​

After adding the determination and validations, ADT will know that implementation methods are required.

Place the cursor on each declaration or unresolved handler requirement.

Use:

text
Ctrl+1

Choose the Quick Fix that adds the missing implementation methods to the Behavior Pool.

Open:

text
ZBP_R_RESV_###

The implementation is placed in local handler code inside the Behavior Pool.


16. The handler method signatures ​

Your local handler will need methods equivalent to:

abap
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.

Let ADT generate the signatures when possible.

Do not manually guess framework method signatures if Quick Fix is available.


17. Why behavior implementation uses IN LOCAL MODE ​

Inside the Behavior Pool, the implementation needs to work with its own BO transaction.

Use EML such as:

text
READ ENTITIES ... IN LOCAL MODE
MODIFY ENTITIES ... IN LOCAL MODE

IN LOCAL MODE is available when a Behavior Pool accesses its own BO. It bypasses access, authorization, feature-control, and precheck restrictions where applicable; determinations and save validations still apply.

It is different from the Lesson 12 external consumer class.

Lesson 12 correctly did not use it.


18. Implement setInitialStatus ​

The determination must:

  1. read the affected Reservation instances;
  2. find instances where Status is initial;
  3. update only those instances to N.

Use:

abap
METHOD setInitialStatus.

  READ ENTITIES OF ZR_Reservation_### IN LOCAL MODE
    ENTITY Reservation
      FIELDS ( Status )
      WITH CORRESPONDING #( keys )
    RESULT DATA(reservations).

  DATA updates TYPE TABLE FOR UPDATE ZR_Reservation_###.

  LOOP AT reservations INTO DATA(reservation)
    WHERE Status IS INITIAL.

    APPEND VALUE #(
      %tky   = reservation-%tky
      Status = 'N'
    ) TO updates.

  ENDLOOP.

  IF updates IS NOT INITIAL.

    MODIFY ENTITIES OF ZR_Reservation_### IN LOCAL MODE
      ENTITY Reservation
        UPDATE FIELDS ( Status )
        WITH updates.

  ENDIF.

ENDMETHOD.

Replace ###.


19. Why read before determining? ​

The determination receives:

text
keys

for the affected instances.

The method still needs the current BO data to answer:

text
Is Status initial?

So it reads:

text
Status

from the transactional BO state.


20. Why not overwrite an existing Status? ​

The determination applies only when:

text
Status IS INITIAL

This matters.

A determination should not blindly replace a value the consumer intentionally provided.

Example:

text
Consumer creates Reservation with Status = C

Our determination does not force it back to:

text
N

Business rules can become stricter later if required.


21. What does the determination change? ​

The determination modifies:

text
RAP transactional state

not the database table directly.

It does not use:

abap
UPDATE zresv_###

The implementation talks to its own BO using EML.


22. Implement validateCustomer ​

Use:

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).

    IF reservation-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.

    ENDIF.

  ENDLOOP.

ENDMETHOD.

23. FAILED and REPORTED have different jobs ​

This distinction is essential.

FAILED ​

Marks an instance as failed.

It is what tells RAP:

text
this instance must not successfully complete the requested processing

REPORTED ​

Carries messages and information.

It explains:

text
why

the business rule failed.

A message alone is not a reliable failure signal.


24. Why append the same %tky? ​

The validation is working on one specific BO instance.

The technical transactional key:

text
%tky

connects both:

text
FAILED entry
REPORTED message

to that instance.


25. Create a message with new_message_with_text ​

This helper:

abap
new_message_with_text(
  severity = if_abap_behv_message=>severity-error
  text     = 'Customer name is required'
)

creates a RAP message.

Today we use direct text so the mechanism remains easy to see.

Later production designs may use message classes and translatable texts.


26. Message severity ​

Common RAP message severities include concepts such as:

text
success
information
warning
error

Our validation failures use:

abap
if_abap_behv_message=>severity-error

because the data must not be saved.


27. Attach the message to a field ​

This component:

abap
%element-CustomerName = if_abap_behv=>mk-on

targets the message at:

text
CustomerName

This becomes especially useful when a UI consumes the BO later.

A consumer can understand:

text
the error belongs to this field

rather than receiving only a generic message.


28. Implement validateDate ​

Use:

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).

    IF reservation-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.

    ENDIF.

  ENDLOOP.

ENDMETHOD.

29. Why use the system date helper? ​

Use:

abap
cl_abap_context_info=>get_system_date( )

instead of hard-coding a date.

The validation must remain correct whenever the exercise is run.

This rule applies when creating a Reservation or changing its date. It does not prohibit an existing Reservation from becoming historical, and changing only its notes does not trigger the date check.


30. Implement validateFee ​

Use:

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.

Mark each instance failed once within this validation while reporting a separate message for each invalid field. The Companion's fee validation reference uses the same pattern.


31. Activate the behavior changes ​

Use:

text
Ctrl+F2

for syntax check.

Then activate related objects:

text
Ctrl+Shift+F3

Verify:

text
Behavior Definition active
Behavior Pool active
Root CDS active

Do not begin the runtime test until the BO activates cleanly.


32. Create the runtime test class ​

Create:

text
ZCL_RESV_RULES_###

Implement:

text
IF_OO_ADT_CLASSRUN

This class is an external BO consumer.

Therefore it must use normal EML:

text
MODIFY ENTITIES
READ ENTITIES
COMMIT ENTITIES
ROLLBACK ENTITIES

It must not use:

text
IN LOCAL MODE

33. Use reserved Lesson 13 numbers ​

Use:

text
94000001
94000002

Meaning:

text
94000001
→ valid Reservation

94000002
→ deliberately invalid Reservation

Do not reuse the Lesson 12 numbers.


34. Make the exercise repeatable ​

Before the tests, remove old Lesson 13 rows safely if they exist.

Recommended pattern:

text
read root CDS to discover UUIDs
        ↓
delete those UUIDs through EML
        ↓
COMMIT ENTITIES

Never run an unrestricted table delete.

Use the Companion's cleanup reference, including its failure guards, before starting either test.


35. Test 1 — create a valid Reservation without Status ​

The first create deliberately omits:

text
Status

Use values equivalent to:

text
ReservationNo   = 94000001
CustomerName    = RAP Rules Student
CustomerEmail   = rules@example.com
WorkshopCode    = RAP201
ReservationDate = today
Fee             = 200.00
CurrencyCode    = EUR
Notes           = Determination test

Do not provide:

text
Status
Uuid
administrative fields

36. Execute the valid CREATE ​

Use:

abap
MODIFY ENTITIES OF ZR_Reservation_###
  ENTITY Reservation
    CREATE FIELDS (
      ReservationNo
      CustomerName
      CustomerEmail
      WorkshopCode
      ReservationDate
      Fee
      CurrencyCode
      Notes )
    WITH VALUE #(
      ( %cid            = 'VALID'
        ReservationNo   = '94000001'
        CustomerName    = 'RAP Rules Student'
        CustomerEmail   = 'rules@example.com'
        WorkshopCode    = 'RAP201'
        ReservationDate = cl_abap_context_info=>get_system_date( )
        Fee              = '200.00'
        CurrencyCode     = 'EUR'
        Notes            = 'Determination test' ) )
  MAPPED DATA(mapped_valid)
  FAILED DATA(failed_valid_modify)
  REPORTED DATA(reported_valid_modify).

37. What should happen during MODIFY? ​

The determination is:

text
on modify

So during this interaction phase:

text
Status initial
    ↓
setInitialStatus
    ↓
Status = N

The on-save validations have not yet reached their save trigger.


38. Read the valid Reservation before COMMIT ​

Retrieve the mapped UUID and read the instance.

Check the interaction response before accessing its mapping:

abap
IF failed_valid_modify IS NOT INITIAL
   OR NOT line_exists( mapped_valid-reservation[ %cid = 'VALID' ] ).
  out->write( failed_valid_modify ).
  out->write( reported_valid_modify ).
  ROLLBACK ENTITIES.
  RETURN.
ENDIF.

DATA(uuid_valid) =
  mapped_valid-reservation[ %cid = 'VALID' ]-Uuid.

Then:

abap
READ ENTITIES OF ZR_Reservation_###
  ENTITY Reservation
    FIELDS (
      Uuid
      ReservationNo
      CustomerName
      Status
      Fee
      CurrencyCode )
    WITH VALUE #( ( Uuid = uuid_valid ) )
  RESULT DATA(valid_before_commit)
  FAILED DATA(failed_valid_read).

Output the result.

Expected:

text
ReservationNo = 94000001
Status        = N

That is your first direct proof of a determination.


39. Commit the valid Reservation ​

Use:

abap
COMMIT ENTITIES
  RESPONSE OF ZR_Reservation_###
    FAILED DATA(failed_valid_commit)
    REPORTED DATA(reported_valid_commit).
DATA(valid_commit_subrc) = sy-subrc.

out->write( failed_valid_commit ).
out->write( reported_valid_commit ).

IF valid_commit_subrc <> 0 OR failed_valid_commit IS NOT INITIAL.
  ROLLBACK ENTITIES.
  RETURN.
ENDIF.

Expected:

text
no validation failure
save succeeds

Check:

text
FAILED
REPORTED
sy-subrc

40. Test 2 — deliberately invalid create ​

Prepare:

abap
DATA yesterday TYPE d.
yesterday = cl_abap_context_info=>get_system_date( ) - 1.

Create Reservation 94000002 with:

text
CustomerName    = blank
ReservationDate = yesterday
Fee             = 0
CurrencyCode    = blank
Status          = omitted

The determination can still default Status.

But the validations must reject the save.


41. Invalid CREATE request ​

Use an EML create equivalent to:

abap
MODIFY ENTITIES OF ZR_Reservation_###
  ENTITY Reservation
    CREATE FIELDS (
      ReservationNo
      CustomerName
      CustomerEmail
      WorkshopCode
      ReservationDate
      Fee
      CurrencyCode
      Notes )
    WITH VALUE #(
      ( %cid            = 'INVALID'
        ReservationNo   = '94000002'
        CustomerName    = ''
        CustomerEmail   = 'invalid@example.com'
        WorkshopCode    = 'RAP201'
        ReservationDate = yesterday
        Fee              = '0.00'
        CurrencyCode     = ''
        Notes            = 'Validation test' ) )
  MAPPED DATA(mapped_invalid)
  FAILED DATA(failed_invalid_modify)
  REPORTED DATA(reported_invalid_modify).

42. The important observation before COMMIT ​

Inspect:

text
failed_invalid_modify
reported_invalid_modify

The save validations are declared:

text
on save

So the interaction-phase create can reach the transactional buffer before the save checks reject it.

This is the key Lesson 13 observation:

text
MODIFY accepted into RAP transaction
does not automatically mean
transaction can be saved

43. Commit the invalid Reservation ​

Now execute:

abap
COMMIT ENTITIES
  RESPONSE OF ZR_Reservation_###
    FAILED DATA(failed_invalid_commit)
    REPORTED DATA(reported_invalid_commit).
DATA(invalid_commit_subrc) = sy-subrc.

This is where the validation rules become visible to the external consumer.

Inspect both response structures.

Expect invalid_commit_subrc = 4 and a nonempty failed_invalid_commit. If the save unexpectedly succeeds, repair the validation declarations and implementations and remove the persisted test row through the EML cleanup before rerunning. A later rollback cannot undo an already successful commit.


44. Expected failed instance ​

The save response should identify the invalid Reservation in:

text
FAILED

That is the programmatic signal that the instance failed validation.

Do not detect failure only by searching message text.


45. Expected messages ​

REPORTED should contain messages equivalent to:

text
Customer name is required
Reservation date cannot be in the past
Fee must be greater than zero
Currency code is required

Validation execution order is not fixed, so do not depend on message order. To print readable message text from %msg, use the Companion's console example.

The important content is:

text
instance
severity
message
target element

46. Messages explain; FAILED rejects ​

Memorize the relationship, not the syntax:

text
FAILED
→ this instance failed

REPORTED
→ here is useful information about it

SAP RAP does not treat a message by itself as a reliable signal that processing must terminate.

Your implementation must mark the failed instance.


47. Roll back after the failed save test ​

After inspecting the failed save response, reset the RAP transaction:

abap
ROLLBACK ENTITIES.

This removes the invalid buffered test state.

Do not leave the consumer in an unresolved transaction.


48. Verify invalid data was not persisted ​

Open:

text
ZR_RESERVATION_###

with:

text
F8

Filter or locate:

text
94000002

Expected:

text
not persisted

But:

text
94000001

should exist with:

text
Status = N

49. Test 3 — invalid UPDATE ​

Now prove validations also protect an existing instance.

Take the surviving:

text
94000001

and attempt to update:

text
ReservationDate = yesterday

Use EML UPDATE FIELDS.

The interaction-phase update enters the transaction.

The save validation should reject it at commit.


50. Roll back the invalid UPDATE ​

After the validation failure:

abap
ROLLBACK ENTITIES.

Read 94000001 again.

Expected:

text
ReservationDate
=
original valid date

The invalid update was not persisted.


51. Determination and validation live in the provider ​

Notice where the rules are implemented:

text
ZBP_R_RESV_###

not:

text
ZCL_RESV_RULES_###

The consumer does not decide:

text
default Status = N
date must not be past
Fee must be positive

The Business Object owns those rules.

That means future consumers can reuse them.


52. Why this matters for future OData consumers ​

Later you will expose the BO through an OData service.

A Fiori application should not need to reimplement:

text
Reservation Date validation
Fee validation
default Status

The Business Object already owns the rules.

Architecture:

text
ABAP EML consumer
        ↘
         RAP BO rules
        ↗
OData / Fiori consumer

Different consumers can share the same business behavior.


53. Determinations are not validations ​

Do not write a determination that silently changes:

text
Fee = -10

into:

text
Fee = 10

unless that is genuinely the business rule.

Automatic correction can hide bad input.

A determination is appropriate when data is legitimately derivable.

A validation is appropriate when the consumer must correct invalid input.


54. Trigger design matters ​

A validation should not run for every possible change if it only depends on one field.

For example:

abap
validation validateCustomer on save
  { create; field CustomerName; }

expresses its relevant triggers.

This improves clarity and avoids unnecessary execution.


55. Do not duplicate rules in the consumer ​

Wrong architecture:

text
ZCL_RESV_RULES_###
checks CustomerName
        ↓
then calls BO

Correct architecture:

text
Consumer sends request
        ↓
RAP BO validates CustomerName

A consumer may perform user-experience checks later.

But the authoritative backend rule belongs in the BO.


56. Messages today are simple text messages ​

Today we use:

abap
new_message_with_text( )

because it makes the RAP message mechanism easy to see.

For larger applications, you normally want:

text
translatable messages
message classes
stable message identities

That is a refinement, not the core concept of this lesson.


57. State vs transition messages — awareness only ​

RAP supports different message lifecycles.

Today our simple validation examples focus on:

text
failed instance
reported error message
field target

We are not yet building:

text
draft UI message persistence
state-area lifecycle
advanced service message handling

Those concepts are easier to understand once the service and draft layers exist.


58. Common mistake — message but no failed entry ​

This is incomplete:

abap
APPEND VALUE #(
  %tky = reservation-%tky
  %msg = ...
) TO reported-reservation.

if your intention is:

text
reject the invalid instance

Also add it to:

text
failed-reservation

59. Common mistake — direct SQL in the Behavior Pool ​

Do not implement the determination with:

abap
UPDATE zresv_###

Use:

text
MODIFY ENTITIES ... IN LOCAL MODE

The provider should work with RAP transactional state.


60. Common mistake — IN LOCAL MODE in the external consumer ​

Provider implementation:

text
IN LOCAL MODE

External consumer:

text
normal EML

Do not copy provider-side EML syntax into ZCL_RESV_RULES_###.


61. Common mistake — validating only in the UI ​

A UI-only validation can be bypassed by:

text
another UI
EML
API client
integration

The BO must protect its own business invariants.


62. Architecture at the end of Lesson 13 ​

You now have:

text
Consumer
   ↓
EML / future OData
   ↓
ZR_RESERVATION_###
   ↓
Managed Behavior
   ├─ standard CRUD
   ├─ determination
   ├─ validations
   └─ messages
   ↓
ZRESV_###

This is a much more realistic transactional backend.


63. Knowledge check ​

1. What is the difference between technical validity and business validity? ​

2. What does a determination do? ​

3. What does a validation do? ​

4. Why does setInitialStatus run on modify? ​

5. Why do our validations run on save? ​

6. What is the difference between FAILED and REPORTED? ​

7. Why is a message alone not enough to reject an instance? ​

8. What does %element-CustomerName communicate? ​

9. Why does provider implementation use IN LOCAL MODE? ​

10. Why should an external consumer not use IN LOCAL MODE? ​

11. Why does the determination read the affected instance before updating it? ​

12. Why should it not overwrite an explicitly supplied Status? ​

13. Where should authoritative Reservation rules live? ​

14. What should happen to invalid data when COMMIT ENTITIES runs? ​

15. Why is the same backend validation useful for both EML and future OData consumers? ​


64. Teacher-led completion checklist ​


65. End-of-day mental model ​

Before leaving Lesson 13, explain:

text
Determination
→ derive/default valid business data

Validation
→ protect a business rule

FAILED
→ reject the affected instance

REPORTED
→ explain what happened

%element
→ point the message at a field

And draw:

text
MODIFY ENTITIES
      ↓
on-modify determination
      ↓
transactional buffer
      ↓
COMMIT ENTITIES
      ↓
on-save validations
   ↙             ↘
valid            invalid
 ↓               ↓
save       FAILED + REPORTED

That model prepares you for Lesson 14:

text
Actions
Field Control
Dynamic Feature Control