Introduction
As SAP customers, partners and consultants embark on their journey to designing and building your own Fiori style applications a major part of this journey will be building your own RESTful API’s using SAP Gateway ( OData services ).
I'm going to break down my learning and insights into SAP Gateway development into a series of blogs where I would like to cover the following topics:
Acknowledgement and Use Cases
The development patterns covered in this series are not originally mine. If you look at the My Accounts Fiori application delivered by SAP in CRM (UICRM001), this pattern was written to support the OData service "CRM_BUPA_ODATA".
Our colleagues who have put together this pattern probably don't realise how much of a gem it has been for me to rapidly produce re-usable, nicely encapsulated entity implementations in SAP Gateway, so hats off to the folk who put a lot of thought and effort behind this.
You can consider this pattern when creating your own OData services in the customer namespace. For modifying SAP delivered OData services you should always follow the SAP recommended approach and enhancement documentation.
Key Learnings from this article:
Putting the Pattern Together
First let’s cover our typical OData service from a high level and use a simple entity relationship model as an example ( Account or Business Partner with an Address association 😞
SEGW - Design Time.
Here we define our entities, attributes of those entities and association between them ( navigation path ).
In our example we want to look at two entities, Account and Address. You can generate the entity with various methods but I like to generate new entities from a custom ABAP structure.
DPC, DPC_EXT - Data Provider Classes
These are the classes that are generate from activating the OData service and where you have been told to implement you entity logic for CRUD functions.
I have a couple of rules around DPC_EXT:
Entity Implementation and Class Hierarchy
Most implementations I have seen have a loose encapsulation concept, in other words implementation in CRUD methods in the DPC_EXT class contain some business logic and other business logic is contained somewhere else.
I don’t like this as it becomes difficult to maintain and causes regression problems when new developers take over and add new features.
Instead we can use a nice class hierarchy that helps us encapsulate different business logic in layers.
This is the current hierarchy I like:
In context with our “Account” entity, this is what we would end up with:
ZCL_ODATA_RT | This class provides a base line search for GET_ENTITYSET that handles skip / count / paging. The base line search is based on a dyanmic SQL approach. it also allows us to do eTag calculation across all out entities. Apart from the Search pattern in this class, I only use it for Gateway framework type functions. |
ZCL_ODATA_BP | I’ve called this a modular layer, we can encapsulate module specific functions. In our use case for Accounts (Business Partner) we may have common functions such as add leading zeros to a BP number( internal / external display ) or functions that format the Business Partners full name that we can write once and call across any concrete class that belongs to our module. |
ZC_ODATA_ACCOUNT | This is the concrete class, here you implement the CRUD functions GET_ENTITYSET, GET_ENTITY, UPDATE_ENTITY etc which are called from the corresponding methods in the actual DPC_EXT class of your service. All entity specific business logic is maintained here. |
Re-use of Entity Objects
Start thinking about the services you need to build. Are there re-usable entities across these services? Let’s take a look at a one example.
Below is a diagram that shows two OData services:
These two services share common entities:
In some implementations, I have seen the same code duplicated across different DPC_EXT classes for the same entity which doesn't lend itself to good re-use patterns although there certainly may be a use case where this is necessary.
Here is what I mean about acceleration, once I have the Account and Address entity implementation up and running, tested and stable I can re-use these entities across new services I'm putting together.
The initial build is obviously the longest but scaffolding up a new service with the same entities then becomes considerably faster.
Data Provider Class ( DPC_EXT )
To facilitate this pattern, we need to make some changes in our DPC_EXT. This allows us to access instantiated concrete classes at runtime.
First we need an attribute on our DPC_EXT class that holds the instances of each entity implementation:
The structure of the table is:
Now to instantiate our entity implementations, during the constructor call in the DPC_EXT class we instantiate each entity and store the instance, class name and entity name:
Now we need to call our entity concrete class implementation. Here we assume the service has been generated and you have re-defined the relevant method.
ZCL_ODATA_RT
We've mentioned this class called ZCL_ODATA_RT. The purpose of this class is to provide:
Overview of Entity Search
The first thing we probably want to do in the Fiori application is search for an entity. I usually start by building out the generic search pattern in ZCL_ODATA_RT that follows this basic flow:
NB: I don't have to use this when setting up a new concrete class for the first time, I can redefine the GET_ENTITYSET method in the ZCL_ODATA_ACCOUNT and write other code to do the search for us.
Here is an overview of the logic of the GET_ENTITYSET method in ZCL_ODATA_RT:
1) Process paging, max hits and skip tokens. This allows our dynamic SQL to retrieve our results based on certain filter parameters and then we have a generic paging pattern taken care for us. Think about the Master Object list in a Fiori app where you type in some search criteria and the OData call includes a "substring" filter.
2) Generate a dynamic SQL statement. This method contains the select, inner join, where statements.
3) Apply additional filters. Here I can read my entity filters passed by $filter and add additional criteria to our dynamic SQL
4) Execute the dynamic SQL statement and page the results
5) Enrich the data and return the result. This is where where you want to populate additional attributes of the entity that could not be retrieved during the dynamic SQL statement, thing such as texts or formatted names for example.
Implementing the search in our concrete class
I now need to implement this in our concrete class. When I have created our ZCL_ODATA_ACCOUNT class, I can then redefine the appropriate methods where I need to put my logic, this includes:
Generate Select
In our generate select method, all we are doing is forming up the dynamic SQL, what attributes we want to select into our entity set. We can also include joins here if we want to search across multiple tables.
Enrich Entity Set Data
Before we pass the selected entity set records back to the odata framework we want to format and add additional things into the result. Stuff like full name or texts. In our example we've just formatted the full name of the business partner into the field FULL_NAME.
We now have a concrete class implementation with an entity set search routine.
Implementing Other CRUD methods
We can then continue to implement the other CRUD functions by redefining the appropriate methods in our concrete class (ZCL_ODATA_ACCOUNT) and data provider class.
Let's do an GET_ENTITY example. Here is the re-definition in the DPC_EXT class:
And let's put some logic in the ZCL_ODATA_ACCOUNT method GET_ENTITY:
I won't go through POST, DELETE, UPDATE this should provide enough foundation for how the pattern works and encapsulates our business logic nicely into a concrete object.
Summary
In this part of the series we've demonstrated that it is possible to build a re-use pattern that encapsulates our entity implementations cleanly and also include a powerful search feature that we can consume on demand if required without having to re-write the same functions more than once.
I hope you have found this blog useful and see you next time where when we cover some performance considerations for your OData services and e-tag handling for data concurrency control.
Stay tuned for updates as I prepare a set of video walkthroughs to augment the content outlined in part 1 of this series and to show more complex search patterns.
Thanks for stopping by.
Make your SAP Gateway Work Smarter not Harder – Part 1.2 ) Search Patterns and Service inclusion
Here is the ink to the first follow on video where I walk through some of the basics of the development pattern and add a new entity to the service.
Make Your SAP Gateway Work Smarter not Harder - Part 1 - YouTube
This is my first attempt at putting together one of these videos so please don't be too harsh on production value :smile:
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
13 | |
11 | |
10 | |
9 | |
9 | |
7 | |
6 | |
5 | |
5 | |
5 |