Für die Entwicklung von Fiori Apps bietet SAP ein neues Programmiermodell an. Dabei kann das Fiori-Frontend auf Basis des Datenmodells (CDS) automatisch generiert werden. Aber RAP kann auch für die Entwicklung von WebAPIs verwendet werden. Dieser Erfahrungsbericht gibt keine Einführung in RAP, sondern betrachtet Details des Bereichs Behavior Definition und Implementation aus der Praxis.
Mehr Informationen zum Thema „RAP-Einführung" erhalten Sie hier:
Anforderung
Unser Kunde benötigte einige UI-Anwendungen, um auf einem mobilen Scanner Buchungen im SAP EWM auszuführen. Er wünschte eine Umsetzung im ABAP RESTful Application Programming Model (RAP), weil es sich hierbei um die neueste Technik handelt.
Bei der Entwicklung im Bereich der mobilen Scanner spielt die Optimierung der Oberfläche für kleine Bildschirmgrößen eine zentrale Rolle. Bei Fiori Apps, die mittels RAP generiert werden, hat man jedoch nur geringen Einfluss auf das Layout. Außerdem ist die Bedienung eher auf Desktop-Anwendungen ausgelegt. Wir haben uns daher darauf geeinigt, RAP im Backend zur Entwicklung des OData-Services zu verwenden und die Frontendentwicklung mittels SAPUI5 vorzunehmen.
Bestehende OData-Services
Das SAP S/4HANA-System liefert bereits einige OData-Services aus. Diese sind hier zu finden, die Liste ist aber recht unübersichtlich. Man kann ebenso in der Eclipse-Entwicklungsumgebung nach API_* suchen. Im Bereich SAP EWM ist vor allem das Entwicklungspaket WME_ODATA_API zu nennen, in dem einige WebServices zu finden sind, die auf CDS aufbauen und die RAP-Technik verwenden. Wir haben allerdings von diesen Services keinen Gebrauch gemacht, weil uns die Dokumentation nicht ausreichte. Trotzdem konnten wir uns dort Anregungen holen und CDS-Views finden, um diese zum Aufbau eigener Services zu nutzen.
Praxisbeispiele
Einfache CDS View publizieren
Oft müssen für Auswahllisten recht einfache Queries erstellt werden, die sich leicht mit einer CDS View abbilden lassen. In unserem Beispiel sollte eine Liste der Lagernummern mit ihren Bezeichnungen angezeigt werden. Erstaunlich war, dass im Standard OData-Service API_WAREHOUSE dies gar nicht so einfach möglich ist, weil die Entity Warehouse den Text nicht liefert und die Assoziation _WarehouseText die Bezeichnungen in allen Sprachen bereitstellt. Man müsste im Frontend den passenden Filter auf die Anmeldesprache setzen.
In diesem Fall erschien es einfacher, eine maßgeschneiderte CDS View anzulegen und im Service zu publizieren. Die View ist denkbar einfach:
Die Entität muss lediglich in der Servicedefinition veröffentlicht werden und schon ist der Service fertig.
Mit dieser einfachen Vorgehensweise lassen sich Datenbankabfragen mit sehr wenig Arbeit als Service anbieten.
In Abbildung 2: Einfache Root View Entity sieht man einen OData-Service, den wir in mehreren SAPUI5 Apps verwenden. Da es im UI5-Frontend sehr einfach ist, mehrere OData-Services anzubinden, kann man in der App sowohl diese allgemeinen Funktionen aufrufen als auch spezielle Funktionen, die für die App entwickelt wurden.
Virtual Elements Calculation
Manchmal benötigt man in einer CDS View zusätzliche Daten, die sich mittels Join oder Association nicht einfach ermitteln lassen. In unserem Projekt sollte aus der Chargenklassifizierung ein Merkmal ausgelesen und als zusätzliches Feld angezeigt werden.
Virtual Elements bieten die Möglichkeit, ein einzelnes Feld der CDS View mittels ABAP nachzulesen. Dazu muss eine ABAP-Klasse das Interface IF_SADL_EXIT_CALC_ELEMENT_READ implementieren. Diese Klasse wird mittels Annotation in der CDS View in dem Feld hinterlegt.
Nachstehende Methode muss in der Klasse implementiert werden.
Methode IF_SADL_EXIT_CALC_ELEMENT_READ~GET_CALCULATION_INFO
Diese Methode wird vor der Datenbeschaffung vom RAP-Framework aufgerufen. Es müssen die Feldnamen angegeben werden, die später für die Ermittlung des Virtual Elements benötigt werden. In unserem Fall sind das die Produktnummer und die Chargennummer, die als Schlüssel zum Lesen der Chargenklassifikation benötigt werden. Die ABAP-Klasse kann in allen CDS Views verwendet werden, wenn sie diese beiden Felder enthält.
Methode IF_SADL_EXIT_CALC_ELEMENT_READ~CALCULATE
Diese Methode wird aufgerufen, nachdem das RAP-Framework alle Daten der Query gelesen hat. Alle ermittelten Datensätze werden als interne Tabelle in die Methode übergeben. In dieser Tabelle müssen dann die Werte des Virtual Elements (im Beispiel „LOT_CODE“) ergänzt werden. Die Struktur der internen Tabelle ist nicht festgelegt. Die ABAP-Klasse kann in mehreren CDS Views mit unterschiedlicher Struktur verwendet werden. Aufgrund der vorherigen Prüfung durch die Methode GET_CALCULATION_INFO ist aber gesichert, dass die Felder „Produktnummer" und „Chargennummer" in der Datenstruktur enthalten sind. Der Zugriff auf die Tabellenzeilen und deren Felder sollte dynamisch (zum Beispiel mittels ASSIGN) erfolgen.
Virtual Elements Filter
Soll bei einem Query über ein Virtual Element gefiltert werden, ist die oben dargestellte Berechnung des Virtual Elements zu spät. Im schlimmsten Fall würden alle Zeilen der CDS View gelesen und für alle Zeilen das Virtual Element berechnet werden. Erst ganz zum Schluss wäre eine Reduktion der Treffermenge möglich.
Aus diesem Grund soll man in der ABAP-Klasse das Interface IF_SADL_EXIT_FILTER_TRANSFORM implementieren. Das RAP-Framework ruft die Interface Methode IF_SADL_EXIT_FILTER_TRANSFORM~MAP_ATOM vor dem Lesen der CDS View auf. Man kann hier aufgrund der Filterkriterien des Virtual Elements eine WHERE-Bedingung für den CDS-Zugriff erstellen.
In unserem Beispiel ist das Virtual Element das Merkmal LOT_CODE aus der Chargenklassifizierung. Die Anforderung besteht darin, zunächst alle Chargen mit der passenden Klassifizierung zu finden und für diese eine passenden WHERE-Bedingung zum Lesen der CDS View zu erstellen.
Glücklicherweise bietet SAP einen performanten Funktionsbaustein zur Klassensuche (CLSC_SEARCH_IN_CLASSTYPE). Dieser unterstützt verschiedene Selektionsoperatoren - allerdings in einer eigenwilligen Darstellung.
Nachstehend finden Sie die Klassensuche zur Ermittlung der Chargen mit passendem Merkmal. Das Selektionskriterium wird über die beiden Parameter IV_OPERATOR und IV_VALUE durch das RAP-Framework angegeben. Diese müssen in die Selektionsdarstellung des Funktionsbausteins umgewandelt werden.
Die Klassensuche liefert alle passenden Chargen in der Tabelle LT_OBJECTS. Der Schlüssel der Charge setzt sich aus dem Produkt und der Chargennummer zusammen. Damit das RAP-Framework anschließend nur Einträge mit dieser Charge liest, muss eine WHERE-Bedingung aus folgendem Muster zusammengesetzt werden:
- ( PRODUKT = <produkt1> AND CHARGE = <charge1> ) OR
- ( PRODUKT = <produkt2> AND CHARGE = <charge2> ) OR …
Die Rückgabe der WHERE-Bedingung erfolgt über eine Klassendarstellung des Interface IF_SADL_SIMPLE_COND_FACTORY. Einstieg ist die Factory Klasse CL_SADL_COND_PROV_FACTORY_PUB. Mit dieser werden für die beiden Selektionsfelder (Produktnummer, Chargennummer) je eine Instanz des Interface IF_SADL_SIMPLE_COND_ELEMENT. Mit Methoden dieser Elemente kann die WHERE-Bedingung zusammensetzt werden.
Custom Entities
Bei den meisten SAP EWM-Apps müssen Handling Units gelesen und zum Teil auch angelegt werden. Leider konnten wir dazu keinen passenden OData-Service finden. Handling Units mit ihrem Inhalt mittels CDS View zu lesen, gestaltet sich schwierig, weil das SAP-Datenmodell dort Daten auf mehrere Indextabellen verteilt. Daher haben wir eine CUSTOM ENTITY verwendet, bei der die Daten per ABAP-Methode gelesen werden. Die Klasse wird hier als Annotation zur CUSTOM ENTITY hinterlegt.
Die Handling Units werden in einer Methode der ABAP-Klasse gelesen. Die Klasse muss das Interface IF_RAP_QUERY_PROVIDER implementieren. Die Methode IF_RAP_QUERY_PROVIDE~SELECT wird sowohl für den Header als auch für die Assoziationen (_Items) aufgerufen. In der Methode müssen daher zwei Leseroutinen für die beiden unterschiedlichen EntitySets implementiert werden.
Die Implementierung der SELECT-Methoden gestaltet sich einfach, weil man mittels SAP-Funktionsbaustein /SCWM/HU_SELECT_GEN die Daten für unterschiedlichste Filterkriterien leicht beschaffen kann.
Der Baustein liest dabei Kopf- und Positionsdaten gemeinsam. Leider wird die SELECT-Methode vom RAP-Framework zweimal aufgerufen: einmal für die Kopfdaten der Handling Unit und einmal für die Positionsdaten. Damit /SCWM/HU_SELECT_GEN nicht zweimal aufgerufen wird, kann man die Daten in der ABAP-Klasse zwischenspeichern. Dazu muss man statische Attribute verwenden, weil das RAP-Framework getrennte Instanzen für die beiden Aufrufe anlegt.
Die Rückgabe des Query-Ergebnisses erfolgt über die Instanz IO_RESPONSE, die vom RAP-Framework in die Methode gereicht wird. Hier kann neben den Daten auch die Gesamtanzahl der Einträge angegeben werden, um eine Paginierung zu ermöglichen.
Entity Behavior
Zu einer Entity können Aktionen mittels Behavior Definition spezifiziert werden. Im Sinne von OOP handelt es sich hierbei entweder um Methoden, die an eine Instanz gebunden sind (Instance Action), oder um Methoden, die übergreifend für den Entity Type verwendet werden (Static Action). Falls man mit Fiori Elements das Frontend automatisch generiert, werden die Actions als Buttons sichtbar. Daher gibt es einige Annotationen, die nur der optischen Gestaltung dienen, die wir aber im Projekt nicht genutzt haben.
Im folgenden Beispiel wird das Anlegen einer Handling Unit mittels Static Action realisiert. Das normale OData CREATE (HTTP POST) wurde nicht verwendet, weil wir mit der Static Action andere Aufrufparameter verwenden können. Außerdem wurde für die Handling Unit eine Instance Action zum Drucken des Labels realisiert.
Aufrufparameter und Rückgabeparameter von Aktionen können am einfachsten mittels Entitäten typisiert werden. Hier bieten sich abstrakte Entitäten an, da man diese nur zur Typisierung und nicht für Queries verwenden möchte.
Die Aktionen werden in einer ABAP-Klasse implementiert und die Eclipse mittels Wizard mit dem Namenspräfix ZBP_ erstellt. Die erstellte Klasse ist und bleibt leer. Die eigentliche Implementierung erfolgt in einer lokalen Klasse (Behavior Handler), die innerhalb der globale ZBP_ Klasse angelegt ist. Das Coding wird erreicht, indem man in Eclipse am unteren Editor-Rand den Reiter „Local Types“ anwählt.
Man erkennt, dass die Methodendefinition neue ABAP-Sprachelemente enthält (FOR MODIFY, FOR ACTION, RESULT), die ausschließlich im Behavior Handler verwendet werden können. Diese Klasse lässt sich nur in Eclipse editieren; in der SAP-GUI Transaktion SE80 kann man sie lediglich begutachten.
Die tatsächlichen IMPORTING- und EXPORTING-Parameter kann man am einfachsten in Eclipse über die „Element Info“ einsehen. Die Dokumentation zu einem Element lässt sich über F2 aufrufen. Sehr praktisch ist es, die Element Info als View dauerhaft anzuzeigen (Menüpunkt Window->Show View->other …->ABAP Element Info).
Um eine HU anzulegen, kann man die Methoden CREATE_HU der Klasse /SCWM/CL_WM_PACKING verwenden. Die Anlage haben wir in eine getrennte Klasse ausgelagert, um das Coding in der Behavior Handler-Klasse kürzer zu halten. Die Aufrufparameter der Action sind als interne Tabelle KEYS erreichbar.
Um das Ergebnis der Aktion an das RAP-Framework zurückzugeben, müssen die Exporting-Tabellen MAPPED und RESULT gefüllt werden.
Fehler können über die Rückgabetabelle FAILED und REPORTED ausgegeben werden, die Fehlerbeschreibung erfolgt als Instanz einer Exception-Klasse. Die verwendete Exception-Klasse muss das Interface IF_ABAP_BEHV_MESSAGE implementieren. Es ist also notwendig, für das Projekt eine geeignete Exception-Klasse anzulegen. In der Rückgabetabelle FAILED kann man über das Feld %fail-cause den HTTP Response Code setzen. In dem Interface IF_ABAP_BEHV sind die möglichen Werte als Konstanten definiert.
In der Behavior Handler-Klasse darf kein COMMIT WORK ausgeführt werden, da dies ausschließlich dem umliegenden RAP-Framework vorbehalten ist. Auch ist es nicht erlaubt, einen Verbuchungsbaustein aufzurufen (CALL FUNCTION ‚…‘ IN UPDATE TASK).
Im genannten Fall der HU–Anlage ist dies ein Problem, da man die Methode SAVE der Klasse /SCWM/CL_WM_PACKING aufrufen muss, die wiederum den Verbuchungsbaustein aufruft. Man erhält zur Laufzeit einen Programmabbruch mit Dump.
Hier zwei Lösungsansätze, um dieses Problem zu lösen:
Lokale SAVER-Klasse
Nach dem Aufruf des Behavior Handlers ruft das RAP-Framework Methoden einer weiteren Klasse auf, die zum Speichern dienen (Behavior Saver). Diese Klasse ist ebenfalls in der „Local Types“-Definition zu finden.
In dieser Klasse darf zwar auch kein COMMIT ausgeführt werden, aber der Aufruf eines Verbuchungsbausteins ist erlaubt. In unserem Fall muss also der Aufruf der Methode /SCWM/CL_WM_PACKING->SAVE in diese Klasse erfolgen.
Jetzt könnte man versuchen, gleich die gesamte Funktion der HU-Anlage in diese Behavior Saver-Klasse zu verschieben. Dies ist aber nicht möglich, weil das RAP Framework hier die Aufrufparameter nicht mitgibt. Die Aufrufparameter stehen lediglich in dem zuvor genannten Behavior Handler zur Verfügung und auch nur dort kann man die Rückgabeparameter setzen. Die gesamte Programmlogik und alle Prüfungen müssen also im Behavior Handler erfolgen, im Behavior Saver wird lediglich der Verbuchungsbaustein aufgerufen.
Verwendung eines zweiten Prozesses
Eine andere Lösung des COMMIT-Problems ist es, die Funktion in einen parallelen Arbeitsprozess auszulagern, da dieser den genannten Einschränkungen nicht unterliegt. Dazu muss man einen Funktionsbaustein aufbauen und diesen asynchron mittels CALL FUNTION ‚…‘ STARTING NEW TASK aufrufen. Die Rückgabe der Daten aus dem parallelen Arbeitsprozess erfolgt in einer CALLBACK-Methode mit dem Befehl RECEIVE. Eine Beschreibung dazu findet man in der ABAP-Hilfe in dem Abschnitt „Remote Function Call“.
Mehrere Logical Units of Work (LUW)
Für die Scanner-Anwendungen ergibt sich häufig die Anforderung, mehrere SAP-Prozessschritte automatisch hintereinander auszuführen. Zum Beispiel muss auf einen Klick erst eine Pick-HU angelegt und gespeichert und anschließend eine Lageraufgabe erstellt werden. Diese Schritte sind im ABAP getrennte Logical Unit of Work (LUW). Jede LUW muss mit einem COMMIT WORK abgeschlossen werden. Wie im vorherigen Kapitel beschrieben, darf man aber im Behavior Handler kein COMMIT WORK aufrufen.
Als Lösungsansatz kann man diese Schritte in einen parallelen Prozess auslagern, wie bereits zuvor angesprochen wurde. Alternativ lässt sich für jede LUW eine getrennte Action definieren und im Frontend nacheinander aufrufen. Nachteil dabei ist eine höhere Laufzeit durch Latenzen und ein höherer Programmieraufwand. Letzterer wird dadurch kompensiert, dass die entstandenen Aktionen durch ihren kleineren Funktionsumfang besser wiederverwendet werden können. Zum Beispiel ist das Anlegen einer HU eine oft benötigte Funktion, die in unterschiedlichen Apps sehr ähnlich ist, und bestimmt wiederverwendet werden kann.
Business Object Projection
In der SAP-Beschreibung von RAP werden die Business Objects (Entitäten) in zwei Layer aufgeteilt: Definition und Projection. Dies ermöglicht es, für ein Business Object mehrere Apps für unterschiedliche Zielgruppen zu erstellen. Man legt mehrere Projections an, die jeweils nur die gewünschten Teile freigeben. Der OData-Service und das generierte Fiori Elements-Frontend basieren nicht direkt auf dem Business Object, sondern auf der Projection. Jede Projection und das zugehörige Frontend bieten daher nur die dort freigegeben Felder und Aktionen an.
In unserem Projekt haben wir die RAP-Technik lediglich für den OData-Service verwendet und kein Frontend generiert. Außerdem wurde für jede App ein getrennter OData-Service aufgesetzt. Dieses ermöglichte eine bessere Arbeitsteilung und gegenseitige Unabhängigkeit. Für Projekte dieser Art scheint die Definition der Projektionssicht überdimensioniert. Wir konnten auch keine technischen Probleme dadurch beobachten, dass die OData-Services direkt die Entitäten publizierten (anstelle einer Projektion).
OData V2 / V4
Unabhängig von der Implementierung der CDS-Entitäten und des ABAP-Codings, kann man den RAP-Service als OData V2 oder OData V4 veröffentlichen. Auch ist es möglich, denselben Service in beiden OData-Versionen parallel zu veröffentlichen. Man muss lediglich die entsprechenden Service Bindings anlegen.
Die Frontend-Entwicklung unterscheidet sich bei den beiden OData-Versionen, weil in der SAPUI5 Library die zugehörigen ODataModel-Klassen unterschiedlich aufgebaut sind. Bei OData V2 bietet die Klasse Methoden zum Anlegen, Ändern und Lesen der Entitäten. Bei OData V4 erzeugt man mit der ODataModel-Klasse Bindings (Unterklassen von sap.ui.model.Binding), mit denen auf den Service zugegriffen wird.
Für die Absprache zwischen Frontend- und Backend-Entwickler sollte die von SAP erzeugte metadata.xml-Datei (Edmx) genauer betrachtet werden. Insbesondere in Hinblick auf die Actions aus der Behavior Definition (siehe Kapitel Entity Behavior) unterscheiden sich die beiden OData-Versionen.
Instance Action
Bei OData V2 werden die Actions als „FunctionImport“ umgesetzt. Solche Funktionen sind prinzipiell nicht an eine Collection oder Entität gebunden. Handelt es sich in der Behavior Definition um eine Instance Action, werden im Edmx die Schlüsselfelder der Entität als Importparameter hinzugefügt. In der nachstehenden Abbildung sehen Sie die Parameter der Instance Action „PrintLabel“. Die Parameter sind hier als Abstract Entity definiert.
In der nächsten Abbildung sieht man die Funktionsbeschreibung als Edmx Odata V2. Man sieht, dass gegenüber der Parameterdefinition die Aufrufparameter Warehouse und HuIdent hinzugekommen sind, die den Schlüssel der Handling Unit Entität bilden.
Der Aufruf dieser Funktion erfolgt mittels HTTP POST. Die Parameter müssen bei OData V2 immer als Query-Parameter angegeben werden und können leider nicht im Body gesendet werden:
POST /sap/opu/odata/sap/zscm_pick/PrintLabel?Warehouse=‘1225‘&HuIdent=‘212250000010008850‘&Printer=‘LOCL‘
Nachstehend als Vergleich: die Funktionsbeschreibung als Emdx Odata V4. Die Action wird als IsBound gekennzeichnet. Es ist nun der Aufrufparameter „_it“ hinzugekommen, der eine Referenz zur Handling Unit Entität darstellt.
Beim Aufruf der Action setzt sich die URL aus der Entität, dem Schlüssel und dem Actionnamen zusammen. Dem Actionnamen muss der Namespace vorangestellt werden, der leider recht lang ist. Die weiteren Aufrufparameter werden im Body zum Beispiel im JSON-Format angegeben. Bei OData V4 ist es uns nicht gelungen, in der Übergabestruktur optionale Felder (nullable=“true“) zu definieren; dies wird scheinbar noch nicht unterstützt. Daher müssen beim Aufruf der Action alle Parameter im JSON angegeben werden. Somit würde ein Aufruf der Action in OData V4 folgendermaßen erfolgen:
POST /sap/opu/odata4/sap/zscm_pick_v4/srvd/sap/zscm_pick/0001/
HandlingUnit(Warehouse=‘1225‘,HuIdent=‘212250000010008850‘)/com.sap.gateway.srvd.zscm_pick.v0001.PrintLabel
{
„Printer“: „LOCL“,
„DocumentCategory“: „“,
„Copies“: 1
}
Static Action
Im Gegensatz zu der vorangegangenen Instance Action bezieht sich eine Static Action nicht auf eine einzelne Entität, sondern auf die Collection. Bei OData V2 entfallen daher die zusätzlichen Parameter des Entity Keys.
Bei OData V4 wird die Static Action wie auch die Instance Action gleichermaßen mit IsBound=“true“ gekennzeichnet. Lediglich der Type des „_it“-Parameters ändert sich. Bei der Static Action bezieht sich der Parameter nun auf die Collection. Nachstehend die Edmx-Darstellung zu der Static Action CreateHandlingUnit aus Abbildung 24.
Beim Aufruf der Action wird in der URL der Schlüssel der Entität weggelassen. Die URL setzt sich folgendermaßen zusammen:
POST /sap/opu/odata4/sap/zscm_pick_v4/srvd/sap/zscm_pick/0001/
HandlingUnit/com.sap.gateway.srvd.zscm_pick.v0001.CreateHandlingUnit
Zusammenfassung
Aus Sicht der Backend-Entwicklung fällt die Bewertung der Techniken RAP und SEGW nicht eindeutig aus. Die Nutzung von CDS und dessen enge Verbindung zum RAP erlaubt eine elegante und performante Realisierung. Besonders die Leistungsfähigkeit der Managed Entities ist hervorragend.
Der lesende OData-Zugriff mittels CDS View kann allerdings nur verwendet werden, wenn das Datenbankmodell sich dafür eignet. Im Beratungsgeschäft setzt unsere Entwicklung in den meisten Fällen auf die SAP-Module auf. Wir müssen also das vorgegebene Datenbankmodell nutzen. Dieses ist nicht immer normalisiert und oft für eine reine Nutzung von CDS Views ungeeignet. Dann müssen Custom Entities oder Virtual Elements erstellt werden. In diesem Fall ist der Entwicklungsaufwand für die Queries bei SEGW und RAP etwa gleich.
Bei schreibenden OData-Zugriffen sollte man in keinem Fall direkt in SAP-Datenbanktabellen schreiben; die Verwendung von vorgegebenen BAPI-Funktionen ist obligatorisch. Daher können in der Regel nur Unmanaged Entities genutzt werden. Auch hier ist der Entwicklungsaufwand bei der SEGW und bei RAP ähnlich hoch. Die strenge Einschränkung, dass im RAP dabei kein Verbuchungsbaustein direkt aufgerufen werden kann und kein Commit erlaubt ist, macht die Programmierung im RAP-Modell aufwändiger.
Die Programmierung mittels RAP-Modell ist dann vorteilhaft, wenn man mit Managed Entities arbeiten kann und das Frontend mit Hilfe von SAP Fiori Elements automatisch generiert. Andernfalls können die OData-Dienste mit der SEGW eher etwas einfacher erstellt werden. Hier ist nochmal zu erwähnen, dass auch in der SEGW ohne Entwicklungsaufwand CDS Views für Queries genutzt werden können.
SEGW und RAP unterstützen beide OData V2 und OData V4. Die Unterstützung von OData V4 durch die SAPUI5 Library ist allerdings gewöhnungsbedürftig und eher umständlich. Ich würde daher aktuell eher OData V2 empfehlen. Wenn man aber für das Frontend andere Werkzeuge nutzt, fällt die Bewertung eventuell anders aus. Wir nutzen für die Frontend-Entwicklung neben SAPUI5 auch Typescript und React. Dafür haben wir eine selbstentwickelte OData Library, die auf OData V4 spezialisiert ist.