Overview
The section can describe the outcomes that we're striving for with Frontend Pluggability. By outcomes, I mean, how do we think this will help developers, course teams, and users?
You may choose to include goals and non-goals as well.
Also, the high-level section can provide a few concrete examples to help the reader understand.
Definitions
Plugin
Plugins are code that is not bundled with an application, but is built/deployed separately and sandboxed in an iframe at runtime. There are a number of sub-classifications of Plugins along a number of different axes:
Slot - Where the plugin is displayed on the page
Config - Which plugins should be loadedHow the plugin is configured
Discovery - How does the application know its config?
Hosting - Where the plugin is deployed changes what it is allowed to do
Deployment - What kind of deployment artifact the plugin is bundled into
All of these are broken down below into their various sub-classifications, which roughly correspond to increasing levels of effort.
Supporting Definitions
A few key terms used in this document.
Site
A website like edx.org. A second-level domain (SLD).
Platform
The platform (edx-platform et al) is deployed to a Site.
Application
An application is a micro-frontend application deployed on a sub-domain of the Site. Applications are built by compiling static assets and deploying them to the web along with an index.html file to load them.
Slots
A Plugin is loaded in a Slot on the page. A slot is a particular mount point in the page (DOM) where the plugin should visually appear. Different slots will have different expectations for their dimensions. Some may grow vertically, horizontally, be statically sized, take over the page, exist in a modal, etc. A given slot may be configured to include multiple plugins. Plugin authors are responsible for understanding the requirements of the Slot in which they expect their plugin to appear, and those requirements will be communicated in documentation.
Examples:
Header
Footer
Sidebars
Below Sequence Nav
Course Tabs
Plugin Configuration
Plugin configuration is done once prior to usage of a plugin. It can be performed by course teams, developers, or operators. The configuration may be changed, of course, which will affect subsequent renderings of the plugin at runtime. Plugin configuration is primarily concerned with helping an Application determine which plugins should be loaded, where, when. It is not concerned with how those plugins choose to load their content.
Configuration as Code
This is effectively a hand-coded configuration document. It's easy to produce but error prone. Because it's code, it will generally need to be updated by a developer.
Configuration via UI
This is plugin configuration that is created via a UI, serialized down into a configuration document like the above, and is handed to the application so it can configure itself. The distinction here is that this is a level of effort above configuration-as-code. It's harder to produce, but easier and safer to use.
Context Sensitivity
Allows plugins to be loaded under certain circumstances. For example, loading a plugin only on a specific course, or for particular classifications of users. Put another way, it implies letting the Application use its Context data (see below) in order to determine when the plugin should be loaded, not just where.
This is a heavy lift, as we expect that for practical purposes, it will be difficult to use without first implementing Configuration as UI.
Note that “context-sensitive” is different than being “context-aware”, which is described below in the Plugin Context section.
Plugin Discovery
The process an Application follows to determine what plugins it should load.
Build-time
This is taking a static or dynamic plugin configuration and handing it to the Application at build-time so that it "knows" it's plugin configuration immediately upon load. This potentially delivers a modest performance boost by reducing request overhead.
Run-time
This is plugin discovery performed at run-time, as an Application is loading, via a separate call or configuration file download. It may be modestly slower than build-time plugin configuration because of the HTTP overhead of the extra download. It's also more flexible.
Plugin Context
Information handed to the plugin after it has been loaded to help it understand how to display itself. This may include:
Application name
Page name / URL
Slot name
User ID
Layout information (width, height)
Particular applications may have application-specific context they want to add in as well. In the case of courseware Applications, for instance, we’ll want to inform the plugin of:
Course ID
Chapter ID
Sequence ID
Unit ID
To reduce back-and-forth, basic plugin context information can be supplied as query string parameters on the initial load request. This is what LTI does. However, to avoid running into predictable issues with URL length, application-specific context information should be transferred via Events (described below). We can think of this distinction as helping the plugin do progressive rendering; basic context information should be enough to orient the plugin as to where it’s being displayed, whereas application-specific context information is used to fill in content.
As a side note: We’ve been using the term “context aware plugins” - this is an unnecessary qualification, as all plugins are aware of whatever context we choose to share with them. The host page and our framework controls how much context to give plugins. It’s not clear that there are any use cases for providing different amounts of context to different plugins, however.
Plugin Events
Plugins may communicate with the host page (and vice versa) via a pre-defined set of events. Each event has a type and an agreed-upon payload schema. These events will be delivered via the postMessage API. We may add to the set of pre-defined events as we discover new plugin use cases.
Lifecycle Events
There will be a number of lifecycle events specifically to manage a plugin’s loading and rendering process. Because the host Application and the plugin can only communicate through the asynchronous postMessage API, we’ll need a set of events published by both sides to inform and properly sequence overall lifecycle state. As examples, we’ll likely need events related to: plugin loading, transferring context data, ready, content resize, and unloading.
Plugin Hosting
Internal
If a plugin is deployed as part of the Site and is hosted on a sub-domain of the Site, then it will have access to the Platform's authenticated APIs by virtue of having a valid JWT token.
External
If a plugin is hosted on a separate domain, then it will not have access to any Platform APIs. It will identify users and other resources via messages passed into it from the host page. If this is insufficient for it to implement its functionality, then it needs to become an Internal Plugin or an LTI Plugin.
LTI (Sub-classification of External)
To allow externally hosted plugins to have access to authenticated Platform APIs, the plugin must conform to the LTI specification and be loaded in an iframe. This means the plugin must implement an OAuth/JWT authentication flow according to the LTI spec.
Plugin Deployment
Plugins can be deployed in several different ways that affect their use cases.
Micro-frontend Only
Micro-frontend-based plugins have no backend. In that sense, they have limited ability to persist data. Deployments of the other types may make use of a micro-frontend for their frontend layer - this category is specifically for deployments that have no backend component.
Micro-service
Plugins that are served/associated with a stand-alone backend service, deployed independently from the Platform. If such a plugin has a micro-frontend layer, that is presumably still deployed separately.
Django App Plugin
A plugin that is deployed as part of the Platform via the Django App Plugin mechanism. If such a plugin has a micro-frontend layer, that is presumably still deployed separately.
xBlock
A plugin based on an xBlock. Today, xBlocks were designed to be used in the actual course content, not in secondary/supporting functionality on the page. However, they may just work; some investigation will be required.
LTI xBlock
The LTI xBlock allows LTI tools to be loaded into an xBlock with the caveats described above. i.e., they can only be used on course content, not in supporting UIs.
Draft Development Milestones
Taken in all their various combinations, the Configuration, Discovery, Hosting, Deployment and Config Granularity sub-types represent a wide variety of plugin types with varying degrees of functionality and flexibility.
We know that ultimately we want to get to LTI-based plugins which can be developed as xBlocks with context-sensitive configuration granularity - that’s also likely the most difficult for us to implement.
Development milestones should be developed by:
Finding a plugin we want to create. (e.g., Discussions)
Determining which functionality it needs to be minimally successful.
Implementing those plugin sub-types in support of the plugin.
Repeating these steps with other plugins that demand further features.
Try to get to LTI/xBlocks as plugins.
This makes some assumptions about which sub-types will be developed first based on our understanding of the requirements/likely development path for a discussions plugin. The phases laid out here are a draft stab at how we might get there - they’re expected to be tweaked and changed depending on what we find out about Discussions.
Key
In progress
Done in prior milestone
MVP Milestone
This milestone should be oriented toward an initial MVP of a discussions plugin. It should contain just enough features to make that project feasible. This plan assumes that the plugin will be internally hosted for convenience, and that it will need modest context information in order to function. It does not presume that the MVP will actually go live with this feature set.
Questions
How will the discussions plugin be built? A Django App Plugin? A micro-service? Internal? External? What discussion framework will it use? Will it be home grown?
Features Milestone
This milestone is focused on enabling dynamic, context-sensitive self-service of plugins. It will involve an administration interface, perhaps in Studio or Django admin to manage a Site’s plugin configuration. This is not currently aligned with any particular plugin implementation, but could be adjusted to be.
LTI Milestone
Finally, phase three is about enabling LTI and xBlock plugins in support of a rich, externally hosted plugin ecosystem.
Features By Phase
Milestones: | MVP | Features | LTI |
---|---|---|---|
Slots | |||
Right Sidebar | |||
Header | |||
Footer | |||
Course Tabs | |||
Configuration | |||
As code | |||
Via UI | |||
Context-sensitivity | |||
Discovery | |||
Build-time | |||
Run-time | |||
Context | |||
Basic context via query string | |||
User ID | |||
Application Name | |||
Page Name / URL | |||
Slot Name | |||
Layout Information | |||
Learning Context | |||
Application context via event | |||
Course ID | |||
Chapter ID | |||
Sequence ID | |||
Unit ID | |||
Events | |||
Lifecycle | |||
Resize | |||
Further events (Example: external link handling) | |||
Hosting | |||
Internal | |||
External | |||
LTI | |||
Deployment | |||
MFE | |||
Micro-service | |||
Django App Plugin | |||
xBlock | |||
LTI xBlock |