-
Notifications
You must be signed in to change notification settings - Fork 11
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: ADRs for modeling containers capability #251
base: main
Are you sure you want to change the base?
Changes from 32 commits
2906792
b1b1a88
413b66c
0deab25
22eb1ae
db61fda
b5f05d8
9d2c62d
8648f16
80cf370
b98c02c
ccada60
778ce2b
e738778
3a28fa5
bbce789
af7dce4
506d9cd
466b450
46999f8
0c426d2
46dfa9e
d37a414
64e4db9
46e5a09
822d9a0
8646225
1f1c962
f260756
86141e5
aff8b9b
76e89d5
6ecde96
f7cc446
dc5a0a2
d0f1fc8
83f8d04
8fa418e
6fd6864
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
17. Modeling Containers as a Generalized Capability for Holding Content | ||
======================================================================== | ||
|
||
Context | ||
------- | ||
|
||
This ADR proposes a model for containers that can hold different types of content and can be used to model other content types with similar behavior, such as units, subsections, sections, or courses. The model defines containers' core structure and purpose, the types of containers, content constraints, container members, version control, publishing, and pruning. | ||
|
||
Decisions | ||
--------- | ||
|
||
1. Core Structure and Purpose of Containers | ||
=========================================== | ||
|
||
This section defines the purpose and structure of containers, explaining how they are designed to hold various types of content through a parent-child setup. | ||
|
||
- A container is designed as a generalized capability to hold different types of content. | ||
- A container is a publishable content type that holds other content types through a parent-child relationship. For example, sections, subsections and units. | ||
- The generalized container capability will have its own Django application as part of the authoring application where other types of containers and content types will build on top of. For instance: | ||
|
||
- Generalized containers (containers app is lowest level of these applications) | ||
- Selectors for dynamically selecting 0-N PublishableEntities, i.e. how we're going to do things like SplitTest and Randomized (selectors application, builds on containers). | ||
- Units (units app, builds on containers and selectors). | ||
|
||
2. Container Types and Content Constraints | ||
========================================== | ||
|
||
This section defines container types, content constraints, hierarchy, and extensibility. It introduces the main types of containers and outlines how content limitations and configurations are handled at the application level to support flexible content structures. | ||
|
||
- A container marks any PublishableEntity, such as sections, subsections, units, or any other custom content type, as a type that can hold other content. | ||
- Containers can be nested within other containers, allowing for complex content structures. For example, subsections can contain units. | ||
- Containers might be of different types, with each type potentially having different restrictions on the type of content it can hold but that will not be enforced by containers. | ||
- Content restrictions for containers are implemented at the app layer, allowing specific container types, like units, to limit their members to particular content types, e.g., units are restricted to contain only components. | ||
- The course hierarchy Course > Section > Subsection > Unit will be implemented as relationships between containers, with each level acting as a container that holds other content. The hierarchy will be enforced by the content restrictions of each particular container but allowed to be overridden to support `0002-content-flexibility.rst`_. | ||
- Containers will follow extensibility principles in `0003-content-extensibility.rst`_ for creating new container types or subtypes. | ||
|
||
3. Container Members and Relationships | ||
======================================= | ||
|
||
This section defines container members, their order, and relationships, covering flexible connections and support for draft and published states of their members. | ||
|
||
- The members of a container can be any type of publishable content. E.g., sections, subsections, units, components, and any other publishable thing. For more details on publishable content, see `PublishableEntity`_. | ||
- Members within a container are maintained in a specific order as an ordered list. E.g., components within a unit, or units within a subsection, are presented in a specific order. | ||
- Containers represent their content hierarchy through a structure, like Course > Section > Subsection > Unit > Component, which defines parent-child relationships at each level. | ||
- Containers support both pinned and unpinned references for its members, allowing members to be pinned to a specific version or set to reference its latest version. For instance, component V1 might be used in a unit instead of its latest version. | ||
- The latest state of a member can be referenced by setting its version to ``None``, which consists of the standard for a floating version. | ||
- A single member (publishable entity) can be shared by multiple containers, allowing for reuse of content across different containers. For instance, a component can be shared by multiple units. | ||
|
||
4. Container Version History | ||
============================ | ||
|
||
This section defines the various lists of container's versions (author-defined, initial, and frozen) used to track the history of changes made to a container, allowing to view past versions and changes over time. | ||
|
||
- Each container version holds different lists of members (author-defined, initial, and frozen) to support rollback operations and history tracking for the container. | ||
- The author-defined list is the list of members that the author has defined for the version of the container. | ||
- The author-defined list won't change for a specific container version even if its references get soft-deleted. | ||
- The initial list is a copy of the author-defined list that has all versions pinned as they were at the time the container version was created. | ||
- The initial list is immutable for a container version. | ||
- The frozen list refers to the list of members at the time when the next version of the container is created. | ||
- The author-defined list is used to show the content of a container version as the author specified it, the frozen list can be used for discard operations on a draft version and the initial-list is part of the history of evolution of the container. | ||
|
||
Let's say a course author creates a unit with three components, all using floating versions. Each component's latest version is V1. The author-defined list would include these three components, ordered as the author decided. The initial list would have the components pinned to V1, while the frozen list would be empty until we create the next version for the container. | ||
mariajgrimaldi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Now, when the author creates a new version of the unit, for example, V2, we need to store the latest state of the container in the frozen list. This means pinning the latest versions of the components at that time, let's say V1, V2, and V3, respectively. | ||
|
||
Next, imagine the course author creates a new unit but uses pinned references for the components instead of floating versions, as they don't want to use the latest updates. In this case, the author-defined list, initial list, and frozen list would all be the same, as the component versions remain fixed. If we were to use different pinned versions, then a new unit version would be created instead. | ||
mariajgrimaldi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
5. Next Container Versions | ||
================================== | ||
|
||
This section defines the rules for version control in containers, explaining when new versions are created based on changes to container structure or metadata. | ||
|
||
- A new version is created if and only if the container itself changes (e.g., title, ordering of members, adding or removing members) and not when its members change (e.g., a component in a Unit is updated with new text). For instance, a new version of a unit is created when a component is removed, not when a new version of a component is created. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question |
||
- When a shared member is soft-deleted in a another container, all containers referencing it should create a new version without the member. This new version will be the new draft version of the container. For example, suppose a component is shared between two units, if the component is soft-deleted independently, then we'd need to create a new version for both units sharing the component. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
6. Publishing | ||
============= | ||
|
||
This section explains the publishing process for containers, detailing how containers and their members become accessible, either together or independently, based on their publication state. The publishing process happens on container versions, but throughout this section we'd call them containers for simplicity. | ||
|
||
- Containers can be published, allowing their content to be accessible from where the container is being used. | ||
- When a draft container is published, all its draft members are also published. For instance, after publishing a draft version of subsection which contains a draft unit with an updated title, the latest published version of the unit will be the one with the updated title, reflecting the changes made previously. | ||
- Members of a container can be published independently of the container itself. E.g., a shared component can be published independently of the unit if it also exists outside the unit. | ||
- When a new draft is created for a container with a shared member that has been soft-deleted, publishing the draft will trigger the publishing of all containers referencing that soft-deleted member. For example, if a component was soft-deleted triggering the creation of two draft units, then publishing one of the units would result in the publish of the second unit. Both units will now be published without the soft-deleted component. | ||
- Containers are not affected by the publishing process of its members. This means that publishing a component won't trigger new publishing processes for a container. | ||
|
||
7. Pruning | ||
========== | ||
|
||
This section defines the rules for pruning container versions, explaining when a container version can be pruned and the effects of pruning on the container and its members. | ||
|
||
- A container version can be pruned if: | ||
#. It's not being used by any other container. | ||
#. It's not a published version. | ||
#. It's not the latest version of the container. | ||
- In a top-down approach, start the deletion process with the parent container and work your way down to its members. E.g., when pruning Section V2 > Subsection V1 > Unit V3, the deletion process starts in the greater container working its way down to the smaller. | ||
- Pruning a container version will not affect the container's history or the members of other container versions, so containers will not be deleted if they are shared by other containers. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Questions: However, I still need some clarification about the implications of removing an entity version that's used somewhere. I'm guessing if another container is using this container entity version, then I cannot delete it (prune). Is that a correct assumption? Or I could delete it, but as with soft-deletion, I'd need to create a new container version for all containers referencing the deleted object, but still, the history for that container would be somewhat invalid after pruning. Now, as for the history of a single container: let's say I have 3 versions for a unit: UV1, UV2 and UV3. What would happen if I prune UV1 and UV2, but there are publishable entities from those referenced by rows in UV3? Would I be able to prune all but the entities referenced by UV3? |
||
|
||
.. _0002-content-flexibility.rst: docs/decisions/0002-content-flexibility.rst | ||
.. _0003-content-extensibility.rst: docs/decisions/0003-content-extensibility.rst | ||
.. _PublishableEntity: https://github.com/openedx/openedx-learning/blob/main/openedx_learning/apps/authoring/publishing/models.py#L100-L184 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,78 @@ | ||||||
18. Modeling Units as a Concrete Implementation of the Container Capability | ||||||
mariajgrimaldi marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if this requires its own ADR, but it helps illustrate the decisions using a more familiar concept like units. |
||||||
=========================================================================== | ||||||
|
||||||
Context | ||||||
------- | ||||||
|
||||||
The container capability is a generalized capability to hold different types of content. This decision focuses on modeling units as a concrete implementation of the container capability. | ||||||
|
||||||
Decisions | ||||||
--------- | ||||||
|
||||||
All decisions from `0017-generalized-containers.rst`_ are still valid but are written here alongside unit-specific decisions for better illustration. | ||||||
|
||||||
.. _`0017-generalized-containers.rst`: 0017-generalized-containers.rst | ||||||
|
||||||
1. Units as Containers | ||||||
======================= | ||||||
|
||||||
- A unit is a concrete type of container that holds components. | ||||||
- A unit is a container, making it also a publishable entity. | ||||||
- A Django application, which builds on the container application definitions, will an API and enough definitions for other unit subtypes to use. | ||||||
|
||||||
2. Unit Types and Content Constraints | ||||||
====================================== | ||||||
|
||||||
- Units can only hold components as their members but will not enforce this restriction at the model level. | ||||||
- Units are the first level of nested content types Unit > Components. | ||||||
- Content restrictions for units are implemented at the app layer, allowing units to limit their members to only components. | ||||||
- Unit subtypes can be created by following the extensibility principles in `0003-content-extensibility.rst`_. | ||||||
|
||||||
3. Unit Members and Relationships | ||||||
================================== | ||||||
|
||||||
- The members of a unit can only be components. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these decisions be part of this ADR as well?
|
||||||
- Components are referenced as an ordered list in a unit. | ||||||
- Units can hold both static and dynamic content, such as user-specific variations. | ||||||
- Units can reference pinned and unpinned versions of its components. | ||||||
- The latest draft or publish version of a component can be set by using `None` in thr parent-child relationship between units-components. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- A single component can be reference by multiple units. | ||||||
|
||||||
4. Unit Version History | ||||||
============================ | ||||||
|
||||||
- Each unit version holds different list of components to support rollback operations and history tracking. | ||||||
- The author-defined list is the list of components defined by the author for a specific unit version. | ||||||
- The author-defined list of components won't change for a specific version. | ||||||
- The initial list is a copy of the author-defined list that has all components pinned to the versions at the time of the unit version creation. | ||||||
- The initial list is immutable for a unit version. | ||||||
- The frozen list refers to the list of components at the time when the next version of the unit is created. | ||||||
- When creating the author-defined list of a new version with pinned references, then the author-defined list is the same as the initial and frozen list. When creating a new version with unpinned references, then the frozen list starts as `None` and should be updated with the author-defined components pinned when a new version is created. | ||||||
- The author-defined list is used to show the content of a unit version as the author specified it, the frozen list can be used for discard operations on a draft version and the initial-list is part of the history of evolution of the unit. | ||||||
- These lists allow history tracking of a unit version and revert operations. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note to self: This needs to be updated according the latest changes in the container ADR. |
||||||
|
||||||
5. Next Unit Versions | ||||||
====================== | ||||||
|
||||||
- A new version is created if and only if the unit itself changes (e.g., title, ordering of components, adding or removing components) and not when its components change (e.g., a component in a Unit is updated with new text). | ||||||
- When a shared component is soft-deleted in a different unit, a new unit version should be created for all containers referencing it without the component. | ||||||
|
||||||
6. Publishing | ||||||
============== | ||||||
|
||||||
- Units can be published, allowing their content to be accessible from where the unit is being used. | ||||||
- When a draft unit is published, all its draft components are also published. | ||||||
- Components within a unit can be published independently of the unit itself. | ||||||
- When a new draft, created for a unit when a shared component is soft-deleted, is published then all units referencing the component will be force-published. | ||||||
- Units are not affected by the publishing process of its components. | ||||||
|
||||||
7. Pruning | ||||||
=========== | ||||||
|
||||||
- A unit version can be pruned if: | ||||||
#. It's not being used by any subsections. | ||||||
#. It's not a published version. | ||||||
#. It's not the latest version of the unit. | ||||||
- In a top-down approach, start with the unit and work your way down to its component versions. | ||||||
- Component versions will not be deleted if they are shared by other units. | ||||||
- Pruning a unit version will not affect the unit's history or the components of other unit versions. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
Selectors for Dynamically Selecting Content | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm currently working on this section. Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the first version of selectors based on the information that's available. I haven't considered these comments yet, but I'll make sure to include reference them since that might impact their design. Also, for the container history.
mariajgrimaldi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
=========================================== | ||
|
||
Context | ||
------- | ||
|
||
This ADR proposes a way to represent dynamic members of a container, where dynamic means selecting members from a specified pool. Some examples of dynamic selection are: | ||
|
||
1. A/B Testing: testing two different groups of components to see which perform better. | ||
2. Per Student: randomly selecting three problems from a set of 20 per student. | ||
|
||
And any other custom use case to dynamically select members for a container. This proposal introduces the concepts of selectors and variants to implement this type of dynamic selection. | ||
|
||
1. Core Structure | ||
================= | ||
|
||
This section explains the concepts and behaviors used to build dynamic selection, selectors and variants. | ||
|
||
- Selectors determine what the container should display based on the selector type. For example, based on the nature an A/B split test or a randomization selector members of the container would vary. | ||
- Selectors are used to dynamically select 0-N publishable entities from a specified pool. E.g., take 5 components from this pool of 20. | ||
- The logic for pushing members into variants depends on the selector selection method. For example, A/B split testing two different sets of components or select three problems from a set of twenty. | ||
- Variants hold the members selected for a container based on what the selection method is. E.g., if the selector is "select 5 components out this pool of 20 components" then the variant would be the 5 components selected for the user. | ||
- Variants are build on the parent-child relationship used for containers and their members, storing the dynamically selected content as an ordered list as containers do. | ||
|
||
2. Selector Types and Selecting Content | ||
======================================= | ||
|
||
This section describes how different types of selectors work and how they handle the selection of dynamic content. | ||
|
||
- A selector can be of any type, which means it can implement any method to select members from a pool. Therefore, selectors will follow extensibility principles in `0003-content-extensibility.rst`_ for creating new selector types. | ||
mariajgrimaldi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- Selection versions encode the rules and holds useful details for the selection process like: where to get members from, number of items to select, and other criteria. For instance, for the "select 5 components out of this pool of 20 components" its selector version would encode where to get the 20 components, how many to get for each user and any other detail needed to create the specific variants. | ||
- Depending on the size of the pool of members, variants can be generated at publishing time or on-demand. This behavior should be determined by the selector version based on high vs low permutation scenarios. | ||
- A compositor is responsible for populating the variants but will not be implemented as part of the selector application which belongs to the authoring app. | ||
|
||
3. Versioning | ||
============= | ||
|
||
A new version of a selector is created whenever the pool of concent changes by adding, removing or reordering existing members. | ||
|
||
.. _0003-content-extensibility.rst: docs/decisions/0003-content-extensibility.rst | ||
mariajgrimaldi marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During discussions, container members were usually referenced as "children", so I will change all references to members to children.