Skip to Content
Technical Articles
Author's profile photo Bilen Cekic

Simplify your ODATA service with a lean and flexible architecture

Hello everyone,
I am going to write about my experience regarding a simplified way of implementing OData service and re-using the same OData service for all of my UI5 projects (around 8+ big projects) for last 5 years.

First of all, if you are implementing standard FIORI applications, smart controls or very simple honkie-ponkie app which is targeting a standard SAPGUI user, this article might not be for you.

Back in 2015, when i was learning nodeJS and React, i noticed sending data from backend and consuming on frontend layer was super easy. In 2016, i wrote a HANA Procedure backup tool with nodeJS and React (here is my article) and really loved data communication methodologies there.

Once i started learning SAPUI5, i noticed something was not right on the entire data flow. Selecting fields in SEGW, updating service again, updating structure due to new requirements, function imports, annotations, deep inserts, entire SEGW was really burden for the project. There was no way i could implement something in a good mood. After a simple REST API data flow, OData configuration, settings, capturing nested data was unnecessarily complicated. One content become dynamic, things were getting uglier.

I decided to simplify things in a more generic way which would change a lot of things in my career and in the company.

Lean ODATA

When i mean lean OData, i am talking about a generic OData service which you can use for all your projects. Below you can see only GET and POST methods are used. We just need two basic methods. I will explain how filters are flowing for the endpoints below.

Our ODATA should contain fixed columns but it has to cater all requirements easily without any change. I created below OData service with 4 fields only based on a structure. Once request comes from UI5, a custom BADI layer is triggered based on parameters.

TMPL_ID : (template id) This column is giving us the information of what is this service all about. It can be related to a specific report module, a workflow, calculation, file generation or anything. This is referring to BADI filter which also contains all the methods related to this module. A sample would be FINANCIAL_REPORT

Generate report, download generated report(XLSX,PDF or live report), context selections before running the pivot, previously generated reports all belong to same MODULE_ID. All related methods exists in same BADI.

DETAIL: This refers exactly what this service will do. This could be a calculation needs to be run, an approval process, template generation or chart generation. It is the method name inside the BADI.Example would be GET_REPORT, RUN_CONVERSION, GET_SALES_CHART, GET_SELECTIONS etc.

CONTEXT: This is key column where i send parameters/filters for POST request in the BODY.  It is also used to retrieve data back as JSON format for both GET/POST requests. JSON content flows here./

MSG: During Get/Post operation, if any error is raised, it is sent here. Having a standard way of error handling makes it easier on frontend. I could send same error in CONTEXT but MSG field makes it easier to handle.

 

For GET method;

        GET BADI LR_BADI
         FILTERS
        TMPL_ID = LV_TMPL_ID.
        
        CALL BADI LR_BADI->GET
          EXPORTING
            IT_FILTER = IT_FILTER  "Filter coming from URL
            IS_DATA   = IS_DATA    "contains info related to query.
          IMPORTING
            ET_MSG    = LT_MSG
            ER_DATA   = LR_DATA.
        
* RESPONSE

        ER_ENTITY-TMPL_ID = IS_DATA-TMPL_ID.
        ER_ENTITY-ACTION = IS_DATA-ACTION.
        ER_ENTITY-DETAIL = IS_DATA-DETAIL.
        ER_ENTITY-CONTEXT = /ui2/cl_json=>serialize( data = LR_DATA ).  "dynamic json content
        ER_ENTITY-MSG = /ui2/cl_json=>serialize( data = LT_MSG ).   "any message if raised

 

for POST method:

       CALL BADI LR_BADI->SAVE
          EXPORTING
            IS_DATA = IS_DATA "contains all info related to query
          IMPORTING
            ET_MSG  = LT_MSG
            ER_DATA = LR_DATA.
* RESPONSE
        ER_ENTITY-CONTEXT = /ui2/cl_json=>serialize( data = LR_DATA ).
        ER_ENTITY-MSG = /ui2/cl_json=>serialize( data = LT_MSG ).

