Skip to Content
Technical Articles
Author's profile photo Hariprasauth R

Developing Multitenant Applications on SAP Cloud Platform, Cloud Foundry environment

Software-as-a-Service (SaaS) applications are becoming more and more relevant now with the ease of delivery to customers. It is impossible to be successful in SaaS without utilizing the capabilities of multitenancy. There is already a blog that explains how to develop a multitenant application in the Neo environment on SAP Cloud Platform.

In this blog, I’ll show you the concept of multitenancy in the Cloud Foundry environment together with a sample multitenant business application that we’ve developed to showcase the consumption of relevant services in the Cloud Foundry environment on SAP Cloud Platform.

As a prerequisite to this blog, I strongly recommend that you read about the changes to the domain model in SAP Cloud Platform in this blog. It is also assumed that you are already aware of the provider vs. consumer (tenant) concepts of multitenancy.

Let’s start off by looking at the components that are needed for a multitenant business application to run in the Cloud Foundry environment in SAP Cloud Platform.

 

The application owner (the provider of the multitenant application) owns the global account and the subaccount where the application is hosted in SAP Cloud Platform. The application providers can manage subscriptions for their customers in order to use the multitenant features of the SAP Cloud Platform.

Some of the aspects of multitenancy, such as being able to subscribe to applications hosted in other global account and the monetization of the provider applications might be available in the future releases.

The application uses the tenant-aware approuter service, which is the single point-of-entry for the application running on Cloud Foundry environment. The xsuaa service is used for authentication at runtime. The application is registered with SaaS Provisioning (saas-registry) service, which enables the subscription lifecycle events of the application with the consumer tenants.

Which coding approach should you follow?

Multitenancy at the persistence level can be approached in various ways, each with its pros and cons:

  • Column Discriminator – data is stored in the same schema of the same database and tenant details are maintained in a column of the table.
    1. This is the most cost efficient, but separation is very weak and tenant-specific configurations are hard to implement.
    2. Tenant-specific backup and restore is not possible.
  • Schema Separation – data is stored in a separate schema (same database) per tenant.
    1. Cost efficiency and data separation are balanced.
    2. Extension of the schema per tenant is possible.
    3. Requires additional logic for backup and restore to avoid overwriting the entire database in case of schema corruption.
  • Database Instances – every tenant has its own database instance.
    1. Provides the highest level of data isolation as the instances are physically separated.
    2. This entails the highest cost as additional server instances are needed to store each database.
    3. Backup and restore is possible for each tenant.
    4. Underutilization is a possibility.

You can find more details about these approaches in this blog.

 

From a cost and isolation perspective, the recommended approach is schema separation.

SAP Cloud Platform also provides the automated onboarding/offboarding capabilities for the application provider. This is done through a couple of API callbacks:

  • getDependencies: Provides dependencies to multitenant reuse services.
  • onSubscription: Provides the logic of setting up a tenant in the application (subscription). The same callback is also used to unsubscribe a tenant. This callback must be implemented to return the application URL that suits the tenant host pattern.

The tenant host pattern is used in a manifest.yml file to identify the tenant that is accessing the application.

TENANT_HOST_PATTERN: <<tenant>>-<<appname>>.<<domain>>

Let’s look at some coding snapshots

 

With this blog, we provide a sample application as a reference point to get this fully implemented. This application is about a Product master. The application stores the details of products that are specific to the tenant (customer). The code snapshots that follow based on column discriminator multitenancy.

The sample application contains the following structures and entities:

  • applicationBackend
  • applicationUIModule + appRouter
  • mtConfig (callback implementation)
  • xs-security.json (XSUAA configuration)
  • yml (for Multi-Target Application builds)

The xs-security.json file describes the scope and the tenant-mode:

 

{
   "xsappname": "inventorymanagementapp",
   "tenant-mode": "shared",
   "description": "Security profile of inventory management app",
   "scopes": [{
		"name": "$XSAPPNAME.Callback",
		"description": "With this scope set, the callbacks for tenant onboarding, offboarding and getDependencies can be called.",
		"grant-as-authority-to-apps": [
		   "$XSAPPNAME(application,sap-provisioning,tenant-onboarding)"
		]
    }]
}
  • The xsappname must be unique per the XSUAA instance.
  • The tenant-mode should be set to ‘shared’ and will require that the TENANT_HOST_PATTERN is declared in the yml file as mentioned earlier.
  • For single-tenant applications, the tenant-mode must be declared as ‘dedicated’.
  • The scope mentioned in the above code snapshot must be granted to the tenant-onboarding service as mentioned in the description of the scope. This is only required for automated onboarding of tenants.

 

