
RAP - CDS Pattern
Wie geht eigentlich das CDS Pattern und was hat CDS-only damit zu tun? In diesem Artikel schauen wir auf die Architektur und Nutzung des Patterns.
Inhaltsverzeichnis
In diesem Artikel schauen wir uns das CDS Pattern an, wie du es implementieren und sinnvoll in deine Entwicklung einbinden kannst.
Einleitung
Das ABAP RESTful Application Programming Model ist das neue Modell in ABAP, um Cloud Ready und Clean Core Anwendungen zu erstellen. Mit RAP lassen sich neben Anwendungen, auch Schnittstellen für den internen und externen Gebrauch zur Verfügung stellen. Mit den neusten Features ist RAP sehr flexibel was den Aufbau und die Nutzung angeht, weshalb wir die Anwendungen in verschiedene Pattern aufteilen möchten.
Aufbau
Das "CDS Pattern" wird so genannt, weil wir die CDS-only Mechanismen der Entwicklung nutzen. Wir verzichten dabei auf die klassischen Instrumente, wie Datenelemente, Domänen und DDIC Tabellen, um unser Datenmodell zu erstellen. Damit ersparen wir uns verschiedene Mappings bei der Nutzung der Daten in unserem Modell. Zur Einleitung noch die Legende für das Modell.
Dazu die folgenden Merkmale zur Abgrenzung:
- Datenquelle des Models sind Table Entities (Nachfolger der DDIC Tabellen)
- Interface Layer (CDS) wird nicht benötigt, Verhalten und Verbindungen können auf CDS Ebene modelliert werden
- Nutzung von CDS-only (Table Entity, Simple Types, Aspects)
- Mapping in Verhaltensdefinition nicht mehr nötig
Mit der Table Entity können wir unser Datenmodell und die Beziehungen bereits auf dieser Ebene definieren und müssen das nicht auf Interface Ebene machen. Daher können wir uns diesen Layer sparen und damit Objekte. Der Layer bleibt aber optional, wenn du vielleicht ein Mapping von Elementen machen möchtest oder andere Besonderheiten hast.
Beispiel
Dazu modellieren wir unsere Anwendung auf dem Datenmodell unserer Core Data Service Reihe und verwenden die Rechnung (Invoice) und die Position um ein RAP Objekt zu erstellen.
Datenmodell
Da wir keine klassische Tabelle haben, legen wir zwei Table Entities im System an. In diesem Fall arbeiten wir mit eingebauten Datentypen und verzichten der Einfachheit halber auf Simple Types. In der Entität müssen wir ebenfalls die neuen Datentypen für Datum und Uhrzeit verwenden, da die Alten nicht mehr unterstützt werden.
@ClientHandling.type: #CLIENT_DEPENDENT
@AbapCatalog.deliveryClass: #APPLICATION_DATA
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'CDS-P: Invoice'
define root table entity ZBS_T_CDSPatternInvoice
{
key DocumentNumber : abap.char(8);
DocumentDate : abap.datn;
DocumentTime : abap.timn;
PartnerNumber : abap.char(10);
@Semantics.user.createdBy: true
LocalCreatedBy : abp_creation_user;
@Semantics.systemDateTime.createdAt: true
LocalCreatedAt : abp_creation_tstmpl;
@Semantics.user.localInstanceLastChangedBy: true
LocalLastChangedBy : abp_locinst_lastchange_user;
@Semantics.systemDateTime.localInstanceLastChangedAt: true
LocalLastChangedAt : abp_locinst_lastchange_tstmpl;
@Semantics.systemDateTime.lastChangedAt: true
LastChangedAt : abp_lastchange_tstmpl;
_Position : composition of exact one to many ZBS_T_CDSPatternPosition;
}
Das Mandanten-Handling passiert über die Annotation im View. Zusätzlich können wir auch direkt die Beziehung im View modellieren. Bevor wir den View aber aktivieren können, sollten wir zuvor noch die Position anlegen. Hier definieren wir auch die entsprechenden Felder, müssen allerdings noch zusätzlich die Einheit für die Menge aufnehmen, da Assoziationen an dieser Stelle nicht möglich sind.
@ClientHandling.type: #CLIENT_DEPENDENT
@AbapCatalog.deliveryClass: #APPLICATION_DATA
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'CDS-P: Position'
define table entity ZBS_T_CDSPatternPosition
{
key DocumentNumber : abap.char(8);
key PositionNumber : abap.int2;
MaterialNumber : abap.char(5);
@Semantics.quantity.unitOfMeasure : 'PositionUnit'
PositionQuantity : abap.quan(10,0);
PositionUnit : abap.unit(3);
@Semantics.amount.currencyCode : 'PositionCurrency'
PositionPrice : abap.curr(15,2);
PositionCurrency : abap.cuky;
_Document : association to parent ZBS_T_CDSPatternInvoice on _Document.DocumentNumber = $projection.DocumentNumber;
}
Verhalten
Im Verhalten definieren wir noch zusätzlich Draft, verlinken die Felder und die verschiedenen Draft Actions, da wir einen OData v4 anlegen wollen. Im aktuellen Status können wir leider nur DDIC Tabellen für den Draft anlegen, dass soll Anfang 2026 sich dann ändern. Da wir das Verhalten direkt auf der Table Entity definiert haben, sparen wir uns das Mapping der Entität auf die Datenquelle.
managed implementation in class zbp_bs_cdspattern_invoice unique;
strict ( 2 );
with draft;
define behavior for ZBS_T_CDSPatternInvoice alias Invoice
draft table zbs_cp_invoice_d
lock master total etag LastChangedAt
authorization master ( instance )
etag master LocalLastChangedAt
{
create ( authorization : global );
update;
delete;
field ( readonly ) DocumentNumber;
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
draft determine action Prepare;
association _Position { create; with draft; }
}
define behavior for ZBS_T_CDSPatternPosition alias Position
draft table zbs_cp_pos_d
lock dependent by _Document
authorization dependent by _Document
{
update;
delete;
field ( readonly ) DocumentNumber, PositionNumber;
association _Document { with draft; }
}
Abschluss
Aktuell werden Table Entities nicht in den RAP Generatoren unterstützt, daher müssen wir das komplette RAP BO per Hand aufbauen. Die restlichen Schritte, wie Projection, Service Definition und Binding können wir dann wie immer definieren. Für das Numbering weichen wir aktuell auf ein Late Numbering aus und implementieren etwas Zusatzlogik. Die weiteren Details zur Implementierung findest du im GitHub Repository im nächsten Kapitel.
Test
Nachdem wir dann das UI über die Metadata Extension angelegt haben, können wir die Anwendung testen. Dazu legen wir einen neuen Header an und befüllen die nötigen Informationen.
Nach dem Speichern wurde eine neue Nummer vergeben und die Daten auf der Datenbank abgelegt, unsere Anwendung funktioniert nun so weit, wenn auch nicht perfekt.
Vollständiges Beispiel
Das vollständige Beispiel findest du bei uns im GitHub Repository und über den Commit findest du alle Änderungen. Innerhalb des Repository befindet sich das Beispiel im Paket ZBS_DEMO_RAP_PATTERN_CDS. Wie bereits oben beschrieben, wirst du das Consumption Model und die Aufrufimplementierung dort nicht finden.
Zusammenfassung
Im Grund ist das Pattern sehr ähnlich zum Classic Pattern in der RAP Entwicklung. Dadurch, dass wir aber auf die klassischen Artefakte verzichten, können wir bereits auf Ebene der Table Entity die echten Feldnamen definieren und die Beziehungen innerhalb der Objekte modellieren. Damit können wir uns in den meisten Fällen den Interface Layer sparen und das Verhalten direkt auf die Tabelle setzen. Damit erhalten wir auch Groß- und Kleinschreibung für die Typen, Objekte und Felder und haben im Gegensatz zur Tabelle, mehr Zeichen für die Benennung.
Allerdings funktioniert das Modell noch nicht zu 100% im aktuellen Zustand. Simple Types mit ENUMs werden noch nicht auf Ebene der UI und RAP unterstützt und Draft ist zwar möglich, verwendet aber immer noch eine klassische Tabelle. Mehr der neuen Features werden in 2026 kommen, um das CDS Pattern zu unterstützen.
Fazit
Das CDS Pattern wird in Zukunft immer mehr das Classic Pattern ablösen und vereinfachen. Die Anzahl der Objekte wird sich reduzieren und so werden RAP Objekte leichter verständlich. Da wir auf Tabellenebene bereits Berechtigungsprüfungen umsetzen können, wird das Modell für Lesezugriffe auch etwas sicherer.



