This is a proposal for a major restructuring of the openedx-platform (formerly edx-platform) repository. I’d originally posted it as a GitHub issue but I’m moving it here to broaden the discussion. Once we have some consensus, I can write this into an ADR and get started on implementation.
Outstanding issues
There is no defined Python API for openedx-platform. Yes, we do have other backend APIs already: openedx-events, openedx-filters, xblock, and soon openedx-learning (a.k.a. learning). These APIs will always be the most stable way to plug into the platform. However, the most advanced plugins will still need to be able to call core openedx-platform functions: enroll a user, create an content library, get the list of organizations, etc. In the current state of affairs, plugins will call those core functions anyway, and then frequently get broken as we develop openedx-platform.
The split between LMS and CMS is more trouble than it is worth. The platform contains bespoke tooling to handle the fact that it is two applications wrapped into one: there is our special manage.py file, the two separate celery configs, the two parallel settings trees, and so on. Each of these has caused a difficult-to-debug production issues at some point. And although we get some architectural benefit from separating LMS (learning) and CMS (authoring) domains, we find that real-world apps usually overlap between these domains, so we end up either with most apps being “shared” between the two sides, or being awkwardly split into 2-3 pieces (see discussions, discussions, and discussions
).
Prior art: how openedx-learning solves this
The Learning Core, a.k.a. openedx-learning, organizes all of its stable Python APIs into modules within openedx_learning.api (example usage). Those API modules simply re-export functions which are defined within openedx_learning.apps. Everything within “apps” is an implementation detail–we maintainers are free to refactor “apps”, as long as we keep “api” stable.
We’re able to maintain good architectural boundaries between the “apps” well orangized by using importlinter rules . The rules specify a layering order, so that low-level apps like “publishing” are not allowed to become dependent on higher-level apps like “backup_restore”. (Note: we’ve begun trying to do this in openedx-platform already, too).
I think this has been working very well, so I propose we adopt something similar for openedx-platform.
Proposed solution for openedx-platform
-
Create a new directory tree:
./openedx_platform/./openedx_platform/api/– All public Python APIs. We avoid breaking changes to these APIs, and use the DEPR process & a major version bump when they’re unavoidable. There would be no implementations in this folder, just imports and re-exports of functions fromapps.- The APIs would be organized into sub-modules, like:
./openedx_platform/api/content_libraries.py./openedx_platform/api/xblock.py./openedx_platform/api/rbac.py
- and certain database models would be allowlisted on a case-by-case basis, e.g.:
./openedx_platform/api/content_libraries_models.py./openedx_platform/api/xblock_models.py./openedx_platform/api/rbac_models.py
- The APIs would be organized into sub-modules, like:
./openedx_platform/apps/– Future home for all implementations, including models definitions. We would use importlinter’s layering and independence rules to ensure good architectural relationships between different apps../openedx_platform/settings/– Future home for Django settings.
-
Begin building out
./openedx_platform/api/, starting with Python APIs that we feel are already near-stable, such as content_libraries. -
Publish openedx-platform to PyPI, starting at v1.0.0.
-
Announce that external imports to all other paths (
./lms/,./cms/,./openedx/,./common/, and./xmodule/) are deprecated. Plugins relying on these imports should move to public Python APIs. In tandem, we will encourage development of new stableopenedx_platform.api. -
After some grace period, begin moving code from the old implementation directories (
./lms/,./cms/,./openedx/,./common/, and./xmodule/) into the new one (./openedx_platform/apps).
What do you think?
This would have both large benefits (more stable APIs, and a more organized monolith), but also a lot of disruption (breaking changes to existing plugins, very painful rebases for forks once we start moving code). I believe the benefits outweigh the intermediate pain, but I want to hear from all of you. Let us know your thoughts below!