Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Add slot naming and life cycle ADR #99

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions docs/decisions/0003-slot-naming-and-life-cycle.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
====================================
3. Plugin Slot Naming and Life Cycle
====================================

Status
======

Proposed


Context
=======

The Frontend Plugin Framework introduced the concept of plugin slots as a way
to customize micro-frontends. Slots are defined in each application's codebase
with React, currently taking the form:

<PluginSlot id="arbitrary_slot_name">
...
</PluginSlot>

Operators can subsequently insert plugins into this slot by referencing
"arbitrary_slot_name" in configuration as follows:

pluginSlots: {
arbitrary_slot_name: {
plugins: [{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'arbitrary_plugin_name',
...
}
}]
}
}

However, the following concerns were identified in relation to completely
arbitrary slot names:

1. The codebase can become progressively littered with slot names that are
unintuitively or inconsistently named, making it harder to document,
maintain, and use them

2. There is no expectation that one should be able to infer purpose and
location from the slot name

3. While Frontend Plugin Framework supports defining multiple slots with the
same name in a frontend app, as the number of slots across the codebase
increases it becomes harder and harder for developers to avoid introducing
accidental name collisions

4. Without a versioning scheme, there's no way to modify a slot's API without
making an implicit breaking change

This is a common problem in computer science, one that has often been addressed
by use of `reverse domain name notation`_. It can be seen everywhere, from
Android package names to Open edX's own specification for `server event
types`_.

.. _reverse domain name notation: https://en.wikipedia.org/wiki/Reverse_domain_name_notation
.. _server event types: https://docs.openedx.org/projects/openedx-proposals/en/latest/architectural-decisions/oep-0041-arch-async-server-event-messaging.html#id5

This technique allows for namespace uniqueness within a self-documentated
hierarchy. For instance, take this fictitious slot name that uses said
notation:

org.openedx.frontend.layout.header.v1

Even without further information, it's possible to tell that:

* The slot belongs to an app in the Open edX org
* It's a frontend app
* It's in the app's layout module
* The slot probably wraps the header
* This is version 1 of the slot, which indicates changes are possible in the
future

And last but not least:

* There's little chance that a slot with the same name exists anywhere in the
codebase other than where the layout header is defined

Based on this concept, this ADR aims to define rules that govern how developers
maintain plugin slots in Open edX frontend apps throughout their lifecycle. In
particular, when adding, deprecating, or removing plugin slots.


Decisions
=========

1. Naming format
----------------

The full name of a plugin slot will be a ``string`` that follows the following
format:

{Reverse DNS}.{Subdomain}.{Module}.{Identifier}.{Version}

Where:

* *Reverse DNS* is always ``org.openedx``
* *Subdomain* is always ``frontend``
* *Module* denotes the frontend module where the slot is exposed, such as
``courseware``, or ``authoring``
* *Identifier* is a snake-case string that identifies the slot, which must be
unique for the module that contains it
* *Version* is a monotonically increasing integer prefaced by a `v`, starting
with `v1`.

For example:

* org.openedx.frontend.layout.footer.v1
* org.openedx.frontend.courseware.navigation_sidebar.v2

2. Versioning
-------------

For the purposes of versioning, a given slot's API contract is comprised of:

* Its location, visual or otherwise, in the Module
* The type (but not implementation!) of the content it is expected to wrap
* The specific set of `pluginProps` it exposes

If one of the above changes for a particular slot in such a way that existing
plugins break or present undefined behavior, *and* if it still make sense to
use the same Identifier, the version string appended to its name will be
incremented by `1`.

Note: a given slot's default content is explicitly *not* part of its contract.
Changes to it do not result in a version bump.

3. Deprecation process
----------------------

When a slot changes sufficiently to require its version to be incremented, the
developer will take care to:

* Propose the previous version's deprecation via the official Open edX
Deprecation Process

* Keep the definition of the previously released version of the slot in the
codebase for the duration of the deprecation process, which should include at
least one Open edX release where it co-exists with the new version

* Implement the new version of the slot in such a way that coexists with the
previous one with no detriment to either's functionality


Consequences
============

The decisions above are intend to let plugin authors create and maintain
plugins that are stable across releases of Open edX, while also allowing slots
themselves to evolve. The naming convention itself has no significant
downsides, and while the deprecation process does add some maintenance burden,
it is expected to be offset by the additional stability provided.
Loading