Design Pattern:

Here we are heavily using strategy design pattern which you can easily see in majority of the SAP products. Just to give you an example, entire BPC architecture (standard mode) heavily uses this approach.

SAP BADI layer is just wrapping the pattern. Below is the class diagram of a strategy design pattern.

Interface is our BADI interface and concrete classes are BADI implementations.

Below screenshot is from my current system. There are 2 main methods one is for GET and one is for SAVE. I could combine in to 1 interface method but just wanted a seperation between HTTP method types.

Each BADI is implemented via FILTER which defines what class to trigger.

 

I have around 114 BADI implemented right now from same interface. It covers around 5 big projects and since 2016 we never visited SEGW t-code again. This gave us flexibility and speed that we need for project implementation. Rather than spending time on SEGW modelling, we spend it for better architecture, innovative functionalities, and better UI/UX for our end users. Standard FIORI screens was very boring honestly.

 

 

Welcoming any change in any phase of the project:

Biggest advantage of above architecture was we could hire any ABAP developer who can pick the logic easily and continue development in a couple of days. They don’t need to know about SEGW configurations, deep inserts, annotations and any other checkbox functionality there. It is all about how HTTP methods, JSON data and performance oriented clean ABAP.

This gave us lightning speed. Once we implemented first project successfully, majority of user’s non-sap implementation ideas turned towards SAP. We were doing it very fast and very robust. New projects keep coming and we had to reject majority of them.

We were accepting any late request, new ideas in any phase of the project based on our SCRUM planning. Everything was smooth and sucessfull. Win-win for both sides.

 

Switch between GET and POST method:

There are several cases where FILTER might get too long (especially on analytic report) where you might hit URL length limit. For this cases, switching from a GET method, to POST method is very easy. It is all about changing parameters from URL to Request BODY.

Again SEGW is not impacted.

 

FOR GET:

ls_param-param1 =  VALUE #( dt-it_filter[ property = `PARAM1` ]-select_options[ 1 ]-low  OPTIONAL ).
ls_param-param2 =  VALUE #( dt-it_filter[ property = `PARAM2` ]-select_options[ 1 ]-low  OPTIONAL ).

For POST:

  DATA: BEGIN OF ls_param,
            param1 TYPE t_member,
            param2 TYPE tt_context,
          END OF ls_param,
/ui2/cl_json=>deserialize( EXPORTING json = <params_from_odata_body>  CHANGING data = ls_param )

 

Distribution of the load:

There are several cases we are exporting multiple table and chart data at the same time. This architecture is giving you an immediate split option whenever there is a performance issue for sequential process. For example 2 chart can be triggered parallel in the screen (or all at once). This is all depends on the scenario.

Below 4 methods are triggered at the same time once user open the homepage. I could be combining them to 1 method with 4 internal table or split into 4 methods and every method exports their internal table. It is all about decision for the design principle and performance for heavy services.

 

Logging everything:

Below API Handler layer is the place where it find the BADI and triggers the specific class/methods based on BADI filters. This gives us advantage of logging all the body/parameters/url of the service, who triggered, how many seconds it took, was there error or anything related to it is stored.

We can get some good statistical reports end the year for users and prepare a usage report. This includes which page used most, average seconds for the end points, errors, failed requests and some other details are reported.

 

logger->timer_Start( ).

<BADI is triggered here>

logger->timer_end( ).

* log service details. User, time, etc.

logger->log_service_data( ).

 

Sample call using async/await

As you can see below, we are just setting our TMPL_ID to define what BADI to trigger and DETAIL to define what method to trigger. URL filter is send for announcement ID.

// SERVICE.JS	
	getAnnouncementDetail: function(iv_annoid) {
			var sPath = "/DATASet(TMPL_ID='APP1_ANNO',ACTION='GET',DETAIL='GET_ANNO_DETAIL')";
			var aFilterValues = ["ANNO_ID=" + iv_annoid];
			var params = {
				sModelName: LOCAL_MODELS.ANNOUNCEMENT_EDIT,
				aFilterValues: aFilterValues
			};

			return this.getODataAPI().read(sPath, params);
		},


