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: ADRs for modeling containers capability #251

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2906792
docs: add ADR for generalized containers capability
mariajgrimaldi Oct 25, 2024
b1b1a88
docs: add ADR for units
mariajgrimaldi Oct 28, 2024
413b66c
fix: reference content flexibility ADR
mariajgrimaldi Oct 28, 2024
0deab25
docs: add containers DB schema
mariajgrimaldi Oct 28, 2024
22eb1ae
docs: add containers and units db schema
mariajgrimaldi Oct 28, 2024
db61fda
docs: correct list ordering
mariajgrimaldi Oct 29, 2024
b5f05d8
refactor: improve writing and drop too implementation specific decisions
mariajgrimaldi Oct 30, 2024
9d2c62d
docs: take container decisions and make them unit-specific
mariajgrimaldi Oct 30, 2024
8648f16
docs: add more decisions on version control and publishing
mariajgrimaldi Oct 31, 2024
80cf370
refactor: drop decision about creating new EntityLists with each version
mariajgrimaldi Nov 4, 2024
b98c02c
docs: Update 0017-generalized-containers.rst
mariajgrimaldi Nov 5, 2024
ccada60
docs: Update docs/decisions/0017-generalized-containers.rst
mariajgrimaldi Nov 5, 2024
778ce2b
docs: Update docs/decisions/0017-generalized-containers.rst
mariajgrimaldi Nov 5, 2024
e738778
docs: reference other types of content based on containers
mariajgrimaldi Nov 5, 2024
3a28fa5
docs: Update docs/decisions/0017-generalized-containers.rst
mariajgrimaldi Nov 5, 2024
bbce789
docs: Update docs/decisions/0017-generalized-containers.rst
mariajgrimaldi Nov 5, 2024
af7dce4
refactor: add section for container states and intro to each section
mariajgrimaldi Nov 12, 2024
506d9cd
refactor: address PR reviews
mariajgrimaldi Nov 12, 2024
466b450
refactor: address PR reviews
mariajgrimaldi Nov 12, 2024
46999f8
refactor: address PR reviews
mariajgrimaldi Nov 14, 2024
0c426d2
docs: add empty ADR for dynamically selected content
mariajgrimaldi Nov 14, 2024
46dfa9e
refactor: reduce ambiguity and address reviews
mariajgrimaldi Nov 14, 2024
d37a414
refactor: write containers capability as a concrete use-case with units
mariajgrimaldi Nov 14, 2024
64e4db9
refactor: address PR reviews
mariajgrimaldi Nov 14, 2024
46e5a09
docs: Update 0017-generalized-containers.rst
mariajgrimaldi Nov 14, 2024
822d9a0
docs: add decisions for pruning according to available info
mariajgrimaldi Nov 19, 2024
8646225
docs: Update 0017-generalized-containers.rst
mariajgrimaldi Nov 19, 2024
1f1c962
docs: Update 0018-units-as-containers.rst
mariajgrimaldi Nov 19, 2024
f260756
docs: add first version for selectors high-level decisions
mariajgrimaldi Nov 19, 2024
86141e5
refactor: add examples to better illustrate decisions
mariajgrimaldi Nov 20, 2024
aff8b9b
refactor!: drop db diagrams for containers to avoid too much specificity
mariajgrimaldi Nov 20, 2024
76e89d5
refactor: add examples to better illustrate selectors
mariajgrimaldi Nov 20, 2024
6ecde96
docs: apply suggestions from code review
mariajgrimaldi Nov 20, 2024
f7cc446
docs: improve readability
mariajgrimaldi Dec 5, 2024
dc5a0a2
docs: address PR reviews
mariajgrimaldi Dec 5, 2024
d0f1fc8
fix: use other approach for linking references
mariajgrimaldi Dec 5, 2024
83f8d04
fix: reference documents from current dir
mariajgrimaldi Dec 5, 2024
8fa418e
docs: update unit ADR with latest changes
mariajgrimaldi Dec 5, 2024
6fd6864
docs: apply suggestions from code review
mariajgrimaldi Dec 6, 2024
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
101 changes: 101 additions & 0 deletions docs/decisions/0017-generalized-containers.rst
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
Copy link
Member Author

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.

============================

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.
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question
Does unpinning a member version from a container merit creating a new version for the container? Or is this an entirely different container?

- 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.
Copy link
Member Author

Choose a reason for hiding this comment

The 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.
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Questions:
This is my first approach to pruning, based on #240 (comment) and #154.

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-extensibility.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
78 changes: 78 additions & 0 deletions docs/decisions/0018-units-as-containers.rst
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
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

The 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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these decisions be part of this ADR as well?

  • Unit members can exist as standalone items outside the unit.
  • Members should indicate which unit they belong to.
  • Members can be duplicated from units but keeping original references.

- 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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- The latest draft or publish version of a component can be set by using `None` in thr parent-child relationship between units-components.
- The latest draft or publish version of a component can be set by using `None` in the parent-child relationship between units-components.

- 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.
Copy link
Member Author

Choose a reason for hiding this comment

The 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.
39 changes: 39 additions & 0 deletions docs/decisions/0019-selectors.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Selectors for Dynamically Selecting Content
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently working on this section. Thanks!

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 19, 2024

Choose a reason for hiding this comment

The 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 designed to dynamically select 0-N publishable entities (e.g., components).
- Each version represents a set of members at a particular time.
- The logic for pushing members into variants depends on the selection version's selection method.
- Variants hold the members selected for a container based on what the selection method is.
- Variants are build on the parent-child relationship between containers and their members, storing the dynamically selected content as an ordered list.

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.
- 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.
- 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 authoring application.

3. Versioning
=============

A new version of a selector is created whenever the pool of concent changes by adding, removing or reordering existing members.