Technical Articles
Designing UI5 Apps for SAP Launchpad Service – Part 3
Content Navigation
Part 1: A simple UI5 app that runs in the SAP Launchpad Service
Part 2: Multiple Apps with a Shared Reuse Library
Part 3: Splitting Bigger Projects (Current page)
Splitting Bigger Projects
In the last posts, you have learnt how to build multiple apps and move common parts of them into a reusable library. Everything is deployed by one MTA.
In this part, we will have a look at how to split this into smaller parts. This enables us to have independent lifecycles to split responsibilities between different developer groups.
Example: The reusable library might have a different lifecycle than the apps and is not changed that often. Maybe they are also maintained by other developers and should be stored in a separate Git repository.
The sample code can be found in the GitHub repository.
Current State
Let’s have a quick look at the starting point. There are three HTML5 apps, which are all deployed to one shared app-host. There is one XSUAA and one destination service. All service names in manifest.json files and the destinations are the same.
The corresponding MTA looks like this:
A First Split for the App-Hosts
The first split you would do is to use multiple HTML5 app-hosts and distribute the HTML5 apps to them.
For every app-host there will be one destination created. They all use the same cloud service name. There is no change for the apps itself.
The corresponding MTA would look like this. There are three managed resources for the app-hosts. Three content deployers and destinations are created by the destination content deployer.
If you have more apps, you can distribute them to app-hosts as needed.
Only splitting the app-hosts as it is done until now does not add a lot of value because everything is still contained in one MTA. But it is a good starting point for the next step.
Split the MTAs
In the next step, we will split the single MTA into multiple ones. Every app and app-host will be moved to a separate MTA. The XSUAA and the destination service will still be shared by all and therefore move to the MTA containing the shared library. The result of the deployment will not change at all. Below, the colors indicate which MTA will deploy which content.
The cloud service name will still be the same everywhere.
The MTAs [Shared, App1, App2] will look like this.
Note that the destination service resources in the app MTAs are of the type “existing service”. Only the shared MTA defines it as a “managed service” because this MTA owns the service instance and will create it. Therefore the shared MTA has to be deployed first.
This setup already fulfils most use cases. The apps and shared library can now be developed and deployed independently. They can also be moved to different Git repositories.
Split the XSUAAs
Until now, the MTA projects are already quite independent but they still share the XSUAA and the contained authorization objects. However, your apps might contain complex back-end services and have complex authorization objects. In that case, developers would not want to maintain the xs-security.json files in the central MTA but move the app-specific parts to the MTA of the app. This is a good reason to split the XSUAAs.
Now we have three XSUAA instances. Each one is deployed by one MTA. As soon as we split the MTAs, we also split the cloud service names. Every pair of app-host and XSUAA uses the same service name including the apps which are deployed on the app-host. This way the authorization handling is bound to the correct XSUAA and its authorization model applies. Example: If you have routes in xs-app.json that require a given scope, they are defined by the corresponding XSUAA. The same would happen for any back-end apps that are possibly contained in your MTA.
The following rules apply:
- There must be exactly one XSUAA instance per cloud service name. Even if you do not have any authorization model, you would have to provide an empty XSUAA.
- All HTML5 apps on the same app-host have to use the same cloud service name
The MTAs [Shared, App1, App2] will look like this:
Every MTA now contains an own xs-security.json [Shared, App1, App2] with an own unique xsappname.
The service names have been replaced in the manifest.json (sap.cloud/service) [Shared, App1, App2] files as well as in the MTA files (sap.cloud.service in destination definition for app-host and xsuaa) [Shared, App1, App2] and in the index.html (resource mappings) [App1, App2]. Also, the URLs to run the apps standalone now contain the new service tags.
To be continued
Above are the most relevant splits for your use cases, which I wanted to publish as soon as possible due to a high demand.
Watch out for an updated version with further splits.
Hi Matthias Schmalz
Thank you very much for the series on the subject, your explanation was of great help to my consultation and understanding of the concepts.
Before your explanation, I was deploying the library and applications in different XSUAA and destination services. Trying to consume the library by route in xs-app.json. Frankly, I don't know if this is possible.
My gratitude is so great that if one day you are interested in knowing Brazil, I humbly dispose of my house for you and your family. I live in Vitória, capital of the state of Espirito Santo, an excellent place to visit.
LinkedIn: https://www.linkedin.com/in/kefrencezar
Thanks for sharing this. Actually I was also exploring the possibility recently for it to reuse the service instances.
I think one of the key part is the "existing_destinations_policy: ignore" in the mta.yaml which prevents subsequent deployment from overwriting the destinations to the app-host created previously potentially by other developers.
I think from my experience a centralized xsuaa instance will be more practical in real life, as it can be easily controlled by the security person. Of course it has pros and cons too.
I have a seperate question, so
once we defined the role/role collections in the xsuaa, then expose the apps to launchpad service, in which there are also roles (inside launchpad). What is the best or recommended way to handle both?
Hi Kevin,
the destination content and html5 content behaves differently when it is deployed.
When an html5 app-host is deployed the previous content is completely erased and replaced with the new content. Therefore sharing an app-host does not work.
However sharing a destination service instance works, as deploying destinations does not delete other destinations. Only destinations with the same name would be overwritten, which can be disabled with the flag you have mentioned.
However I have already faced that this prevents you from updating destinations e.g. if the OData service has changed.
Yes a central XSUAA is sufficient for most use cases. Splitting it should only be done if needed. I assume this is rather for bigger projects where there is no single security person but every development team has one.
Regarding the roles, I am not a complete expert. As far as I know roles from Launchpad configuration appear as role collections in cockpit and you can assign your XSUAA roles to it. I am not sure if it is possible to create role collections via XSUAA, which can be used as role in launchpad.
Best regards
Matthias
Hi Matthias,
Is it possible to onboard a Fiori Launchpad in "A subaccount" and add Fiori applications that are deployed in "B subaccount"? Or we've to deploy de Fiori app in "A subaccount" to add it to Fiori Launchpad?
Regards,
Alfredo
Hi Alfredo,
the FLP service only considers destinations from the same subaccount for finding the html5 applications. However there might be some options which I can't describe in details now:
Best regards
Matthias
Do you need to deploy UI5's this way? Or can you also use the UI5 applications On-Premise linked to a SAP Launchpad Service?
No UI5 is not deployed but consumed from a Content Delivery Network URL (sapui5.hana.ondemand.com which is based on Akamai).
It is possible to integrate UI5 applications from an OnPremise system into the Launchpad service, but this is not in scope of this blog.
I know it is not in the scope of this blog, but received an answer nonetheless :). Thank you.
Very good blog post.
Is it possible to make a Fiori Elements app "reusable" for other Elements and Freestyle apps in the same way?
So far, my attempts for Elements have always failed. The documentation is a bit sparse, specifically it would be about an oData v4 in an oData v4 app. [Docu: https://ui5.sap.com/#/topic/d869d7ab3caa48b2a20dc20dfa2483809]
Hi Andre,
thank you.
Honestly I am not an expert in that topic. It sounds like embedding a reusable UI Component into a main app and the reusable UI Component is based on Fiori elements.
Maybe it is possible, but unfortunately I can't provide details.
Best regards
Matthias
Hi Matthias,
First of, thank you very much for such an enlightening blog series, I found it very beneficial and followed for a case however ended up with an issue, I would so appreciate it if you would have a comment onto it.
I have create a separate MTA project called "cross-mta-lib-scanner" for a scanning third-party library to be a provider to another MTA project with a CAP application.
First I have created the library project on my trial account and tried to consume it with a separate dummy MTA CAP application. It worked like a charm on Launchpad! Once again thank you for showing us how to do that! You may see how the dummy project has consumed and found the library path as below.
I don't have any question for this part, I am only sharing this information with the hope that it might help to my following question.
Dummy MTA CAP Project name: crossapplibcap
MTA Lib Scanner Project name: cross-mta-lib-scanner-solution
MTA Lib Scanner namespace: comcbslibscanner
Dummy MTA CAP Project consuming the MTA Library Scanner
Once I saw that the consumption and provider projects are working fine, I deployed the MTA Library Scanner project to our development environment, and again the services and everything being generated perfectly smooth.
However once it came to invoking this library from our actual running CAP application, it didn't work. It works fine on local run from BAS, but doesn't work on Launchpad service..
As you can see in the below error message. Launchpad service tries to invoke the library out of sap resources link ("https://sapui5.hana.ondemand.com/1.114.0/resources/com/cbs/lib/scanner/library.js"), which is fully incorrect. And also as can be seen below, the custom library did not bind with its project folder as how it has been bound in the above example.
Please see the mta.yaml binded with cross-mta-lib-scanner-destination-service as following:
UI5.yaml from the application folder:
I highly appreciate for any comment.
Thanks in advance,
Merve
Hi Merve,
thank you for the warm words.
On a first glimpse it looks good, but I don't have the complete picture. I assume that on your destination service instance, the destinations to the reuse library are not working.
Could you open the dashboard of the destination service instance and check which destinations exist there?
I can try to check from our side, but need to know on which Cloud landscape you are on (on EU10 I could not find it).
Best regards
Matthias
Hi Matthias,
Thank you for your fast response.
The Cloud landscape is EU20. You may see the library dependent destination service binding as follows:
I also can see it is binded to the application service:
However as I wanted to display the cross-mta-lib-scanner destination service Dashboard I am getting this below page:
Best,
Merve
Hi Matthias,
In addition to my previous reply, could that be the proxy an issue here?
I am reffering to this below tweet from Volker Buzek. Since my issue here is that the custom library is trying to be invoked under sap references (which shouldnt be the case), could that be proxy causing this? I followed the tweet and appended /sap* to the ui5 path for the fiori-tools-proxy declaration, however it didnt help either :/
https://twitter.com/vobu/status/1433013956789088259
Best,
Merve
Hi Merve,
the Fiori Tools Proxy is only relevant for the local preview, but your problem occurs after deployment.
I had a look into your instance on EU20 and it only has one app-host connected, which is the one containing the CAP app. The other app-host with the library is not connected.
Please check which destinations exist on the instance. When you have the service instance details screen as in the screenshot above, click on Manage Instance. Then you should be able to navigate to the contained destinations.
Have you split the XSUAA or is one XSUAA instance reused? If you have split it, there must be 4 destinations in total (2 for both app-hosts and 2 for both xsuaas).
If one XSUAA is shared, there should be 3 destinations If so please also verify if all SAP Cloud Service Names are aligned and do not differ.
Best regards
Matthias
HI Matthias,
Thank you for your return, we appreciate it a lot! I have followed xsuaa-split solution so therefore we have 4 services binded to the destination-service, please see below.
Apparently the app still searches for the old destination-service "baustellen-management-destination-service". We realized that with you pointing to "the instance seem only one app-host connected" (thank you for that).
So what happens is that, before the cross-mta-lib-scanner project came up, this baustellen-management app was connected to its own destination-service, called "baustellen-management-destination-service", which has the instance ID you saw in my previous post starting with "fcaa7...".
With the cross-mta-lib-scanner integration, this baustellen-management-destination-service became obsolete. It has been replaced with "cross-mta-lib-scanner-destination-service" in our mta (you may see above) to make the library project and the baustellen-management project look towards the same destination-service. However, apparently it seem from the console log, the app still tries to be connected to the baustellen-management-destination-service which instead should be "cross-mta-lib-scanner-destination-service" with the instance ID "b50a3...".
We have a look with the colleagues into it but couldn't find anything that needs to be updated on the mta file nor any cache issue we saw. Would you kindly guide us what could be missing, or is there any specific way to clear the cache? I appreciate it if it is ok from your side, I would like to share my personal email here to be able to share our instance Id with you, we would be so happy if you could kindly contact with me (gulmmerve@gmail.com). If not, any help from here is eventually very welcome!
In total, we have 3 app-hosts. You can ignore the very bottom one with device-management.
You may see all the services relevant to these both MTA projects.
As stated above, "baustellen-management-destination-service" became obsolete and replaced with "cross-mta-lib-scanner-destination-service".
This is the obsolete dest-service:
This is the active dest-service:
cross-mta-lib-scanner-destination-service / Manage Instance (4 services (2 for app-host, 2 for uaa)):
Console log, the old dest-service "fcaa.. (baustellen-management-destination-service)" can be seen as the connected dest-service:
Hi Merve,
possibly you have to remove the obsolete service instance. The reason is that possibly you have deployed an app with the same Id there.
Portal does not support multiple apps with the same Id, which might occur here. In such cases it does no longer sync. Therefore I recommend to drop the obsolete instance and then sync the HTML5 provider in Portal again. Also set up the Portal app for your new deployment.
Best regards
Matthias
Hi Matthias,
Thank you for your suggestion, we removed the obsolete service and then everything worked fine. Highly appreciated for your help! However there are couple of questions I would like to ask your opinion regarding this work.
Initially (as also described above) we made the library's (cross-mta-lib-scanner) destination service as the main (managed) destination-service and binded the consumption app's (baustellen-management) service to this destination-service with making the previous destination service obsolete/overruled. However at the last step, we realized that we needed a srv-api definition for consumption app. Since the managed destination service is in the library project, we turned to its mta.yaml file (I raised a question about this below). In order to created the srv-api in a quick manner, we even defined srv-api with setting the launchpad URL explicitly (since there is no srv in this file, there is no srv-url to read the launchpad url). However, even with that, the srv-api is not created. So this issue brought couple of questions:
Below definition of srv-api did not end up with a creation.
Currently the definition of destination-service in the library project:
Can we later append additional destination services like as follows?
A huge thanks from me and my colleagues once again for this blog series and guiding us on our issue. We look forward to hear from you and wish you a nice week ahead!
Best,
Merve
Hi Merve,
great that it works now.
Regarding your question, above you write:
Srv-api is a service of consumption app. But the destination-service definition is in the library mta project. And this srv-api has to be defined with the destination-service, which means the srv-api of the consumption app has to be defined in the library mta.yaml file.
Actually, this is not the case. You can define the destination in the MTA which semantically owns it and also defines the srv-api instance. Also in my sample any MTA can contribute destinations to the shared destination service instance. Just check the colored picture under "Split the MTAs". The green and purple destinations are defined in the consuming app MTAs.
This works as long as all the srv-api instances that are used by the single apps are from different services and define own sap cloud service names. This will ensure that the routing to the correct service instance works smoothly.
In case you use the same service and want to bind multiple instances of it to the different apps, you might need another split which is not described, yet. There you would split the destination service instance and have one destination service instance per group of apps that consume the same srv-api. There won't be a shared destination service instance anymore.
Best regards
Matthias
HI Matthias,
Thank you for your reply, and helping us to better comprehend how BTP works under the hood.
For the "this is not the case. You can define the destination in the MTA which semantically owns it and also defines the srv-api instance" part, I believe we didn't define the srv-api correctly for the first try and gave up a little quickly because of the time pressure and switched to library mta.yaml file. Since the issues are now resolved, I can give it a try as a separate task. Thank you for confirming that it is possible.
I really wondered how the split "having one destination service instance per group of apps" could be managed. Maybe if we are lucky enough you would write this as the 4. blog, will be looking forward to that 🙂
Many thanks for all the contribution,
Wish you a very nice week ahead!
Best,
Merve