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:

Relevant Docs

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 only

    • Not 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:

  • Loaders

    • Allow webpack to process the other, non-JS file types as well

  • Plugins

    • 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 . 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:

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:

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:

frontend-app-learning