Skip to Content
Personal Insights
Author's profile photo DJ Adams

Monday morning thoughts: exploring reuse

In this post, I think about reuse and extension, in the context of the Application Programming Model for SAP Cloud Platform, and mindful of Björn Goerke’s SAP TechEd 2018 keynote message in Barcelona – “keep the core clean”.

Last week saw the Barcelona edition of SAP TechEd 2018, where SAP CTO Björn Goerke and a great team of role models on stage gave us a keynote with something for everyone – technical and business alike. During the keynote, I tweeted:

My three keywords from the #SAPTechEd keynote so far:

Open (standards, protocols, APIs)

Reuse (important superpower of @sapcp application programming model) 

Clean (keep the core clean by extending outside of it)

I want to think about the “reuse” and “clean” keywords, because in many ways they’re complementary, in that reuse (and by association, extension) can help to achieve the goal of a clean core.

Of course, there’s a lot more to it than that, but reusing & extending definitions and services is a key part of building outside of the core, whether for net new applications or to extend existing solutions. That implies that the application programming model, which has reuse as a “superpower”, is a very useful model to know about.

So I thought I’d look into an example of reuse and extension that exist for us to meditate upon and learn from.

 

cloud-samples-itelo

Earlier this year Oliver Welzel wrote “ITelO – A Sample Business Application for the new Application Programming Model for SAP Cloud Platform” in which he described an application with ra product catalog, and reviews, for the fictitious company ITelO. The data model is in three layers, with each building on the one beneath it.

This diagram from the post provides a nice summary of that:

The component overview, showing how the data model is built up in layers

(Perhaps before continuing with this post, it might be worth you going and taking a read of Oliver’s post. Don’t forget to come back, though!)

 

Multiple layers

The idea is that there are core artefacts in the “foundation” layer, the “product-catalog” layer builds on top of that, and then there’s the “itelo” specific application layer at the top.

Each layer is represented by a repository in GitHub, so all the source is available to study. If we start at the top, and look at the data model definition at the “itelo” layer, this is what we see*, specifically in the db/model.cds source:

namespace clouds.itelo;

using clouds.products.Products from '@sap/cloud-samples-catalog';
using clouds.foundation as fnd from '@sap/cloud-samples-foundation';

extend Products with {
	reviews: Association to many Reviews on reviews.product = $self @title: '{i18n>review}';
	averageRating: Decimal(4, 2) @(
		title: '{i18n>averageRating}',
		Common.FieldControl: #ReadOnly
	);
	numberOfReviews: Integer @(
		title: '{i18n>numberOfReviews}',
		Common.FieldControl: #ReadOnly
	);
}

entity Reviews: fnd.BusinessObject {
	product: Association to Products @title: '{i18n>product}';
	reviewer: Association to Reviewers @title: '{i18n>reviewer_XTIT}';
	title: String(60) @title: '{i18n>reviewTitle}';
	message: String(1024) @title: '{i18n>reviewText}';
	rating: Decimal(4, 2) @title: '{i18n>rating}';
	helpfulCount: Integer @title: '{i18n>ratedHelpful}';
	helpfulTotal: Integer @title: '{i18n>ratedTotal}';
}

annotate Reviews with {
	ID @title: '{i18n>review}';
}

entity Reviewers: fnd.Person, fnd.BusinessObject {
}

annotate Reviewers with {
	ID @title: '{i18n>reviewer_XTIT}';
}

 

*I’m specifically using the “rel-1.0” branch in each case, because that’s what’s also used in the dependency references that we’ll see shortly, and represents a stable version that we can examine.

 

Reuse through “using” statements

Looking at the first few lines, we see some “using” statements:

using clouds.products.Products from '@sap/cloud-samples-catalog';
using clouds.foundation as fnd from '@sap/cloud-samples-foundation';

