How to use microfrontend in a multitenant instance

Hi all,

We start thinking about how to use the micro-frontend in a multitenancy installation, we want to share with you our first iteration, get feedback and find a way to make a solution for the community.

The proposal

Frontend-platform recommended extending the initialization handler for config runtime, with that in mind, we think to get the site configuration values through a REST API with a new handler (don’t touch config handler to maintain env config and the actual workflow, where each MFE could override it, for example, in account).

In this case, we don’t have to modify the base code on each MFE.

Test Environment
This proposal was tested in a Tutor Maple dev installation with these plugins:

  1. tutor-mfe : " easily add micro frontend (MFE) applications on top of an Open edX platform that runs with Tutor"

  2. eox-tenant " a multi-tenancy Django app for edx-platform"

In addition, we use a mock API (http://localhost:3000/config/tenant_id) with some config information, which “common” is a config for all MFE

{
     {
      "id": "tenant-a.lms.mango.edunext.link",
      "common": {
        "SITE_NAME": "Tenant A",
        "LOGO_URL": "https://www.example.com/tiger.png",
        "LOGO_TRADEMARK_URL": "https://www.example.com/tiger.png",
        "LOGO_WHITE_URL": "https://www.example.com/tiger.png",
        "FAVICON_URL": "https://www.example.com/tiger.png",
        "LEARNING_BASE_URL": "http://tenant-a.lms.mango.edunext.link:2000",
        "LMS_BASE_URL": "http://tenant-a.lms.mango.edunext.link:8000",
        "LOGIN_URL": "http://tenant-a.lms.mango.edunext.link:8000/login",
        "LOGOUT_URL": "http://tenant-a.lms.mango.edunext.link:8000/logout"
      }
    },
    {
      "id": "tenant-b.lms.mango.edunext.link",
      "common": {
        "SITE_NAME": "Tenant B",
        "LOGO_URL": "https://www.example.com/cat.png",
        "LOGO_TRADEMARK_URL": "https://www.example.com/cat.png",
        "LOGO_WHITE_URL": "https://www.example.com/cat.png",
        "FAVICON_URL": "https://www.example.com/cat.png",
        "LEARNING_BASE_URL": "http://tenant-b.lms.mango.edunext.link:2000",
        "LMS_BASE_URL": "http://tenant-b.lms.mango.edunext.link:8000",
        "LOGIN_URL": "http://tenant-b.lms.mango.edunext.link:8000/login",
        "LOGOUT_URL": "http://tenant-b.lms.mango.edunext.link:8000/logout"
      },
// example add optional variables or specific for a MFE
      "auth": {
        "INFO_EMAIL": "diana.olarte@edunext.co"
      }
}

Steps to follow

In frontend-platform

We fork frontend-platform for testing purposes and make some modifications (you can look it out below), following the next steps:

  • Create a new config variable to know if is a multitenant instance or not; this could be set in the env settings (like default config).
 MULTITENANT_API: true
  • Create a function (getHttpConfig) to call the API with the config. We think it’s a good idea to use the cache system.

  • Create a handler for the initialize method (we call that configTenant).

  • Add validation in the initialize to call one of the handlers: config or configTenant.

You can check the first version of the code here (we’re happy to get feedback :slight_smile: )

Note: We use module.config.js to develop these changes and test frontend-platform.

In the tenant config of Django Admin

We found out it’s possible to config the URL through the tenant settings like this:

{
    "ACCOUNT_MICROFRONTEND_URL": "http://tenant-b.lms.mango.edunext.link:1997/account",
    "ENABLE_PROFILE_MICROFRONTEND": true,
    "LEARNING_MICROFRONTEND_URL": "http://tenant-b.lms.mango.edunext.link:2000/learning",
    "LMS_BASE": "tenant-b.lms.mango.edunext.link:8000",
    "LMS_ROOT_URL": "http://tenant-b.lms.mango.edunext.link:8000",
    "PLATFORM_NAME": "Tenant B",
    "PROFILE_MICROFRONTEND_URL": "http://tenant-b.lms.mango.edunext.link:1995/profile/u/",
    "SITE_NAME": "Tenant B",
}

That’s why we can use the hostname to identify the tenant and the MFE (place in the path URL) to set the specific additional configuration in needed cases.

Finally

Use a mock API with the config of the tenant you want to test (like we explained in the test environment section), change the URL in getHttpConfig to match with your API, and test it!.

The result
We want to share a screenshot of 2 tenants (the result of our test), with different values gotten by an API (mock) in the same MFE:

  • The URL
  • Logo and favicon
  • Site name

Tenant A

Tenant B

8 Likes

Hi @dcoa and welcome to the forums!! :grin:

I don’t have any feedback specifically but I want to celebrate this work. :tada: thanks for the submission!

2 Likes

Hi @dcoa:

Thanks for sharing your proposal. We recently updated to Maple version through tutor and we have doubts if this version is compatible with multi-site configurations.

I’m reviewing your post and it confirms my doubts that microfrontends does not support multi-site out-of-the-box. I think this is a big drawback and it should have been considered before put microfrontends as default option.

In the tutor discussion @regis says that tutor does not support multi-sites and we are wondering if it is a restriction or a suggestion.

We are planning to addressing diferents sites through k8s namespaces by deploying one platform per site but before to do this, we can give a chance to implement it in a single namespace.

We have some questions regarding to your proposal:

  • It is necessary the plugin eox-tenant?
  • How can be synchronized the sites created dynamically through sites django admin vs the statically created response in the mock API?

Thanks

Hi Diana!

I see you tested using different subdomains (tenant-a and tenant-b) within the same lms domain (lms.mango.edunext.link). I wonder if it will work using completely different domains for each tenant (of course all CNAMEs pointing to the same LMS url) as can be done with site configurations. I doubt if caddy can support several domain names for the same site. Did you test it?

I fully agree with @jboo that multitenancy should be given a chance. There is a huge market of low-end educators that cannot afford the cost of a full implementation, even in very optimized installations as can be done with Kubernetes.

1 Like

Hello @dcoa

I think the biggest drawback to getting it works is in how the microfrontend url is obtained in the backend.

For example, in the case of learning microfrontend, reviewing the code of the platform I see this in url_helpers.py:

  • mfe_link = f'{settings.LEARNING_MICROFRONTEND_URL}/course/{course_key}'

The LEARNING_MICROFRONTEND_URL value is not being retrieved through the site configuration by using the site configuration helpers, as it is being done in other points of the platform to obtain other values from the settings.

How did you address this in your proposal?

Thanks.

Hi @jboo and @andres, thank you for your interest in this topic.

Answering your questions:

Yes, this is a complex question, I understand Tutor does not recommend multitenancy but is possible to extend it with a plugin to achieve that, and this is one of the proposes of eox-tenant, and we don’t know if exists another alternative.

The main idea is that the endpoint connects with a service that can read the settings and that have to be used for the MFE.

No, it is not tested yet, but we are working on testing that.

I’m not sure but we are setting the URL with the settings and apparently works for tutor-mfe

1 Like

Hello,

I have opened a PR to edx-platform repository to get microfrontend URLs from site configuration. With these changes we can manage different MFE through multisites at the backend.

Still pending how to handle multiple backends from the microfrontend side. @dcoa I have linked your proposal for frontend-platform configuration.

2 Likes

Hi @jboo,
I liked your PR! Did you test it?

1 Like

Hi @jboo

We are setting LEARNING_MICROFRONTEND_URL on each “tenant config” to override the settings because currently doesn’t exist one option to override it from site configurations.

I like your PR, It might be a good solution for set it from site configurations.

Hi @andres

We were testing it and finally, we find that this is working with any sub-domain, but it depends on setting SESSION_COOKIE_DOMAIN.

Currently, Tutor defines it using the same LMS_HOST on this case “lms.mango.edunext.link”.

Maybe We can do it works with a different domain but as you said, we will need some change in caddy, or maybe changing the cookies settings might be enough, We’ll work on it.

@andres @Alecar thanks for your likes :wink:

@Alecar as you say, you have to configure SESSION_COOKIE_DOMAIN in site values, overwriting the default value and assigning the same value as site domain’s name, but I think this is not due to the changes introduced in the PR.

Regarding to the frontend part, we are planning to address this part by deploying as many copies of MFEs as sites, each one properly configured to the corresponding domain. Since we host MFEs in S3 it will not add an extra cost in infrastructure, but it does force us to do a new deployment every time we create a site. @dcoa with your proposal we can avoid this drawback, how did you plan to implement this part?

Anyway, I think it’s not a bad idea, despite the fact of making a new deployment for each tenant. This approach will allow to make a customized MFE for each one.

There was some talk about this very topic in the BTR WG during the conference and we took this a compromise to deliver some of this work soon.

I made a summarizing document at: MFE Runtime Configuration - Google Docs

Please take a look, comment and let me know what needs changing or more discussion or if you are happy with the approach.

1 Like