Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
UliBestfleisch
Advisor
Advisor

When building your application with the SAP Cloud Application Programming Model you can make use of actions and functions. A question that often comes up is when you should use these and what their benefit is. In this blog I want to share my perspective and why I consider them an important element in application development.




May I Introduce: Actions and Functions in CAP


Simply put, actions and functions in CAP are like methods in object-oriented programming. Actions have side-effects on application state, whereas functions are pure and free of side-effects. Like methods, both actions and functions can have parameters and a return value. Functions are required to have a return value, for actions this is optional.


A further differentiation is that of bound vs. unbound. Staying in the OO metaphor bound actions and bound functions are instance methods, unbound actions and unbound functions are static methods.


Let’s look at a variation of the bookshop example to illustrate the ideas and start with an excerpt from the data model, that contains a publication status of a book. Please note that publicationStatus is a read-only element.



entity Book : managed {
key ID : UUID;
title : String;
summary : String;

@readonly
publicationStatus: String enum {
UNPUBLISHED;
PUBLISHED;
DELISTED;
} default 'UNPUBLISHED';
}


On top there are two services: A maintenance service for maintaining the book catalog, and a book selling service that allows consumers to buy books. Let’s first have a look at the book maintenance service:



service BookMaintenanceService {
entity Book as projection on samples.Book actions {
action publish();
action delist();
};

action applyDiscount(percentage: Decimal, validFrom: Date, validTo: Date);

}

There are two bound actions on a book: publish and delist. The action implementation (not shown here) will set the publication status on a book, which is not changeable directly being annotated with @readonly. The third action applyDiscount is an unbound action that will set a discount on all books within a certain timeframe.


The book selling service contains a bound action addToShoppingCart on the book that obviously adds the book to a consumer shopping cart. The bound function calculateDeliveryDate determines the delivery date of a book given the country and address.



service BookSellingService {
entity Book as projection on samples.Book actions {
action addToShoppingCart();
function calculateDeliveryDate(country: Country, address: String) returns Date;
}
}

 

Actions and functions are a concept of OData that has been adopted by CDS and CAP. Consequently, actions and functions are exposed by CAP using the OData protocol. This cheat sheet summarizes nicely how in OData actions and functions can be called. You can also call actions and functions programmatically on CAP services, in both CAP Node.js and CAP Java.


Actions and functions are nicely integrated into Fiori Elements and can be declaratively put on object pages and list reports where they will simply become buttons for users.




Situations for Actions


In my experience you should consider using actions in the following situations:




  1. An important business process step is triggered where you need a controlled way of getting from state A to state B. A typical example is status of an entity that often represents a business process step. Of course, one can model a status element and allow this element to be changed directly. However, you may not want to allow every status transition, or check pre-conditions for a status change. In our books example you might want to check the completeness of book details before publishing it. An action gives you a direct, explicit lever to do so. Dedicated buttons on the user interface (different from create, update, and delete) are an indication that you are triggering such a dedicated business process step.If you’d implement this as part of an update request, you can achieve this as well. However you need to figure out from the changed elements what the actual business intent behind was. This might be doable for single elements, but it gets more difficult if multiple elements are involved.

  2. The state of more than one entity needs to be changed in a consistent, transactional manner. In this case you would use an unbound action.

  3. If there is the need to control authorization for certain state change, this is an indicator to introduce a dedicated action. Actions also give you means to control authorization, as they can be annotated with @requires, like in this example:


@requires: 'Publisher' action publish();

 



If you are working with domain-driven design (DDD) there is a straight-forward translation: each command in your model that is different from create, update and delete is a good candidate to become an own action in your CAP service. With appropriate actions, you can also emit according DDD domain events much easier.


Good real-world candidates for actions are any kind of status transitions, controlled change of active contractual entities like employments, service contracts with customers and any kind of bulk changes that affect many entities.


 


Situations for Functions


Functions are the side-effect-free, read-only siblings of actions. I personally consider them useful in the following situations:




  1. Rather expensive calculations, that need to be triggered explicitly – as opposed to calculated fields that are filled immediately when they are requested.

  2. Calculation that needs data from many entities and that needs to be encapsulated.

  3. If there is the need to control authorization for certain calculations. Exactly like for actions, you also can annotate functions with @required.


Good real-world examples for functions would be tax and price calculations and any form of optimization algorithm.



Overcoming Anemic Services by Using Actions and Functions


I'm convinced that actions and functions are a great way of enriching CDS services - that have the tendency to become CRUD centric - with more business semantics. They help turning an “anemic” service (along the lines of the anemic domain model described by Martin Fowler) into an expressive, semantically rich service.


Hopefully this blog was useful to you and helps you deciding in which situations to use actions and functions in CAP.


What are your experiences with actions and functions and your indications to use them? Let me know in the comments below or on other channels!


For being notified about my future blogs, please subscribe to ulrich.bestfleisch.


You might also want to check out my previous blog on establishing domain services in CAP.



Related Resources


1 Comment