Micro-frontend (MFE) Onboarding
Purpose
Open edX has developed a new architectural strategy of implementing micro frontends (MFEs) for the platform’s web UI. MFEs are implemented in React and are loosely coupled with Open edX platform instead of the previous tight coupling of implementing web UI within edx-platform itself.
This document is intended to provide information about MFEs to developers who are new to either edX or our new MFE direction.
Introduction
Relevant Repos
A number of Github repositories are used to develop and deploy the MFEs:
GitHub - openedx/frontend-build: Common build scripts and tooling for Open edX micro-frontends.
Common place for configuration needed to build an MFE, such as webpack and lint configs.
GitHub - openedx/frontend-platform: A framework for Open edX micro-frontend applications.
Lightweight framework for all Open edX MFEs, providing foundational services such as logging, analytics, and authN.
API Docs are here: https://edx.github.io/frontend-platform/
Pattern library containing accessible React components for use in Open edX UIs.
GitHub - openedx/brand-openedx
Default branding assets and style used in Open edX applications.
Default branding assets and style used in edx.org applications.
Github template repository for creating new Open edX MFE applications.
GitHub - edx/tubular: A repo for edx pipeline related scripts.
Contains deployment scripts used by all MFEs.
https://github.com/edx/edx-internal
Contains GoCD deployment pipeline configuration.
Contains stage- and prod-specific frontend configuration overrides.
https://github.com/edx/edge-internal
Contains edge-specific configuration frontend configuration overrides.
GitHub - openedx/frontend-component-header
Shared React header component for Open edX MFE applications.
GitHub - openedx/frontend-component-footer: Site footer component for edX frontend apps.
Shared React footer component for Open edX MFE applications.
GitHub - edx/frontend-component-header-edx
Shared React header component for edX instances of MFE applications. Typically applied at build time through an NPM alias defined in
edx-internal
.
GitHub - edx/frontend-component-footer-edx
Shared React footer component for edX instances of MFE applications. Typically applied at build time through an NPM alias defined in
edx-internal
.
All the top-level MFE repos, shown here:
Relevant Docs
Each repository above contains a helpful README file.
Frontend How-To - but specifically: Micro-frontends How To
https://openedx.atlassian.net/wiki/spaces/FEDX/pages/1917944547 to see what it takes an MFE from “Planning” to “Done”.
https://openedx.atlassian.net/wiki/spaces/FEDX/pages/1920270629 to see the status of our many frontend features.
Open edX Developer Docs: https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/developers_guide/micro_frontends_in_open_edx.html
Architectural decision records (ADRs) are in the
docs/decisions
directory of each frontend-app-* repo.
Development
To develop MFE code, two methods currently exist to test code changes locally. (Both methods assume that you have a working Open edX devstack locally on your development machine.)
Using Docker devstack
devstack has Docker containers which are regularly built for the following MFEs:
frontend-app-learning
frontend-app-payment
frontend-app-publisher
frontend-app-library-authoring
frontend-app-program-console
So those MFEs can be updated/started up in the usual devstack way:
make dev.pull.frontend-app-learning
make dev.up.frontend-app-learning
Then the development workflow can occur in the usual way - updating code and waiting for an MFE reload, attaching to the container to see output, etc.
Using Node.js
(Use this workflow if your MFE has not yet been built as a devstack Docker container or simply if preferred.)
The development workflow for using Node.js to update code is described in each MFEs README file - here’s an example. You would first install Node.js v12 locally and then git clone
the MFE repository to your local machine. To start up an MFE, use these commands:
npm install (install dependencies)
npm start (start development server)
npm run lint
npm run test
npm run build (build static output)
Each of these build targets is defined in the package.json file of each MFE repo.
Enabling the new MFE Experience
Most MFEs are replacing web UI that’s integrated into the current platform. For some MFEs, waffle flags have been added to disable/enable the usage of MFE code, in order to release the MFE in an orderly, controlled fashion. You’ll need to find which waffle flags are involved and set them appropriately locally to enable the MFE to be properly used by the platform.
<TODO: List appropriate waffle flags.>
Configuration
Open edX MFEs expect a number of process environment variables (accessible in Node.js via process.env
) to properly configure authentication, route to other parts of the system, and more. Each MFE repository has the following environment variable files that are consumed by the MFE code:
.env
Contains
null
values onlyNot used in production MFE builds
.env.development
Contains values for development via devstack
.env.test
Contains values for running tests via devstack
Process environment variables are processed using the dotenv package. This package has a limitation of only supporting string values, which means:
Variables which are numbers need to be type-converted.
Variables which are lists will require splitting the string value on a chosen delimiter to form the list.
Building an MFE
Configuration
The build process supplies the actual production values via the command line prior to running npm run build
, because the dotenv webpack plugin will use any environment variable passed in on the command line first - then use the .env files as fallbacks.
Steps of the build process:
.env
variables are read by webpack.The dotenv webpack plugin performs string interpolation on the code during a webpack run, replacing any code that says “
process.env.VARIABLE_NAME
” with the actual value set in the build environment.The base frontend-platform repository has a config.js file where all the known process.env variables are listed out:
As webpack processes the code, each process.env variable in config.js gets filled out with the correct value.
This process exposes all config variables in a single object to the app itself.
To retrieve a config variable in the apps, the code makes this call:
getConfig().LMS_BASE_URL
(for example)
Other
An MFE contains several different types of files, including:
Images
JS
CSS
SASS
SVG
In building an MFE, webpack is used to process all the JS files into one big artifact. But webpack also supports:
Allow webpack to process the other, non-JS file types as well
Allow the behavior of the build to be changed by using build lifecycle hooks.
Used for minifiers, analytics, etc.
A MFE build is invoked by running: npm run build
Dependency Management
MFE dependencies are managed via npm. Each MFE repo contains a package.json
file which contains the pinned npm dependencies for the MFE. Downloaded dependencies are cached in the node_modules
directory.
But sometimes you’ll need to override those pinned dependencies when developing/debugging locally. To do so, use the module.config.js
file in https://github.com/edx/frontend-build#local-module-configuration-for-webpack . This file is a home-grown config file that allows overriding/addition of packages when performing local development of an MFE.
For example, one can use module.config.js
for:
getting the http://edx.org branding for your MFE (which defaults to open edX branding)
getting the http://edx.org headers/footers for your MFE
How To Add a New Config Value
Occasionally, a new config value will need to be added to support some new configuration. Adding this new config has a couple of stages:
frontend-platform Addition
The frontend-platform repo contains code to take process environment values and place them in an MFE-accessible config. For process environment variables to become part of the MFE config, they need to be added to the code here.
MFE Addition
To use the new MFE config value in development, the value must be added to the .env*
files in the MFE itself, particularly .env.testing
and .env.development
.
edx-internal Addition
To add the new config value to the production configuration, the value must be added to the appropriate YAML file in the edx-internal repo. For example, this PR adds the new IGNORED_ERROR_REGEX
value to the frontend-app-learning production configuration.
Deployment
How are MFEs prepared for - and operated in - a production environment?
Configuration - Prod
Production environment variables which end up accessible via getConfig()
are stored in YAML files in the edx-internal Github repo. For example, frontend-app-learning uses these two YAML files as its production configuration:
https://github.com/edx/edx-internal/blob/master/frontends/common/prod_config.yml
https://github.com/edx/edx-internal/blob/master/frontends/frontend-app-learning/prod_config.yml
The YAML files are read and transformed into environment variables which are set in the environment before running the npm run build
statement.
Deploying
MFEs are deployed by GoCD. The pipelines are here: https://gocd.tools.edx.org/go/pipelines#!/frontend
Each MFE typically has a stage pipeline and a prod-edx pipeline. Some MFEs are also on prod-edge and have a separate prod-edge pipeline.
The deployments are all performed via two scripts:
https://github.com/edx/tubular/blob/master/tubular/scripts/frontend_build.py
https://github.com/edx/tubular/blob/master/tubular/scripts/frontend_deploy.py
The YAML definitions for the GoCD pipelines can be found here:
Cloudflare
But to where are the MFEs deployed? A built MFE consists of static files which are pushed to AWS S3 and are cached to a content distribution network called CloudFlare. The CDN serves the files to the requesting browsers and treats S3 as its origin.
Each MFE is has its own subdomain, such as:
Cloudflare points all URLs for the subdomain to index.html.
Monitoring
MFEs are listed in NewRelic as “Browser applications”. Here’s an example summary page: