Skip to Content
Technical Articles

SAP Cloud Platform Backend service: Tutorial [7]: CDS : organize model (1): separate models

This blog is part of a series of tutorials explaining the usage of
SAP Cloud Platform Backend service in detail.

Today’s detail is about separating data model and service definition

Example 1: Define data model and service definition together

Up to now, we’ve always defined our CDS-service as top-level element and we’ve defined the entities inside the service.
Remember? I had mentioned in the beginning of the tutorial series:

CDS allows to define 1) data model and 2) service and 3) UI.
That can be done in one file or multiple files.
Also, data model and service can be defined at the same time.

That’s what we’ve been doing until now, for the sake of simplicity.

The following code snippet shows that:
Define a service (“BusinessPartnerService”) and data model (“BusinessPartnerEntity”) in one step:

service BusinessPartnerService {
    entity BusinessPartnerEntity{
        key id: Integer;
        name : String;
    }
}

 

Example 2: Separate data model and service definition

Now let’s see how we can create a CDS file with separate data model and service definition.
Below example is identical with the above snippet (only one little difference):

entity BusinessPartnerEntity{
    key id: Integer;
    name : String;
}

service BusinessPartnerService {
    entity BusinessPartners as projection on BusinessPartnerEntity;
}

You can see:
The entity is defined as top-level element and the service as well.
The service definition refers to the entity.

What does “projection” mean?

Projection means that this is not a new entity definition.
In the backend, no new database table is generated.
Rather, it is reflected as database view
This makes perfectly sense, as we don’t want to duplicate the data that we want to store.
Furthermore, it makes sense that the OData service operates on a view instead of a table.

There’s one little difference…

Have you noticed that we’re actually renaming our entity?

That’s why I’ve asked

In the service definition we’re defining an entity as “BusinessPartners” which points to the “BusinessPartnerEntity”
Like this, we can correct the bad naming of the data model entity (“BusinessPartnerEntity”).
Remember: the CDS element “service” is used to define which data is exposed and how it should look like.
After creating an API in the Backend service cockpit, the OData endpoint will use the entity name which is given in the service definition.

What? I never liked such abstract descriptions…

OK, an example:

https://<backendservice>/odatav4/DEFAULT/MYAPI/BusinessPartners

Can I define multiple entites?

Yes, of course.

Can I define multiple services?

Ehmm…yes… but…

But what?

OK, the truth: CDS allows to define multiple services referring to the same data model.
However, it will be ignored by Backend service.

A model like this would make some sense:

entity BusinessPartnerEntity{
    key id: Integer;
    name : String;
}

service SupplierService {
    entity Suppliers as projection on BusinessPartnerEntity;
}

service CustomerService {
    entity Customers as projection on BusinessPartnerEntity;
}

Note:
Backend service doesn’t support 2 service definitions
It makes sense:
Backend service helps you in creating an API, based on a model,
as well as testing and maintaining the API.
So, only one API per CDS file is desired.
As such, when feeding above example to Backend service, only one of the service definitions will be considered.

Example 3: Features of service model

The following example illustrates some more capabilities which can be leveraged while separating data model and service definition.

The following sample model contains:
one custom type,
one BusinessPartner-entity and
one service.

The service exposes 2 entity collections, each with different aspect, but both based on the same data (“BusinessPartnerEntity”).

type AddressType {
    city : String;
    street : String;
    streetNumber : Integer;
}

entity BusinessPartnerEntity{
    key id: Integer;
    createdAt: Timestamp;
    createdBy : String; 

    supplierName : String;
    supplierAddress : AddressType;

    deliveryCustomerName : String;
    deliveryCustomerAddress : AddressType;
}


service BusinessPartnerService {
    // example for exclude properties
    entity Suppliers as projection on BusinessPartnerEntity 
                            excluding {
                                createdAt, 
                                createdBy, 
                                deliveryCustomerName,
                                deliveryCustomerAddress
                            };

    // example for include and rename properties
    entity Customers as projection on BusinessPartnerEntity{
                                id as companyId,
                                deliveryCustomerName as companyName,
                                deliveryCustomerAddress.city as companyHeadquarters                                 
                            };

}