// controller.js
_GetAnnouncementDetail: async function (iv_annoid) {
            sap.ui.core.BusyIndicator.show(0);
            var oResponse = await this.Service.getAnnouncementDetail(iv_annoid);
// some bindings here
            sap.ui.core.BusyIndicator.hide();
        },

Conclusion:

I implemented this architecture on my first SAPUI5 project and later on improved a bit and started using on my latest client as well. Since 2016 service was not touched at all but new BADI implementations keep coming by using same engine. We saw biggest advantage on 3 different areas;

  • Development speed
  • Flexibility
  • Third party library integration

Similar to above, i also implemented a file upload solution which works in a similar way but i can write about it later.

Since 2018, i was planning to write regarding this approach and finally i am able to write it. I know many of developers might disagree but we loved it and still using without any issue.

Below is a sample screenshot from latest project.I had to blur sorry.

  • Announcement section(where user can upload and crop images and add HTML content)
  • Calendar control where they can add events from admin screen
  • Some chart data related to activities.
  • Left menu (also coming as JSON from backend based on auth)

 

 

UPDATE :
Just a small update, recently Palantir which is a big data company also published an article regarding REST API architecture. They are basically telling;

First, many clients don't need to differentiate between Create, Update, and Delete — they want an omnibus "set" operation.

which is similar to this approach. Yes it is about REST and this article about ODATA, but with above approach, it is very similar what Palantir is telling in their article. You can find article below.

https://blog.palantir.com/rethinking-crud-for-rest-api-designs-a2a8287dc2af

 

