Skip to Content

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

Blog – Architecture overview

To report this post you need to login first.

7 Comments

You must be Logged on to comment or reply to a post.

    1. Hariprasauth R 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,

       

      (1) 
  1. 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.

    (1) 
  2. 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)

     

    (0) 

Leave a Reply