We will create a new "shell" MFE to act as the top-level host for all other MFEs. It is exclusively responsible for:
Initializing the application via
@edx/frontend-platform
.Loading the base, expected version of all our shared dependencies.
Rendering the "layout" of the application, including the header and footer.
Loading the brand.
Acting as the entry-point for loading MFEs at runtime and during development.
Like other hosts, it is also responsible for:
Loading all the manifests from the "guest" MFEs it intends to load.
Using module federation to load the guest MFEs on demand.
Architecture
Relationship to @edx/frontend-platform
The shell is an MFE, whereas frontend-platform is a library providing access to shared services (“platform exports” in the diagram) like authentication, an HTTP client, config, internationalization, logging, and analytics. MFEs depend on frontend-platform, but they should be decoupled from the shell application.
This means that frontend-platform stays an independent library which both the shell and other MFEs depend on. Long term (modulo maintaining backwards compatibility), the shell resembles MFEs as they exist today - initializing the app - and today’s MFEs slim down and cease to run frontend-platform’s initialize
function, as they’re just a collection of modules being loaded into the shell.
frontend-platform is a “singleton” shared dependency, meaning only one version of it can exist in the page. This ensures guest MFEs can access the shared services that the shell set up at startup time.
Guest MFEs also need to be able to access their own configuration defined at build time, or via the MFE config API. This means they need their own version of frontend-platform’s export getConfig
. To accomplish this, we will likely need to find a way to not share some small subset of frontend-platform, likely the exports in @edx/frontend-platform/config
.
Dynamic Routes
We will likely want to add a helper component in frontend-platform that marries a react-router Route
with Suspense
and a component loaded at runtime from the appropriate guest MFE.
As an example, say we have a route for the profile page: /profile
.
When we navigate to that route, we need to load the ProfilePage
module from the frontend-app-profile
MFE. The shell will need module federation configuration that describes where to find this module. The element
of the Route
will be a React Suspense
component that waits for module federation to load and supply ProfilePage
and fulfill the Suspense’s promise.