Assigned tags

      24 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ravish Garg
      Ravish Garg

      How do you handle your app catalogs and app security in this design pattern, since the same oData service is being used for all apps?

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      I have one role to give same ODATA service access to all users but each user have a separate role assigned for tile access. This means they can only see the apps which is assigned to themself.

      Lets say somehow they copy-paste one of the service URL from a different app to their session, well BADI won't be triggered since they don't have access to that application (also the BADI implemented for that application), you will get error and nothing will be displayed.

       

      Author's profile photo Duncan Barre
      Duncan Barre

      Great post, thank you!  You were saying:

      Standard FIORI screens was very boring honestly.

      and:

      if you are implementing standard FIORI applications, smart controls or very simple honkie-ponkie app which is targeting a standard SAPGUI user, this article might not be for you.

      I'm using lots of smarttables/smartfilters etc so i'm curious, what alternatives do you use?

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Hi Duncan!

      Thanks for your reply.
      Lets start with smart filters,
      here is the screenshot from help;Properties of the expanded filter bar
      It has so many input controls on the screen. Exactly looks like how tcodes in SAP GUI are designed. Look what usage says;

       

      Usage
      Use the smart filter bar if:
      An OData service is available.
      You want to develop quickly and efficiently.

      In short, if you are rushing on filter, use this :D.

      From UI persective it is ugly, UX perspective choosing items one by one, visibility issues on multi selections makes the screen messier. Our users didn't use SAP GUI before, they are planning users where they used several non-sap application before and their expectation also high. I don't see functional issue on smart filter, but it is designed in a generic way that as if everyone is going to retrieve open items from FI or sales orders from SD and filter by company code.

      We designed a custom filter control where they can select their high level filter before they retrieve anything from backend. Entire filter is carried to the other screens automatically. It is more specific to the business process rather than a generic filter control. We also prefer creating separate views with pre-defined filters rather than asking user to filter.

      For smarttable fiori guideline suggest to use sap.ui.table or responsive table if requirement is complex.

      We extended sap.ui.table control and added copy-paste functionality where you can copy from excel and paste to ui.table. Also added import/export from excel (which is from backend where you can export with formulas, multiple sheets and data validations), dynamic column totals based on filtered rows etc. There are great third party libraries like handsontable, aggrid but due to company policy we need to stick ui.table. I am not super happy with ui.table but no alternate for virtual scrolling and rendering the huge rows.

       

       

       

       

      Author's profile photo AjeethKumar R
      AjeethKumar R

      Excellent Blog, Bilen. Thanks for posting this.

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Thank you!

      Author's profile photo Syambabu Allu
      Syambabu Allu

      Great Blog,Thanks for sharing with step-by-step.

      Thank you,

      Syam

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Thank you!

      Author's profile photo Jaco Louw
      Jaco Louw

      Excellent Blog Balin!

      I am fascinated with your approached.

      Will this also work where we have several external systems that needs to access very different odata services?

      Thanks

      Jaco

       

       

       

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Hi Jaco

      Yes, biggest advantage of this architecture is you can extract data from anywhere without thinking the ODATA structures. It could be reading via REST API from a non-sap envrionment, RFC to a different SAP system, reading HANA views etc you have unlimited option. I can give a sample from my system, one of the BADI is used for cost allocation where it pushes data to ECC, runs cost allocation and gets data back.
      Another BADI is used reading live master data from ECC (same ODATA service)

      For all the scenarios above, we didn't visit SEGW tcode even once!.

       

      Author's profile photo Tobiasz Hołtyn
      Tobiasz Hołtyn

      Hello Bilen,

      The concept looks great!. Thank you for sharing your knowledge and experience.
      Isn't it a problem that you use the Odata service and return a dynamic response? How to model such a service in SEGW? (Not too much experience in Odata). What is the advantage of using SEGW instead of the way presented e.g. below:
      https://blogs.sap.com/2013/01/24/developing-a-rest-api-in-abap/

      Thanks,

      Tobiasz

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Hi Tobiasz,

      Thank you! Consumer always expects a JSON content so we design our UI5 side based on this approach. Since we don't use smart controls, it is all about using JSON model on UI5 side and binding accordingly. It is not different than developing a React or Vue application. We are using a SE11 structure, which has 5 columns only as above. Creating service on top of that structure.

      Advantage of SEGW is security and monitoring embedded in gateway. Login and token handling also automated on ui5 side since it is designed for OData service. If you need OAuth authentication, ODATA is a must. REST API does not allow OAuth. We are actually restifying OData here.

      I also used REST API for many of my ETL process from SAP to NON-SAP.  If you are handling the security, monitoring and token handling, it is very clean and light weight. If you want to use from SAPUI5, you might need to call in a different way comparing to standard odata call. If you are deploying a React or Vue app, i think REST API would be easy. If you have both SAPUI5 apps and React/Vue apps deployed to SAP, i would go for OData with above approach since you can call easily like a REST API from other applications.

       

      Author's profile photo Mostafa Naghizadeh
      Mostafa Naghizadeh

      Hi Bilen,

      I am so glad to read this blog. You have a dynamic Mindset and I really like your work.

       

      I did something Same.

      A Request Table that Defines the Base properties.

      A field property Table with Relation Fields Table along it. For Hide, Limit, Convert to Require and... of related fields.

       

      I also used JSON, But for me, not just for data and run methods, For Building UIs Also. All screen and Value helps, built at runtime.

      In one of my projects, I implement it and they use it for 54 Different Requests with no need to code but in some complicated fields.

      I hope some day we work together in a team, Because I like your mindset.

       

      Take care,

      Mostafa Naghizadeh

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Hey Mostafa, salam.

      Thank you first of all and you did an amazing work there! I tried building sap.ui.table dynamically which i send cols, rows and column types and it was working. Actually if you develop a nice engine to build UI, you can use many areas of your project 😀 , great job.

      I also like open minded people. I know majority just stick to SAP rules ehehe. Hope to work with you as well.

      take care.

       

      Author's profile photo Miller Lam
      Miller Lam

      So impressive. Such a great Blog.

      Thanks for sharing with step-by-step.

      Thank you so much,

      🙂

      Miller Lam.

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Thank you Miller!

      Author's profile photo Martin Voros
      Martin Voros

      Hi,

      I must be the only one here who thinks this is a terrible idea. You are throwing all benefits of oData to provide this generic solution. It seems like your UI contains lots of business logic and you do lots of custom development. This is fine but it costs money. I am not going to argue over benefits of sticking to Fiori design guidelines as you seem to have strong opinions about UX.

      But I have two questions. why picking UI5 when you are doing lots of custom stuff? Why not to pick something with wider eco system? The second question is really similar. You are not doing oData, so why bother with oData at all? ABAP AS has ABAP REST library  which seems like much better fit for implementing your generic interface.

      Cheers

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Calm down, no need to be emotional. Developers are free to implement how they want. I think you need some strong cases here before calling the approach terrible. I don't think you are the only one. People who stick to traditional approach won't like this method. If you are not implementing complex applications where requirements, filters, results keep changing it won't be for you. (as mentioned in the beginning of the article). Before calling the approach terrible, better to understand what type of scenarios might occur. 

      This is not POC and design has proofed its stability, flexibility and development speed over the years. Not only me, you can find a lot of discussion how oData development in SAP is cumbersome for UI5 development. Another thing i am not telling drop your current approach and use this, i also explained this is not for everyone. 

      This is nothing to do with oData benefit, if you think so, you are free to use standard way and keep yourself busy with SEGW.  For our case,  we don't need expand, select, orderby, associations, function import, deeps inserts or other parameters. They all can be done with this approach if needed. Application is custom but what is the problem with it ? Do we need to always implement terrible looking fiori applications or extend it ? "it costs money" does not make sense. First of all this approach aims to speed up development which makes everyone to focus on the business logic rather than losing time on technical layer of oData. From that perspective, it is much more cheaper. We had zero limitations till this time and had no issues welcoming any change on any functionality of the project. Dynamic tables, dynamic charts, dynamic PDF page designs for exports, electronic signatures, dynamic email templates etc all done easily with this approach. I can understand from your perspective where users need to adopt all the rules provided by SAP including fiori guidelines. Developers living in isolated SAP environment have this problem.

      For doing custom apps, SAPUI5 did a great job with recent developments to help on custom developments and applications. Such as ui5-cli, easy generator, transpilers, type-script support etc. I don't think "go wider envrionment" makes sense. They are doing good job. Also it is funny to expect all developers to know svelte, react or vue. Why should we deploy that apps unless there is a non-sap team in the company? There has to be a clear direction on enterprise level on this.

      To answer your questions;

      1. Companies has preference of sticking to one framework for their roadmap. This helps finding resources internally and support on the long term. Any UI5 or ABAP developer can support and continue development on above approach. It makes it wider and easier to find resource. I have VUE applications embedded to my SAPUI5 applications (especially documentation and user guide applications) which runs inside a UI5 view but i can't simply develop a vue/react for an entire app. It is against the roadmap.
      2. Yes we also used REST API for other purposes especially data integrations. It makes it much more easier. For UI5 development, oData makes it easier and gives out-of-the-box functionalities which makes it convenient to use such as security, monitoring, error handling and of course data.js in SAPUI5 is built for oDATA call and login and token handling also automated on ui5 side since it is designed for OData service. If you need OAuth authentication, ODATA is a must.

      My above solution is for anyone who wants more flexibility for their technical layer and develop apps as if they are doing react/vue apps over rest api. After my article i received so many messages where they have exactly same issues in their companies and looking for a better solution. It might be looking terrible for a developer who sticks the standard way of doing apps but it solves many issues on real applications.

       

      Author's profile photo Martin Voros
      Martin Voros

      Hi

      I never said that it does not work. But I am still not convinced by your arguments. None of the things you mentioned are thing that I can't imagine to do with oData.  And you have really strong feeling about UI5 and oData which is fine. But this is not a problem, especially if you have positive experience with your solution.

      But I will try to provide some context where having some proper structure provides some benefits. There are some industry wide best practices on how to build REST services. It seems to be that you declaratively define interface using something like Swagger/OpenAPI, then generate stubs and then developers then implement logic. You are throwing away any structure and making everything generic. This approach extends behind REST for stuff like gRPC as well.

      If you have some spare minutes, you may find this video interesting. It describes how they took that approach even further and defined own linters for service definitions and so on. IMO it provides good arguments that having some definition of interface (a.k.a. $metadata of oData service) can be very useful.

      Going back to SAP world, you can see benefits of structure with ABAP RAP and side extension model. You can easily consume oData service as the tooling can generate proxy service of remote oData service for you.

       

      But every approach has its pros and cons. It's always to have a discussion about these things.

       

      Cheers

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Hi Martin

      I think you totally misunderstood the concept or not reading what i wrote.

      This is nothing to do with REST api standards to test over swagger and nothing to do with RAP. It is all about simplifying oData architecture for UI5 development and making easier for data communication. Especially for the cases where you have dynamic content.
      You can't use for Microsoft Office oData call with this approach, you will receive JSON content, you can't use over SAP Analytics Cloud which is expecting a proper oData structure, you can't use with Power BI as oData source. Purposes are totally different.
      It won't work with smart tables, filters, overview page or other apps which requires strict oData sources.

      Any tool expecting a standard oData output, you can't use. It is purely focusing on UI5 development.  That is why i said in the beginning of article it is not for everyone. It won't fit all scenarios but resolves majority of UI5 development issues for dynamic scenarios.

      Somehow people are thinking i am insisting everyone to use this approach and giving emotional feedbacks like you. If existing oData is already enough for your application, you don't need this. This is not magic, it just aims to hide some complexity of oData development for some cases.

       

      Author's profile photo Martin Voros
      Martin Voros

      Hi,

      I got that, that's not a problem. First thing, I don't understand why you keep calling it oData. It's not. But this is just semantics but I don't want readers of this blog to think that this is oData.

      What I am trying to say is that I am not convinced about the benefits of your approach and you are losing some benefits of proper oData down the road. But it's OK to decide this way. Not only down the road, but immediately as well. E.g. enforcing consistent UI behavior is hard smart controls help with this a lot. But I assume your argument would be something like it's terrible and we can do better.

      One last thing. This approach reminds me Neptune Software solution. That is usually liked by traditional ABAP developer. Did you look into it?

      Cheers

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

       

      " I don't want readers of this blog to think that this is oData" 😀 😀

      I have no idea why you are very emotional with the article. 
      I decided to delete my answer sorry this discussion is not going anywhere, everyone free to implement oData how they want. You can reach readers one by one and inform them if you want. You don't see the issue here. It is not something standard way of implementing oData cannot do but we can do. It is all about removing some unnecessary work from the developer. Even there are some people here already did similar implementations. Whoever implementing applications for SD-FI-MM scenarios or Sflight-Sbook tutorials or any applications mentioned in RAP developer guide won't need this approach. Whoever need dynamic capabilities for their business scenarios (especially planning applications) will understand what i am trying to say here. 

       

      Author's profile photo Andrew Barber
      Andrew Barber

      Bilen Cekic

      Really nice article and well written in my eyes.

      Its always good to get an insight into how other developers are using SAP, even if we don't agree, it makes us think and consider new ideas and ways of working.

      We have carried out similar ideas of using BADIs in oData feeds based on front end components (e.g. an address component).

      For example a component like entering addresses might have the same UX but they provide a different back end goal, different tables are updated and different customisation is done based on the components parent app.

      Cheers

      Andrew

       

      Author's profile photo Bilen Cekic
      Bilen Cekic
      Blog Post Author

      Thank you Andrew!

      Totally agree, we should open for new ideas even we don't prefer the way of implementation. Thanks for your positive feedback!
      I understand your logic and yes mine is not very different. So you grouped your BADI based on components but each component on different part of application can save to maybe different table or target model. Above approach is not that different, we just hide the complexity of OData components to a single service.

      Cheers