To consume a backing service such as SAP HANA, PostgreSQL and so on, the service instance of the backing service must be bound to the application. You can bind the service instances to an application either from the SAP Cloud Platform cockpit or through the CF CLI as mentioned in the

In the UI model of an SAP UI5 application, you must ensure to maintain the approuter dependency in the package.json.

  "dependencies": {
	    "@sap/approuter": "2.7.1"
	  },
	  "scripts": {
	    "start": "node node_modules/@sap/approuter/approuter.js"
	  }
   ....
   }

 

The configuration for the approuter (xs-app.json) must contain the necessary routes to the UI application and the backend application.

 

{
     "welcomeFile": "/inventorymanagementui/index.html",
     "authenticationMethod": "route",
     "logout": {
		"logoutEndpoint": "/logout"
     },
     "routes": [{
		"source": "^/inventorymanagementui/(.*)$",
		"target": "$1",
		"localDir": "webapp"
     }, {
		"source": "^/inventorymanagementbackend/(.*)$",
		"target": "$1",
		"csrfProtection": true,
		"authenticationType": "xsuaa",
		"destination": "inventorymanagementbackend_api"
     }]
}

The backend application uses the sapxsenv and xssec libraries to parse the environment variables and the JSON Web Token (JWT) strategy, respectively.

var xsenv = require('@sap/xsenv');
var JWTStrategy = require('@sap/xssec').JWTStrategy;

var services = xsenv.getServices({ uaa:'uaa_inventorymanagementapp' });
passport.use(new JWTStrategy(services.uaa));
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }))

Let’s now look at the implementation of the discriminator column as mentioned in the previous sections.

For a simple column-discriminator persistence, the query to retrieve the tenant-specific data would look like this:

var selectAllProductsQuery = "SELECT * FROM products WHERE \"tenant_id\" = " + tenant_id;

For inserting values, the statement would look like this:

"INSERT INTO products (\"product_name\",\"product_description\",\"supplier_name\",\"price\",\"available\",  \"quantity\",\"tenant_id\")" +
"VALUES($1,$2,$3,$4,$5,$6,$7);";

For detailed documentation about these steps, refer to Developing Multitenant Business Applications in the Cloud Foundry Environment on SAP Help Portal.

In subsequent blogs that we’ll be posting, you’ll learn about the recommended schema-based multitenant persistency on an SAP HANA database.

 

References and sequels:

Sample code – Developing a SaaS Multitenant Business Application on SAP Cloud Platform Cloud Foundry Environment

Sample Code 2 – Multitenancy on HANA

Blog – Architecture overview

Blog – SaaS Provisioning service and its consumption

