Making your SAP Gateway work smarter not harder : 1) Developing Re-Use Patterns
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:
- Re-Use and Development Patterns
- Encapsulation of Business Logic & Searching for Entities
- eTags for concurrency control
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:
- You need someone who understands how to build OData services in SAP Gateway they should know how to leverage re-use patterns to help accelerate UX development and multi-channel consumption.
- You can build a generic template that seeds a base implementation for your concrete entities.
- You can build a module specific abstract object where you can define re-usable functions on a module level (SD, MM, BP for example )
- This is NOT the only way or the standard SAP way of building OData services, I just really like this pattern as it has served me enormously well on a large scale wall to wall Fiori project and I’m hoping to adopt it has a gold standard on future projects I work on and hope it helps you to accelerate your OData service development.
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.
- I like to start with a standard SAP structure as an Include ( BUT000 in this example )
- I like to include the term “ODATA” in the naming convention to signify the structure is associated with a REST service that implies restricted use.
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:
- Pass through implementation only for each entity CRUD function to an encapsulated class
- NO business logic in DPC, this should all be encapsulated in a concrete class
- Function Imports should have their own API, in other words any implementation in EXECUTE_ACTION should be calling some API or utility type class.
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:
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.
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.
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:
- My Accounts Service
- My Sales Order Service
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.
We’ve mentioned this class called ZCL_ODATA_RT. The purpose of this class is to provide:
- Common search capability based on dynamic SQL. This means when I implement an entity for the first time I can re-use this search pattern that allows me to process complex searches that handles a range of other functions like paging. In a nutshell we have implemented a re-usable search feature once across all our entity implementations that we can tailor for each concrete class.
- I can place framework specific functions such as eTag HASH calculations that will work across all of my entities
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:
- ENRICH_WITH_USER_FILTER (not shown in our basic example below)
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.
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.
Other Episodes in this series – Part 1.2 – Search patterns and service inclusion
Follow On Video 1)
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.
This is my first attempt at putting together one of these videos so please don’t be too harsh on production value 🙂