Noteworthy:

  1. Service renames entity
    We have again chosen the bad entity name with bad suffix (“BusinessPartnerEntity”)
    But in the service definition, we can change the name.
    No useless suffix.
    But always plural
  2. Service exposes multiple kinds of the entity
    In the data model we have one entity.
    This entity with generic name (“BusinessPartnerEntity”) holds data which can be used for “Suppliers” or for “Customers”
    In the service, we want to be less generic.
    As such, we define specialized 2 entities, one for “Suppliers” and one for “Customers”.
    But both point to the same data model entity
  1. Service refactors data model
    While specializing, we see that we somehow need more refactoring
    It is not enough to change the name of the entity,
    we also want to remove some properties, which don’t match the desired specialized entity
    For example, the renamed “Suppliers” entity shouldn’t carry info about “Customer”-related data.
    This is possible, there are 2 ways of achieving it
    3.1. excluding
    We define a projection to the data model entity,
    then explicitly list those properties which we want to exclude
    3.2. including
    We define a projection to the data model entity,
    then explicitly list those properties which we want to include
    All properties which are not listed here, are not part of the OData service
  1. Service refactors properties
    While listing properties which are included in the service model, new names can be assigned (like alias)

After creating an API in Backend service, based on above CDS model, the resulting OData model looks as follows:

 

<EntityType Name="Suppliers">
   <Key>
      <PropertyRef Name="id"/>
   </Key>
   <Property Name="id" Type="Edm.Int32" Nullable="false"/>
   <Property Name="supplierName" Type="Edm.String"/>
   <Property Name="supplierAddress_city" Type="Edm.String"/>
   <Property Name="supplierAddress_street" Type="Edm.String"/>
   <Property Name="supplierAddress_streetNumber" Type="Edm.Int32"/>
</EntityType>
<EntityType Name="Customers">
   <Key>
      <PropertyRef Name="companyId"/>
   </Key>
   <Property Name="companyId" Type="Edm.Int32" Nullable="false"/>
   <Property Name="companyName" Type="Edm.String"/>
   <Property Name="companyHeadquarters" Type="Edm.String"/>
</EntityType>
<EntityContainer Name="EntityContainer">
   <EntitySet Name="Suppliers" EntityType="BusinessPartnerService.Suppliers"/>
   <EntitySet Name="Customers" EntityType="BusinessPartnerService.Customers"/>
</EntityContainer>

Noteworthy:

We have 2 EntitySets, like expected
The refactored entity names are used, no suffix “Entity” is visible anywhere
The properties are as expected:
Suppliers: the excluded properties are not there anymore
Suppliers: the custom type has been flattened
Customers: the new names have been considered, only mentioned properties are here
Customers: the flattened custom type has been replaced by one property-alias (“companyHeadquarters”)

 

Testing

In the Backend service cockpit, go to the testing tool

Go to the “Suppliers” section

Execute a GET request on the “Suppliers” collection:
-> empty

Create one “Suppliers” entry.
-> All normal

Execute a GET request on the “Suppliers” collection:
-> the created entry is there

Now go to the “Customers” section

Execute a GET request on the Customers collection
-> There’s one entry
-> It has empty properties
-> Only the key value is there, it is the value of the previously created Supplier

That’s as expected and shows that both EntitySets operate on the same database table

 

 

Summary

At the end of the day (today) you’ve learned what benefits you get when separating data model and service definition in your CDS file

While the given example is not good for enterprise ready usage, I believe it serves as good example of how the mechanism works.
Yes, thanks

 

Links

CDS documentation in SAP Help here  and here

Tutorials:
The overview
Previous tutorial: Custom Types
Next tutorial: namespace and context

Appendix

<empty>

 

Be the first to leave a comment
You must be Logged on to comment or reply to a post.