So this is already interesting. Is this reuse in action? It is. But what does it mean, exactly? Let’s investigate. Taking the first “using” statement, something called “clouds.products.Products” is being used from something called “@sap/cloud-samples-catalog”.

In the Model Reuse section of the documentation on the SAP Help Portal, we can see that this is effectively an import of a definition from another CDS model. OK, which one? Well, we can recognise the “cloud-samples-catalog” name as it’s one of the layers in the diagram we looked at earlier. But how is that resolved?

For that, we have to look in the “itelo” layer project’s package.json file, where, amongst other things, we see some dependencies defined:

"dependencies": {
	"@sap/cloud-samples-foundation": "https://github.com/SAP/cloud-samples-foundation.git#rel-1.0",
	"@sap/cloud-samples-catalog": "https://github.com/SAP/cloud-samples-catalog.git#rel-1.0"
}

Ooh, well that’s exciting, for a start! The package.json file is from the Node Package Manager (NPM) world and the dependencies section is where one defines dependencies to other packages, typically ones like “express”, if you’re building services that handle HTTP requests, for example. But what do we have here?

Well, we can see the names referenced in the “using” statements earlier, in other words “@sap/cloud-samples-catalog” and “@sap/cloud-samples-foundation”. But instead of simple package names, they’re mapped to GitHub URLs. And not just any GitHub URLs, but URLs that refer to specific repositories, and indeed specific branches! Taking the URL for the “@sap/cloud-samples-catalog” name, we have:

https://github.com/SAP/cloud-samples-catalog.git#rel-1.0

which refers to the rel-1.0 branch of the cloud-samples-catalog repository belonging to SAP.

 

The “product-catalog” layer

Looking there, we see a fully formed application – the middle “product-catalog” layer that we saw earlier, with app, srv and db folders representing each of the three components of a typical fully fledged solution based on the application programming model.

In the db folder we see the model.cds file, which starts like this:

namespace clouds.products;

using clouds.foundation as fnd from '@sap/cloud-samples-foundation';
using clouds.foundation.CodeList;

