Appearance
Lesson 19 — RAP Business Events: A Standalone Online Order
The business problem
Imagine an online shop. A customer places an order. The shop must save it, but the warehouse also needs to know that a new order exists. One solution would be to put warehouse logic directly inside the shop's Create Order operation. That works at first, but soon the shop would also need to know about emails, stock reservations, delivery planning, and every other process that wants to react.
A RAP Business Event gives us a simpler design: once a new order has been saved, the shop announces “An order was created.” That announcement is called OrderCreated. The warehouse component can listen and react without the Online Order application calling it directly. Today, our warehouse's reaction is deliberately small: write a record to a log table that we can inspect in ADT.
text
Customer creates an Online Order
|
v
Managed RAP saves the order
|
v
OrderCreated event
|
v
Separate event handler
|
v
Warehouse log table gets a rowWhat students should remember: the create operation is a request to do something; the event is an announcement that something happened.
This is a self-contained lesson. It does not modify or reuse Reservation, Support Issue, or Checkpoint 4. It does assume students already recognize tables, managed RAP, behavior definitions, service bindings, and ADT from earlier lessons.
Course format: 3 hours guided plus 3 hours individual practice. Environment: SAP BTP ABAP environment using Eclipse ADT and Fiori Elements preview. No time allocations are attached to individual chapters.
1. What we will prove
A correct result has two different pieces of evidence:
- The Online Order is in its active persistence table.
- A separate event-handler log table contains a matching record.
These prove different things:
text
Order exists in ZONLINESHOP_###
= the RAP app saved an order
Matching row exists in ZORDEVT_###
= the separate event consumer handled the announcementAn event handler runs asynchronously. Its log record may appear shortly after the order is saved; a single immediate refresh is not a guarantee of delivery.
What we are not doing
We are not sending an actual email, provisioning Event Mesh, making a remote subscription, building a second Fiori app, or extending an S/4HANA standard BO. Those are possible follow-on designs but unnecessary for learning the fundamental concept.
2. The six objects that matter
| Object | Role | Name / location |
|---|---|---|
| Package | Keeps this project separate | ZPKG_L19_### |
| Online Order table | Saves real orders | ZONLINESHOP_### |
| Managed RAP application | Lets users create orders | Generated from the table |
| Event payload type | Describes the announcement's extra information | ZA_ORDER_EVT_### |
| Event log table | Saves the listener's result | ZORDEVT_### |
| Local event handler class | Reacts to OrderCreated | ZCL_ORDER_EVT_### |
The generated application contains several additional repository objects: base CDS, base BDEF, Behavior Pool, projection, projection BDEF, service definition, service binding, and (normally) draft persistence. We do not manually build these for today's exercise.
Replace every ### consistently with your assigned three-character group suffix, for example 007. SAP object names have length limits; use the short names shown here.
3. Before starting: Trial prerequisites
You need:
- a working ABAP Cloud project in Eclipse ADT;
- permission to create tables, data definitions, classes, and behavior definitions;
- the Generate ABAP Repository Objects command on a table;
- ability to activate/publish a generated OData V4 UI service and launch its Fiori preview;
- support for RAP Business Events and local event-handler classes in your tenant.
SAP's September 2026 developer tutorial documents this workflow for the SAP BTP ABAP environment and lists free-tier or licensed access as its prerequisite. The availability of every step in an individual shared Trial tenant must be checked by the instructor. The lesson does not require Event Mesh.
Instructor preflight: in the actual class system, first check that a base BDEF accepts event ...; and with additional save with full data, then that ADT accepts a class FOR EVENTS OF the generated root. If the local handler cannot be activated due to environment restrictions, the producer-only fallback in Section 18 still lets students observe the event-raising code; do not claim delivery was demonstrated.
Part A — Build the small Online Order app
4. Create the package
In the ABAP Project Explorer:
text
Right-click your course superpackage
→ New
→ ABAP PackageFor a shared Trial this may be under ZLOCAL or your course's assigned superpackage.
Enter:
text
Package: ZPKG_L19_###
Description: Lesson 19 - Online Order Business EventsUse the appropriate package/transport settings for your system and finish.
Checkpoint: an empty package with your suffix is visible in ADT.
5. Create the Online Order table
Create a Database Table named:
text
ZONLINESHOP_###The name and ordereditem field deliberately resemble SAP's official Online Shop event tutorial, so the generator and event examples are easy to compare with that source.
Replace the table definition with:
abap
@EndUserText.label : 'Online Shop Orders'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zonlineshop_### {
key client : abap.clnt not null;
key order_uuid : sysuuid_x16 not null;
ordereditem : abap.char(25);
local_created_by : abp_creation_user;
local_created_at : abp_creation_tstmpl;
local_last_changed_by : abp_locinst_lastchange_user;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
last_changed_at : abp_lastchange_tstmpl;
}Why these fields?
order_uuiduniquely identifies the order. RAP will generate its value.ordereditemis the product/item the user enters.- Administrative timestamps support managed persistence, locking/ETag behavior and the generator.
Save and activate. Use F8 Data Preview to check the table exists and is initially empty.
Checkpoint: no activation error in ZONLINESHOP_###.
6. Generate the RAP application
Select the new table, then choose:
text
Generate ABAP Repository ObjectsChoose:
text
OData UI ServiceSelect the Lesson 19 package. SAP's current wizard may add prefixes and suffixes automatically; review the generated-object preview carefully before finishing.
Do not assume its generated class, draft table, projection or service-binding names are identical in every ADT version.
Record these actual names before proceeding:
| Generated artifact | Actual name in your system |
|---|---|
| Root CDS entity | ____________________ |
| Root entity alias in base BDEF | ____________________ |
| Base BDEF | ____________________ |
| Behavior Pool | ____________________ |
| Projection CDS | ____________________ |
| Projection BDEF | ____________________ |
| Service Definition | ____________________ |
| OData V4 UI Service Binding | ____________________ |
For the generator configuration used in SAP's tutorial, the root is typically:
text
ZR_ONLINESHOP_###and its BDEF alias may be:
text
ZrOnlineshop###Important: all event code later in this file uses those tutorial-style names. If your generator produces a different root or alias, substitute the names everywhere. Do not change just one occurrence.
7. Make sure the generated app works before adding events
Open the generated base BDEF. Check that the root has:
text
create;
update;
delete;and managed UUID numbering for OrderUuid.
If it is draft-enabled, that is normal. In the generated UI, Save activates the newly created order. We will not teach Draft again in this lesson.
Open the generated OData V4 UI Service Binding, activate and Publish its local service if needed, then launch the Fiori Elements preview.
Create one test order with ordereditem, for example:
text
USB-C ADAPTERSave. Check the active ZONLINESHOP_### table in Data Preview.
Stop here if there is no active row. Business events cannot compensate for a broken generated app. Fix the generator/service/create workflow first. The first order is a baseline only; it predates the event and will not automatically appear in our event log.
Part B — Give the Online Order an announcement
8. Decide what OrderCreated should say
The event name is:
text
OrderCreatedThe technical key (OrderUuid) is always available to the event type. We will also include a short, human-readable piece of information:
text
ItemNameAn example announcement looks like:
text
Event: OrderCreated
Key: <order UUID>
ItemName: USB-C ADAPTERThe event does not ask the warehouse to create another order. It simply announces that an order already exists.
9. Define the payload using a CDS abstract entity
Create a Data Definition named:
text
ZA_ORDER_EVT_###Choose an Abstract Entity template. If ADT inserts unused parameters, remove them.
Paste:
abap
@EndUserText.label: 'Order Created Event Payload'
define abstract entity ZA_Order_Evt_###
{
ItemName : abap.char(25);
}Activate.
An abstract entity is a type, not a table. It describes the information carried by the event; there is no Data Preview for it.
Checkpoint: ZA_ORDER_EVT_### activates and contains only ItemName.
10. Declare the event in the base BDEF
Open the generated base Behavior Definition, not the projection BDEF.
Find the root entity body. Add:
abap
event OrderCreated parameter ZA_Order_Evt_###;Place the declaration alongside the existing create, update, delete declarations inside the root behavior body.
Do not remove generated fields, draft actions, or mapping lines.
The event says: the root BO is capable of publishing an OrderCreated announcement. It does not automatically raise that event every time create is called. We must implement that separately.
11. Tell managed RAP that we need additional save logic
The generated BDEF starts with a managed header similar to:
abap
managed implementation in class zbp_r_onlineshop_### unique;
strict ( 2 );
with draft;Update that global managed implementation header (not the root entity header) to:
abap
managed with additional save with full data
implementation in class zbp_r_onlineshop_### unique;
strict ( 2 );
with draft;Use the actual Behavior Pool name from your generated BDEF. Preserve any other generated statements and options.
Why these additions?
with additional save: let us add code during RAP's save sequence while the framework still saves the order.with full data: include the full order data, includingOrdereditem, in the saver change structure so we do not need a separate read.
This is not fully unmanaged RAP. The managed runtime still owns CRUD and persistence.
Activate the BDEF. If ADT requests a save_modified implementation, that is the expected next step.
12. Raise OrderCreated in save_modified
On with additional save with full data, use Ctrl+1 and select the Quick Fix to create the required save_modified implementation in the Behavior Pool saver class.
Open the Behavior Pool's Local Types and locate the saver class, a subclass of:
abap
cl_abap_behavior_saverIn its generated method save_modified, add the following (adjust the generated alias if necessary):
abap
METHOD save_modified.
IF create-zronlineshop### IS NOT INITIAL.
RAISE ENTITY EVENT ZR_ONLINESHOP_###~OrderCreated
FROM VALUE #(
FOR new_order IN create-zronlineshop###
( %key = new_order-%key
%param-ItemName = new_order-Ordereditem )
).
ENDIF.
ENDMETHOD.Read it from top to bottom:
create-zronlineshop###= the list of newly created root instances in this save sequence.IF ... IS NOT INITIAL= only continue if there is at least one new order.FOR new_order IN ...= one event row per new order.%key= the UUID of the created order.%param-ItemName= the item being ordered.RAISE ENTITY EVENT= publish theOrderCreatedevent for those orders.
If you already have other save_modified logic generated, integrate this block into that method, rather than creating a duplicate method.
Important syntax rule: RAISE ENTITY EVENT uses the global root CDS name (ZR_ONLINESHOP_###), not the entity alias. The create-... component uses the alias. These are deliberately different names.
Checkpoint: activate the Behavior Pool with no syntax errors.
13. Why do we raise the event during Save?
A create request changes RAP's transactional data first. It can still fail during later processing. SAP's RAP guidance recommends raising business events in the save sequence, commonly save_modified for a managed BO with additional save.
Think of it this way:
text
User requests Create
↓
RAP checks/processes the request
↓
RAP save sequence
↓
OrderCreated is raisedThe event represents the order creation reaching the save sequence, not an independent command to start creation.
Because the generated app uses Draft, creating a temporary draft is not the same as activating an order. Use Save/Activate in the Fiori app and confirm the active table row when testing event publication.
Part C — Let a different component react
14. What should the warehouse listener do?
Our listener will be an independent ABAP event handler class. It receives the event and writes a row to:
text
ZORDEVT_###We deliberately store the order UUID as the log's key. That means the demo stores one entry per order. Reprocessing the same event can update that one entry rather than create duplicates. This is a simple teaching-friendly idempotency rule, not an enterprise event-processing framework.
15. Create the event log table
Create a Database Table named ZORDEVT_###:
abap
@EndUserText.label : 'Warehouse Order Event Log'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zordevt_### {
key client : abap.clnt not null;
key order_uuid : sysuuid_x16 not null;
item_name : abap.char(25);
received_at: abp_creation_tstmpl;
}Activate it. Open Data Preview: it should be empty before you make new orders with the handler active.
The log is not part of the Online Order RAP BO. It belongs to our separate listener.
16. Create the event handler class
Create an ABAP class:
text
ZCL_ORDER_EVT_###In its Global Class definition, make it an event handler for the generated root entity:
abap
CLASS zcl_order_evt_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC
FOR EVENTS OF ZR_ONLINESHOP_###.
ENDCLASS.
CLASS zcl_order_evt_### IMPLEMENTATION.
ENDCLASS.Then open Local Types for that class.
Add a local handler class derived from CL_ABAP_BEHAVIOR_EVENT_HANDLER. It must contain only a PRIVATE SECTION.
Using the tutorial-style alias ZrOnlineshop###:
abap
CLASS lcl_order_handler DEFINITION
INHERITING FROM cl_abap_behavior_event_handler.
PRIVATE SECTION.
METHODS on_order_created
FOR ENTITY EVENT
IMPORTING created_orders
FOR ZrOnlineshop###~OrderCreated.
ENDCLASS.
CLASS lcl_order_handler IMPLEMENTATION.
METHOD on_order_created.
"The event handler runs in its own processing context.
"SAP's reference pattern closes the RAP modify phase
"before doing direct SQL writes to a separate log table.
cl_abap_tx=>save( ).
LOOP AT created_orders ASSIGNING FIELD-SYMBOL(<event>).
DATA log_entry TYPE zordevt_###.
CLEAR log_entry.
log_entry-order_uuid = <event>-%key-OrderUuid.
log_entry-item_name = <event>-%param-ItemName.
GET TIME STAMP FIELD log_entry-received_at.
"One log entry per source order.
MODIFY zordevt_### FROM @log_entry.
ENDLOOP.
ENDMETHOD.
ENDCLASS.Why CL_ABAP_TX=>SAVE( )? SAP's current local-handler tutorial calls this before direct database writes, to close the active RAP modify phase in that consumer context. Do not add COMMIT WORK to the handler or to a RAP behavior provider.
Why can this class write directly to a table? ZORDEVT_### is a separate, simple log table, not the RAP-managed Online Order persistence. Do not use direct SQL to change ZONLINESHOP_### and bypass the BO.
If the generated alias is different, substitute it only in the handler signature's FOR <alias>~OrderCreated and in the saver's create-<alias> component. The FOR EVENTS OF declaration still uses the global CDS name.
Checkpoint: activate the class. If the FOR EVENTS OF or local handler method is rejected due to tenant permissions or availability, consult the instructor preflight; don't substitute an ordinary OO event.
17. Test the complete flow
Open two ADT Data Previews:
text
ZONLINESHOP_### active orders
ZORDEVT_### event receiptsIn the generated Fiori Elements preview, create a new order:
textOrdereditem = NOTEBOOKSave/activate it.
Refresh the active order table: a row should appear.
Refresh the event log table: eventually one row with the same
order_uuidanditem_name = NOTEBOOKshould appear.Create another order with
Ordereditem = PENand save it.Confirm that the log contains one receipt per newly created order.
Don't confuse the baseline order from Section 7 with newly created orders: it was saved before the event logic was in place.
Expected results
| Action | Active order? | New OrderCreated receipt? |
|---|---|---|
| Create a new draft but do not activate | No | No |
| Activate a new order | Yes | Yes, asynchronously |
| Change an existing active order's item | Existing order updated | No (create-... is empty) |
| Open or refresh the order list | No new order | No |
For the update test, edit only if the generated app permits it; otherwise use the existing records and explain why an ordinary read/list refresh cannot trigger creation.
18. If the log does not appear
Use this diagnostic sequence instead of changing random code:
| Observation | What to investigate |
|---|---|
| No active Online Order row | Fiori Save/Activate, generated BO activation, validation or authorization |
| Active row exists, saver not entered | with additional save with full data, generator alias, saver activation |
Saver entered, create-... empty | Were you editing rather than creating, or only saving a draft? |
RAISE ENTITY EVENT executed, log stays empty | Handler class activated, FOR EVENTS OF, local event delivery, consumer dump/error, permissions |
| Handler entered, SQL write fails | cl_abap_tx=>save( ), table name/type, table access, key mapping |
| Log key exists but item is blank | Ordereditem field and %param-ItemName mapping |
Producer-only fallback (environment restricted): set a breakpoint in save_modified, create and activate a new order, and show the event row's %key and %param. This verifies the producer path only. It does not prove that an asynchronous consumer received the event. Instructors should mark these separately.
Debugger note: GUI/Fiori requests can run under a different user/context from an ordinary console class. Set the correct external/session breakpoint supported by your ADT environment.
Part D — Understand what you just built
19. The producer does not call the consumer
Notice what is not in the Behavior Pool:
abap
"Do NOT add this kind of direct call:
"zcl_order_evt_###=>write_log( ... ).The Online Order BO knows about OrderCreated, not about ZCL_ORDER_EVT_### or ZORDEVT_###.
This is the reason the architecture is useful: other processes can be added without turning every order action into a collection of unrelated calls.
20. The event has two important pieces
Event identity: OrderCreated tells subscribers what happened.
Event data: %key identifies the source order; %param-ItemName gives the listener useful context.
A CDS abstract entity is used only to describe the extra payload. It is not a database table.
21. The handler is asynchronous
The order save and handler execution are not one synchronous method call. The order creator does not wait for the handler to return an application result.
If the warehouse handler temporarily fails, that does not mean the original Create Order request should be redesigned to call the warehouse directly. Real integrations need operational monitoring, error handling and retry design, which are outside this introductory exercise.
22. Local events vs remote events
What we built:
text
RAP producer
→ local event channel
→ handler in the SAME ABAP systemA remote integration can look like:
text
RAP producer
→ Event Binding / event infrastructure
→ application in ANOTHER systemAn Event Binding is not an OData Service Binding. We did not provision SAP Event Mesh or a remote subscriber. In standard shared Trial setups those services and permissions are not guaranteed.
23. Why this matters for the certification
You should be able to answer and demonstrate:
- What is a business event and why would a company use it?
- What are a producer, event name, payload, and consumer?
- How does the RAP BDEF declare
event ... parameter ...? - Why is the managed BO configured with additional save?
- Where does
save_modifiedbelong? - How does
RAISE ENTITY EVENTpass keys and payload? - What does
FOR EVENTS OFdo in an event-handler class? - Why is the handler separate and asynchronous?
- How do you tell the difference between successful order creation and successful event consumption?
No Event Mesh administration is required to master today's core concept.
24. Teacher-led acceptance checklist
25. References and scope
SAP sources verified for this lesson:
- Create a RAP Business Event in SAP BTP ABAP Environment — revised September 14, 2026. The main model for this standalone Online Shop exercise,
with additional save with full data,RAISE ENTITY EVENT, and the local handler pattern. - SAP Learning — Raising and Handling Business Events — the producer/channel/consumer concept,
TABLE FOR EVENT, save sequence, and local consumption. - SAP Learning — Working with Event Parameters — abstract entities,
%keyand%param. - SAP RAP110 — Exercise 11 — additional-save event implementation and local log-table verification.
Verification boundary: source patterns were checked against these SAP references and cross-checked between the three files. The lesson has not been compiled or executed in your own ABAP Trial tenant. The generated naming and local event-consumption behavior must be confirmed during the instructor preflight.
Next lesson: RAP & CDS Extensibility. This Online Shop can remain a separate example; later lessons do not depend on it.