Assigned Tags

      21 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Venu Ravipati
      Venu Ravipati

      Very useful Blog Hariprasauth.

      Thanks for sharing. Looking forward for next Blogs.

      is the sample code available in github?

       

      Best Regards,
      Venu

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Hi Venu,

      Happy that you liked the blog! Keep watching this space for the sample code. We are just in the process of publishing the code along with the guide!

      Thanks for the support..

      Cheers,

       

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Updated with sample code!

      Author's profile photo Kirill Chekhter
      Kirill Chekhter

      Thank you for a very detailed description, Hariprasauth,

       

      would that be possible to provide a link to your sample application in the Github?

       

      Thank you and best regards, Kirill.

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Thank you reading the blog, Kirill. We are in the process of releasing the code sample. Keep watching this space for more information.

      Regards, Hari

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Updated with the code!

      Author's profile photo Hans-Joachim Milbert
      Hans-Joachim Milbert

      Hi Hari

      I've tried to bring your example project from Git to my CF Trial account, but the deploy to CF failed.
      (see the following error msg). Have I done something wrong or isn't it possible to run the example on the CP Trial?

       

      Thnx and BR Hans-Joachim

      10:45:57 (DeployService) Target: https://deploy-service.cfapps.eu10.hana.ondemand.com/slprot/Pxxxxxxxxxtrial_trial/dev/slp
      Preparing to deploy /mta_archives/inventorymanagementapp/inventorymanagementapp_1.0.0.mtar
      Checking if there are conflicting processes
      Found 1 conflicting processes
      Aborting process 73314494
      Uploading archive
      Starting deployment
      Validating parameters...
      Processing MTA archive...
      Processing MTA extension descriptors...
      Detecting MTA major schema version...
      MTA schema version: 3.1.0
      Detecting deploy target...
      Detected deploy target "Pxxxxxxxxtrial_trial dev"
      Validating and merging descriptors...
      Detecting deployed MTA...
      No deployed MTA detected
      Collecting system parameters...
      New MTA version: 1.0.0
      Error resolving merged descriptor properties and parameters: Unable to resolve "inventorymanagementui#inventorymanagementbackend_api#url"
      Unexpected error: Unable to resolve "inventorymanagementui#inventorymanagementbackend_api#url"
      Exception occured during execution: "Unable to resolve "inventorymanagementui#inventorymanagementbackend_api#url""
      Job failed
      10:45:57 (DeploymentToCF) Deployment of the "inventorymanagementapp" project to Cloud Foundry failed.(Response state is FAILED)

       

      Author's profile photo Eric Jiang
      Eric Jiang

      Hi Hariprasauth,

      Thanks for the blog very much. What if the business application has some other third party services bound e.g. a Workflow Service, Destination or Connectivity etc.? After subscription, does the customer have to create these service instances in their Subaccount or not? If yes, how does our business application know these service instances under customer's Subaccount and access them? If no, these service instance may have their own data storage which is not controlled by us then how to do the data isolation for different customers there? Thanks very much.

      Best regards,

      Eric Jiang

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Hi Eric,

      Thanks for following the blog! The application provider can architect the code in a way to read the configurations from the consumer side. This way, the customers/consumers can simply configure their respective URLs.

       

      Regards,

      Hari

      Author's profile photo Siddharth Jain
      Siddharth Jain

      Hi Hari,

      Great blog,Eagerly awaiting on the next blog on recommended schema-based multi tenant persistence on an SAP HANA database using Dynamic HDI Mapping .It would be great if you can also have a demonstration with Spring Boot  as back end application.

      Thanks,

      Siddharth

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Hi Siddharth,

      May be this code helps.

      Regards,

      Hari

      Author's profile photo Siddharth Jain
      Siddharth Jain

      Thanks Hari for your efforts and help,I noticed a very strange thing ,the sample inventory management app which is a available on Git hub ,i deployed it previously and did the subscription and unsubscribe it before but now Unsubscribe feature is not working ,it just stay as subscribed, is there any change in API ,call back just return status 200 in your nodejs application code ,what is going wrong ,please suggest.

      Author's profile photo Clark Huang
      Clark Huang

      Hi Hari,

      Thanks for your sharing. Any updates on the next blog about schema-based multi tenant?

      Best Regards

      Clark Huang

      Author's profile photo Hariprasauth R
      Hariprasauth R
      Blog Post Author

      Hi Clark,

      It should be out soon.

      Regards,

      Hari

      Author's profile photo Josh Clark
      Josh Clark

      Thanks for the blog!  I'd also be very interested in some multitenant Java samples that use HANA DB backend w/ schema per tenant.  Is there such an example or documentation on how to accomplish this?

      Author's profile photo Philip MUGGLESTONE
      Philip MUGGLESTONE

      Hi Josh,

      Hands-on video tutorials are now available that cover the multitenant topic including HANA-based schema separation - see this blog for details:

      Hands-on Video Tutorials for Developing Multitenant Business Applications

      Regards,

      Philip

      Author's profile photo Treasa PA
      Treasa PA

      Hi Hariprasauth,

      Great blog!

      I was also trying to develop a multi tenant app and have followed the steps mentioned here. But i get the following error: "Authorization request error- No client with requested id: ******* ".

      Could you please help.

      Thanks and Regards,

      Treasa

      Author's profile photo Terry Huang
      Terry Huang

      Hi Hariprasauth,

      Is the UAA has to be set as "tenantmode": "shared"? I found that the authentication process will be link to different IdP if I set it as "shared".

      Best regards,

      Terry

      Author's profile photo Andrew WU
      Andrew WU

      Hi Terry

      I found the same issue with the idp. If the tenant-mode set to dedicated, it'll redirect to my IAS service for login. But with tenant-mode set to shared, it'll use the default SAP HANA XS Advanced login. Have you found any solutions for this?

      Cheers
      Andrew

      Author's profile photo Suraj T Acharya
      Suraj T Acharya

      Hi Hari / Terry / Andrew,

      Facing similar issue (redirecting to default SAP HANA XS Advanced login page) when using tenant-mode as "shared" in xs-security.json. Is there any config issue that should be looked into?

      Thanks

      Regards,

      Suraj Acharya

      Author's profile photo Mike Zaschka
      Mike Zaschka

      Hi,

      I do have the same problem and get redirected to the XS Advanced Login, where my user won't be recognized. Did you find any solution for this?

      Kind Regards,

      Mike