entity Products: fnd.BusinessObject {
	// general info
	key ID: String(36);
	name: localized String @(
		title: '{i18n>name}',
		Common.FieldControl: #Mandatory,
		Capabilities.SearchRestrictions.Searchable
	);
	description: localized String @(
		title: '{i18n>description}',
		Common.FieldControl: #Mandatory
	);

	[...]

 

Fractals

In a wonderfully fractal way, we notice immediately that this model definition also refers to another package with a “using” statement, but let’s resist descending deeper just at this moment. Instead, we can concentrate on looking at what’s going on with the “using” statement we’ve seen in the consuming definition earlier, which looked like this:

using clouds.products.Products from '@sap/cloud-samples-catalog';

We realise that “clouds.products.Products” refers to the Products entity in the “cloud.products” namespace, which is defined here with the “entity” definition:

entity Products: fnd.BusinessObject { ... }

But what is that “fnd.BusinessObject” sitting between the entity name and the block definition in curly braces? Why, it’s more reuse, this time of the underlying “foundation” layer. Just above in the same file, we can see that this layer is referenced in a “using” statement, this time with a local alias “fnd” defined:

using clouds.foundation as fnd from '@sap/cloud-samples-foundation';

So now let’s briefly descend into the fractal. The reference to “fnd.BusinessObject” is to an entity defined in the “foundation” layer, which we can see if we follow the dependency reference in the “product-catalog” layer’s package.json:

(It’s worth observing that in this layer we only have data definitions — in the form of “.cds” files — rather than a full blown solution with app, srv and db folders.)

In this repository (again, branch “rel-1.0”) we can find the definition of the BusinessObject entity in the common.cds file looking like this:

abstract entity BusinessObject : ManagedObject {
	key ID : UUID @(
		title: '{i18n>uuid}',
		Common.Text: {$value: name, "@UI.TextArrangement": #TextOnly}
	);
}

Note in passing that here the “BusinessObject” entity is defined as “abstract” which means that it’s just a type declaration rather than something for which instances should exist. Note also that it’s further defined, using a similar pattern to where we saw the “fnd.BusinessObject” reference, by another abstract entity definition “ManagedObject” (you can find this definition of ManagedObject also in the common.cds file).

 

Extension through “extend” statements

Moving back up the layers for some air, we see that directly following the “using” statements, there is this:

extend Products with {
	reviews: Association to many Reviews on reviews.product = $self @title: '{i18n>review}';
	averageRating: Decimal(4, 2) @(
		title: '{i18n>averageRating}',
		Common.FieldControl: #ReadOnly
	);
	numberOfReviews: Integer @(
		title: '{i18n>numberOfReviews}',
		Common.FieldControl: #ReadOnly
	);
}

With the “extend” aspect, entity definitions can be repurposed with extra properties, for example. In this case, the existing Products entity (from the “product-catalog” layer’s data definition) is extended with three properties: “reviews”, “averageRating” and “numberOfReviews”. Note that the “reviews” property is an association to a Reviews entity at this (itelo) application layer, defined expressly for this purpose.

Moreover, some of the properties in the Reviews entity are also defined as associations to further entities therein, such as the reviewer property which points to the Reviewers entity, which has no properties of its own, but in a beautiful way inherits from some of the definitions (Person and BussinessObject) at the “foundation” layer:

entity Reviewers: fnd.Person, fnd.BusinessObject {
}

 

Wrapping up

That might be a lot to take in, in one sitting. It has become quite clear to me that the facilities afforded by the CDS language in the application programming model are very rich when it comes to reuse and extensions. Not only at the definition level, but also in the simplicity of how package based references are realised.

While at first I thought it was a little odd to see the GitHub repository & branch URLs, and indeed to realise that the package.json mechanism was fundamental to how artefacts in the application programming model are related, I’ve come to think that it’s a natural way to do it, and a celebration of adopting an approach that’s already out there in the world beyond our SAP ecosphere.

What’s more, we haven’t even touched on how annotations work and what we are able to do in terms of reuse there too. But I’ll leave that for another time, instead leaving you with the suggestion that reuse is indeed an important superpower of the application programming model, and demonstrably so. And keeping the core clean – well, the more extension and reuse we can achieve, the closer we can get to a cleaner core.

 

This post was brought to you by a chilly Monday morning, by Pact Coffee’s Asomuprisma in my SAP Coffee Corner Radio mug, and by a Spotify mix designed for concentration.

 

Read more posts in this series here: Monday morning thoughts.

Assigned Tags

      7 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Michelle Crapo
      Michelle Crapo

      This is way too heavy reading for a Monday morning.  So I will be honest and say I did go to Oliver's post.  I then read your post "lightly".  What does that mean? It means I didn't dig into the tech behind it.  <Sigh> I'm always feeling I'm running as fast as I can and the super train is flying by me!

      One step at a time - Right?  I'll come back later to look at this one!

      Michelle

      Author's profile photo DJ Adams
      DJ Adams
      Blog Post Author

      Whoa, sorry Michelle! Thanks for being honest and letting me know. Perhaps a second reading will be more fruitful, and I can use your comment as useful input as to how deep or technical I should go for other posts. But yes, one step at a time. Let me know if you have any questions after you've come back for a second go. Cheers!

      Author's profile photo Mike Doyle
      Mike Doyle

      Nice post DJ, thanks.  I think that 'smart' reuse is key enabler of success.  By 'smart' I mean not making things too complicated, and only making things re-usable if they are likely to be re-used.  Often it's hard to tell this ahead of time, but experience does help somewhat.

      I wonder how we will navigate all of the thousands of CDS artifacts that will be created in the coming years.  We could do with some kind of smart browser to show us the artifacts and how they fit together.  Otherwise duplication is quite likely I fear.

      I think I would get a lot out of a one-day code jam on the Cloud Platform Application Programming Model, given a good teacher/coach.  Is there anything planned? For any Australasian harbour-side cities that begin with S?

       

       

       

      Author's profile photo DJ Adams
      DJ Adams
      Blog Post Author

      Cheers Mike, appreciated. Yes, with many things, including this subject, as well as, say, cloud functions, there's a management and orchestration aspect that is important. Hand in hand with that is thoughtful design, too.

      Currently we don't have application programming model CodeJam events, but I think it's definitely worth considering. I'll reach out to my colleagues to start the conversation. Good suggestion!

      Author's profile photo Henrik Damhøj Andersen
      Henrik Damhøj Andersen

      Great blog, DJ!

      Maybe this is a little off-topic, but I have been wondering about a reference in cloud-sample-spaceflight which i almost thought I got the answer for reading your blog 🙂

      In cloud-sample-spaceflight there is a reference to ‘@sap/cds/common' in cloud-sample-spaceflight/db/common.cds.

      It’s not defined in dependencies. So where is it defined? Is it some kind of implicit declaration?

      cloud-sample-spaceflight/db/common.cds
      namespace common;
      	using {
      	  managed as AdminData,
      	  sap
      	} from '@sap/cds/common';
      	
      cloud-sample-spaceflight/db/package.json
      	{
      		"name": "deploy",
      		"dependencies": {
      			"@sap/hdi-deploy": "3.7.0"
      		},
      		"scripts": {
      			"postinstall": "node conditionalBuild.js",
      			"start": "node node_modules/@sap/hdi-deploy/deploy.js --treat-unmodified-as-modified"
      		}
      	}

       

      Regards, Henrik

      Author's profile photo DJ Adams
      DJ Adams
      Blog Post Author

      Hey Henrik, thanks for the praise. Glad you like it, and this question is certainly not off-topic, so don't worry!

      The dependencies are defined in the repo's top-level package.json file, rather than any in the child directories. The one you're looking at above is in the db directory. If you look in package.json at the top level you'll see this:

        "dependencies": {
          "@sap/cds": "^2.8"
        },

      Following the trail to `@sap/cds` with the command "cds version" we can see where cds is installed:

      => cds version | grep home
      CDS home: /Users/i347491/.nvm/versions/node/v8.12.0/lib/node_modules/@sap/cds

      And in there, we find the "common" definition:

      => ls -l /Users/i347491/.nvm/versions/node/v8.12.0/lib/node_modules/@sap/cds
      total 368
      -rw-r--r--   1 i347491  staff    6290  8 Oct 08:11 CHANGELOG.md
      -rw-r--r--   1 i347491  staff     694  8 Oct 08:11 README.md
      -rw-r--r--   1 i347491  staff  143066  8 Oct 13:09 SIGNATURE.SMF
      drwxr-xr-x   5 i347491  staff     160 19 Oct 09:12 _i18n
      drwxr-xr-x   9 i347491  staff     288 19 Oct 09:12 apis
      drwxr-xr-x  18 i347491  staff     576 19 Oct 09:12 bin
      -rw-r--r--   1 i347491  staff    3926  8 Oct 08:11 common.cds
      -rw-r--r--   1 i347491  staff   12591 28 Feb  2017 developer-license-3.1.txt
      drwxr-xr-x   6 i347491  staff     192 19 Oct 09:12 lib
      drwxr-xr-x  21 i347491  staff     672 19 Oct 09:12 node_modules
      -rw-r--r--   1 i347491  staff    1412  8 Oct 08:11 npm-shrinkwrap.json
      -rw-r--r--   1 i347491  staff    4508 19 Oct 09:12 package.json
      

      which contains, amongst other things, the "managed" definition.

      Good question, btw (I'm still learning too!)

       

      Author's profile photo Henrik Damhøj Andersen
      Henrik Damhøj Andersen

      Thanks for taking the time to reply so even I get it 🙂

      Regards, Henrik