The Core of Your Application: Understanding the SAP RAP Behavior Layer
Welcome to the world of the ABAP RESTful Application Programming Model (SAP RAP)! While CDS views give our data a shape and service definitions expose it, the real magic—the business logic, validations, and custom processes—happens in the Behavior Layer. This layer is where your application comes to life, transforming from a simple data display into an intelligent, interactive business tool.
This deep dive will demystify two fundamental concepts of this layer: Behavior Implementations and Behavior Projections. We'll explore what they are, why they are separate, and how they work together using a real-world scenario.
Scenario: A Simple Sales Order Management App
Throughout this blog, we'll use a common business case: managing Sales Orders. Our core business object will have fields like SalesOrderID
, Customer
, OrderDate
, TotalAmount
, and Status
(e.g., 'New', 'Approved', 'Shipped').
Part 1: The Behavior Implementation - The Engine Room
A Behavior Implementation is an ABAP class that contains the actual code for the business logic of your RAP object. It's the 'how' of your application. When you define a behavior (Create, Update, Delete, Actions), you are making a promise. The implementation class is where you fulfill that promise.
This class inherits from cl_abap_behavior_handler
and implements the methods defined in your Behavior Definition (BDEF) file.
The Base Behavior Definition (BDEF)
First, we create a base BDEF for our Sales Order business object. This file defines all possible behaviors the object can have.
managed implementation in class ZBP_I_SALESORDER_U unique;
strict ( 2 );
define behavior for Z_I_SALESORDER_U
persistent table ZSALES_ORDER_DB
lock master
authorization master ( instance )
{
// Standard Operations
create;
update;
delete;
// Field Properties
field ( readonly ) SalesOrderID, LastChangedAt;
field ( mandatory ) Customer, OrderDate;
// Validations
validation validateCustomer on save { create; update; }
validation validateOrderDate on save { create; }
// Determinations
determination setInitialStatus on modify { create; }
// Actions
action approve result [1] $self;
}
This BDEF states that we will implement the logic for C/U/D, two validations, one determination, and one action named 'approve' in the class ZBP_I_SALESORDER_U
.
Implementing the Logic in the ABAP Class
Now, let's look at the implementation class. This is where we write the ABAP code.
Example 1: Implementing a Validation
Let's implement validateOrderDate
to ensure a sales order cannot be created for a past date.
CLASS lcl_salesorder_handler DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS validateOrderDate FOR VALIDATION salesorders~validateOrderDate.
ENDCLASS.
CLASS lcl_salesorder_handler IMPLEMENTATION.
METHOD validateOrderDate.
" Read the data for the entities to be validated
READ ENTITIES OF z_i_salesorder_u IN LOCAL MODE
ENTITY salesorders
ALL FIELDS WITH CORRESPONDING #( keys )
RESULT DATA(lt_sales_orders).
LOOP AT lt_sales_orders INTO DATA(ls_sales_order).
" Check if the order date is in the past
IF ls_sales_order-OrderDate < cl_abap_context_info=>get_system_date( ).
" If invalid, add the key to the FAILED table
APPEND VALUE #( %tky = ls_sales_order-%tky ) TO failed-salesorders.
" Add a corresponding message to the REPORTED table
APPEND VALUE #( %tky = ls_sales_order-%tky,
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'Order Date cannot be in the past.' ),
%element-OrderDate = if_abap_behv=>mk-on )
TO reported-salesorders.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
How it works: The framework passes the keys of the records to be checked. We read their data, perform the check, and if the logic fails, we populate the special tables failed
and reported
. The framework then automatically stops the transaction and displays the error message on the UI.
Example 2: Implementing an Action
Now, let's implement the approve
action. This action will change the order's status from 'New' (N) to 'Approved' (A).
CLASS lcl_salesorder_handler DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS approve FOR BEHAVIOR ACTION salesorders~approve RESULT T_RESULT.
ENDCLASS.
CLASS lcl_salesorder_handler IMPLEMENTATION.
METHOD approve.
" 1. Lock the instances to be modified to prevent concurrent changes
LOCK ENTITIES OF z_i_salesorder_u IN LOCAL MODE
ENTITY salesorders
BY \_salesorder FROM CORRESPONDING #( keys ).
" 2. Read the current data of the selected sales orders
READ ENTITIES OF z_i_salesorder_u IN LOCAL MODE
ENTITY salesorders
FIELDS ( Status ) WITH CORRESPONDING #( keys )
RESULT DATA(lt_sales_orders).
" 3. Perform the modification to change the status
MODIFY ENTITIES OF z_i_salesorder_u IN LOCAL MODE
ENTITY salesorders
UPDATE FIELDS ( Status )
WITH VALUE #( FOR ls_so IN lt_sales_orders WHERE ( Status = 'N' )
( %tky = ls_so-%tky
Status = 'A' ) ). " Change Status from 'N' to 'A'
" 4. Set the result parameter with the keys of the modified entities
result = VALUE #( FOR ls_so IN lt_sales_orders ( %tky = ls_so-%tky %param = ls_so ) ).
ENDMETHOD.
ENDCLASS.
How it works: When the user triggers the 'approve' action on the Fiori UI, this method is called. It uses the EML (Entity Manipulation Language) statement MODIFY ENTITIES
to update the status in the transactional buffer. The framework handles saving this change to the database.
Part 2: The Behavior Projection - The Shop Window
A Behavior Projection is a new BDEF that sits on top of the base BDEF. Its purpose is to create a specific, often restricted, view of the base behavior for a particular consumer (like a Fiori app). It defines what subset of the base behavior is exposed, not how it's implemented.
Think of the implementation as a factory with many tools and capabilities. The projection is like a specific toolkit you assemble from that factory for a particular job.
The Scenario: Different Apps for Different Users
Imagine we need two different Fiori apps:
- Sales Clerk App: Sales clerks can create, update, and delete draft sales orders. They cannot approve them.
- Sales Manager App: Sales managers can see all orders and are the only ones who can execute the 'approve' action. They cannot create new orders.
This is the perfect use case for projections. We use the *same* underlying business logic but expose different capabilities.
Example 3: Projection for the Sales Clerk App
We create a new projection BDEF. Notice the keyword use
. It's not defining new logic; it's simply inheriting or 'using' the logic from the base implementation.
projection;
strict ( 2 );
define behavior for ZC_SALESORDER_CLERK alias SalesOrderClerk
{
// Expose standard operations for the clerk
use create;
use update;
use delete;
// The 'approve' action is NOT exposed here. It will not be visible on the UI.
}
When we build the Fiori app for clerks based on this projection, the 'Approve' button will simply not exist. The core logic for approval is still safe in the base implementation, just not accessible through this service.
Example 4: Projection for the Sales Manager App
For the manager, we create a different projection.
projection;
strict ( 2 );
define behavior for ZC_SALESORDER_MANAGER alias SalesOrderManager
{
// Managers cannot create or delete, only update is allowed (e.g., to add notes)
use update;
// Expose the 'approve' action specifically for the manager
use action approve;
}
The manager's Fiori app, built on this projection, will have the 'Approve' button but will be missing the 'Create' and 'Delete' buttons. We have successfully tailored the UI behavior without writing a single line of duplicate ABAP code.
Conclusion: The Power of Separation
The separation between Behavior Implementation and Behavior Projection is a cornerstone of RAP's robust and reusable architecture.
- Behavior Implementation (The 'How'): It is the single source of truth for all your business object's logic. You write it once, test it thoroughly, and ensure it's correct. It is completely reusable.
- Behavior Projection (The 'What'): It provides a secure and context-specific 'view' of the behavior. It allows you to create multiple different services for different user roles or applications from a single, stable core logic base.
By mastering this separation, you can build applications that are not only powerful and intelligent but also incredibly flexible, maintainable, and secure. You write your core logic once and simply project the necessary pieces for each new requirement, dramatically accelerating development and ensuring consistency across your enterprise applications.
No comments:
Post a Comment