Legend

  • βœ… Done.
  • 🟒 Confident it will not require backwards compatibility breaks.
  • 🟑 Medium confidence; more work needed to achieve necessary confidence.
  • πŸ”΄ Low confidence; requires a lot more work

Overview

XB's data model (ComponentTreeItem) was originally prototyped with:

  1. only SDCs
  2. βœ… dynamic data fetched from the host entity, the right value(s) extracted using (Reference)Field(Object)PropExpression and assigned to the right component instance into the component tree: DynamicPropSources
  3. βœ… static data stored in independent field item objects: StaticPropSources (which also must extract the right value(s), and does so using (Reference)FieldType(Object)PropExpression)
  4. βœ… symmetric + asymmetric translation support (symmetric: tree is locked, inputs are per-translation; assymmetric: both are translatable, meaning different component trees for each translation)

Those foundations remained largely the same, but since then we gained:

  1. βœ… multiple component types, thanks to ComponentSource plugins β‡’ everything must scale to unknown future component types
  2. βœ… additional prop sources, such as DefaultRelativeUrlPropSource β‡’ everything must scale to unknown future prop sources
  3. βœ… auto-saving of component trees

Finally, we know there's both known technical debt, and more features that we'll eventually need to support:

  1. βœ… debt: full dependency support, throughout all of the above, to allow for reliable updates
  2. 🟒 debt: the explicit input schema must be able to evolve, and each ComponentSource may have different needs for that. For example: block plugins may change their settings (and their config schema), SDCs may gain new optional inputs, JS components may gain new required inputs β†’ #3501708: Prove that it *will* be possible to apply block settings update paths (assuming #3521221 in core) to stored XB component trees in config/content + #3523841: Versioned Component config entities (SDC, JS: prop_field_definitions, block: default_setting, all: slots for fallback) + component instances refer to versions β‡’ less data to store per XB field row here β€” but #3524751: [later phase] Component Source plugins: generalized support for schema changes of explicit inputs later.
  3. βœ… debt: we went with a JSON-heavy initial implementation (the inputs and tree props both store a JSON struture), but we may want to reconsider this β†’ solved in #3468272: Store the ComponentTreeStructure field property one row per component instance, with additional refinements possible post-release for more field types than just XB
  4. βœ… feature: content entities' XB component trees must be able to populate the exposed slots provided by the corresponding ContentTemplate (β†’ #3519352: Content templates, part 3b: store exposed slot subtrees on individual entities)
  5. 🟒 feature: asymmetric translation will need some explicit inputs to be locked, because some explicit inputs may be considered to be not translatable β€” for example an image input of some component
  6. 🟒 debt: currently, XB fields can only exist on XB's own Page content entity type (base field definition) and on the article bundle of Node entities (configurable field) β€” expanding that is out of scope here and is in scope for #3498525: [META] Allow Canvas to be used on any content entity type (bundle), as long as it has certain entity type characteristics. This poses no additional risks for XB's data storage, because this already covers the gamut of possible kinds of XB fields on content entities.
  7. 🟒 feature: full read/write support of XB trees in JSON:API (#3521002: [META] Maintainable client-side data model + internal HTTP API's #3499632: Remove `ClientServerConversionTrait` (Cβ†’S) and `ComponentTreeItemList::getClientSideRepresentation()` (Sβ†’C) in favor of (de)normalizers), including the ability to filter (β†’ #3523844: Spike: Explore adding configuration options to the tree item formatter to support alternate use-cases).
  8. 🟒 feature: ability to store variants of component trees for use cases like personalization (i.e. different component subtree for Belgians & Brits vs everyone else β†’ #3525564: [META] Experience Builder Personalization), responsive design (i.e. breakpoint-specific overrides), and arbitrary future use cases (β†’ #3525746-6: Update the React client preview view)

Beta blockers (July 2025)

Beta blockers (tag: beta blocker) MUST happen before #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites.

⚠️ As part of this, we're aiming to ideally close #3477428: [PP-1] Refactor (or decide not to) the XB field type to be multi-valued, to de-jsonify the tree, and to reference the field_union type of the prop values and #3440578: [PP-2] JSON-based data storage proposal for component-based page building, but both of those cover many aspects. They're being tackled piece-by-piece by the beta-blocking issues below, as originally proposed by @larowlan at #3477428-44: [PP-1] Refactor (or decide not to) the XB field type to be multi-valued, to de-jsonify the tree, and to reference the field_union type of the prop values.

(Numbered lists must happen one after the other, bulleted lists can happen in parallel.)

    βœ… Dependencies/usage beta blockers
    Precise dependency information for every XB component tree: every config entity with a component tree AND every revision of an XB field instance in content entities, because that allows strong guarantees, and is essential for explicit input schema evolvability
    1. βœ… #3457504: XB field type: calculate all dependencies, store them, surface in new Component "Audit" operation
    2. βœ… #3521202: Store XB field type's "deps_*" columns in separate table to allow efficient querying
    3. βœ… #3460230: Component entity config dependencies are incomplete: missing ComponentSourceInterface::calculateSettingsDependencies() to compute deps for field storage + instance settings + widget
    4. βœ… #3544604: Calculating dependencies of `ReferenceField(Type)PropExpression` is missing intermediate dependencies
    βœ… Field type storage (content entities) schema beta blockers
    Given lessons in the ecosystem over the past decade, plus what we know about functionality we still intend to add to XB, organize the data storage to avoid future backwards compatibility breaks, and allow data storage for new features to be easily added later.
    1. βœ… Ensuring multi-value (type: array) explicit inputs (e.g. list of integers for sparkline component, list of images for image gallery component, etc.) can be supported later: #3467870: Support `{type: array, …}` prop shapes
    2. βœ… 1 row/revision with 2 enormous JSON blobs β†’ 1 row/component instance/revision with a single tiny JSON blob: #3468272: Store the ComponentTreeStructure field property one row per component instance
    3. βœ… content templates: XB field on content entities should be able to store >1 component tree: one per exposed slot in the governing ContentTemplate, if any #3519352: Content templates, part 3b: store exposed slot subtrees on individual entities
    4. βœ… New name field property for additional functionality: #3460958: Data storage support for allowing Content Creator to name component instances for the specific context
    5. βœ… versioned Components + associated storable prop shapes (field types + settings) (blocker for #3524751: [later phase] Component Source plugins: generalized support for schema changes of explicit inputs): #3523841: Versioned Component config entities (SDC, JS: prop_field_definitions, block: default_setting, all: slots for fallback) + component instances refer to versions β‡’ less data to store per XB field row
    6. βœ… bike shed finalize the field type's field property names: #3528167: Rename `version` field property to `component_version`
    7. βœ… ensure this architecture allows for efficient updating of existing component trees when explicit input schemas (block settings, SDC props …) change: #3501708: Prove that it *will* be possible to apply block settings update paths (assuming #3521221 in core) to stored XB component trees in config/content
    8. βœ… #3528528: `ComponentInputs::getPropSources()` needs to take `GeneratedFieldExplicitInputUxComponentSourceBase::rawInputValueToPropSourceArray()` into account
    9. βœ… #3528499: Revisit storage of dependencies in separate table now we have separate deltas per component + remove plugin dependencies + make component version usages auditable
    10. βœ… #3520923: [PP-1] Use `json` schema type for SQLite and remove `text` workaround
    βœ… Configuration storage schema beta blockers
    βœ… Validation beta blockers

    Stable blockers (October 2025)

    Stable blockers (tag: stable blocker) do not block #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites, but would be nice-to-have at that point in time. They MUST happen before #3517885: Milestone 1.0.0: Production Sites.

    Field type storage (content entities) schema stable blockers
    Configuration storage schema stable blockers
    Validation stable blockers
    Security stable blockers
    Other stable blockers

    Post-stable priorities (after October 2025)

    Post-stable priorities (tag: post-stable priority) do not block any release, and are things we'd like to see happen, but are intentionally NOT happening any time soon. We're highly confident that these will be net additions without the need for backwards compatibility breaks.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

wim leers created an issue. See original summary.

wim leers’s picture

Issue summary: View changes
wim leers’s picture

Issue summary: View changes
wim leers’s picture

Issue summary: View changes
wim leers’s picture

Issue summary: View changes
wim leers’s picture

Issue summary: View changes
wim leers’s picture

Issue summary: View changes

Moved a significant chunk out of here and into #3520484: [META] Production-ready ComponentSource plugins.

wim leers’s picture

Issue summary: View changes
catch’s picture

There is a @todo in ComponentTreeItem linking to #3487533: Cannot modify a table which uses JSON type so I think that probably needs to be included here?

catch’s picture

wim leers’s picture

Issue summary: View changes
wim leers’s picture

Issue summary: View changes
larowlan’s picture

Additional things I think we need for a 1.0

* plugabble/flexible ComponentTreeLoader so that LB/PG can provide a translation layer for legacy data
* it would be good if default values were stored as an array instead of JSON for the sake of git diff on exported config - same reasons as #2871354: Improve "allowed_html" to provide a better config diff - the current json is going to be very painful for content template git conflicts

catch’s picture

longwave’s picture

Track explicit input schema version: hash (the normalized) explicit input schema, assign version (sequential integer), store version: {hash, provider_version} mapping in Component config entity.

I don't think anything with automated sequential integers is going to work because we have to handle partial deployments of config between sites. Let's say you export a Section from your site that uses a component that is on version 2 of the schema. The SDCs and component config entities might already exist on the destination site, but if it was installed later than the site it was exported from, the same component might only be on version 1 of its schema.

wim leers’s picture

penyaskito’s picture

wim leers’s picture

wim leers’s picture

wim leers’s picture

Issue summary: View changes

WRT dependency/usage tracking: #3457504: XB field type: calculate all dependencies, store them, surface in new Component "Audit" operation landed, and so did its follow-up #3521202: Store XB field type's "deps_*" columns in separate table to allow efficient querying. This cleaned up the technical debt around FieldTypeUninstallValidator and hence enabled me to close 3 stable blockers, see #3457504-56 for details.
This unblocked:

Only remaining: #3460230: Component entity config dependencies are incomplete: missing ComponentSourceInterface::calculateSettingsDependencies() to compute deps for field storage + instance settings + widget. That is a stable blocker too, but won't require data storage changes. So: I'm assigning that subset of this meta a 🟒.

wim leers’s picture

wim leers’s picture

wim leers’s picture

Issue summary: View changes

Removed

debt: we know we'll need to support implicit inputs (e.g. block plugins can declare required + optional "contexts" that allow block plugins behave differently not only to component instance author's explicit wishes ("explicit inputs"), but also based on the component developer's logic ("implicit inputs")

because that doesn't affect the data storage.

wim leers’s picture

wim leers’s picture

wim leers’s picture

Issue summary: View changes

#3468272: Store the ComponentTreeStructure field property one row per component instance landed!

Next up: #3523841: Versioned Component config entities (SDC, JS: prop_field_definitions, block: default_setting, all: slots for fallback) + component instances refer to versions β‡’ less data to store per XB field row.


Good news! The current issue summary's

Blocked on the above first bringing clarity to the full set of needs first, before starting a potentially enormous refactor

is getting to that point of clarity thanks to @larowlan, @effulgentsia, @catch and I actively having started to push these forward in the past few weeks.

So: time for an overhaul of the issue summary!


Updated the issue summary to more clearly distinguish between:

  1. beta blockers (i.e. necessary for #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites)
  2. stable blockers (i.e. necessary for #3517885: Milestone 1.0.0: Production Sites)
  3. post-stable priorities (i.e. important, but after #3517885: Milestone 1.0.0: Production Sites)

… and within each of those, the areas within "data storage" they target. For beta, that's:

  1. Dependencies/usage
  2. Field type storage (content entities) schema
  3. Configuration storage schema
  4. Validation
wim leers’s picture

Issue summary: View changes

Per @lauriii: add more features we'll eventually need to support.

And: update the status indicators for all of them.

wim leers’s picture

Issue summary: View changes

Clarify JSON:API support, bump to 🟒 . Linked (not added!) #3499632: Remove `ClientServerConversionTrait` (Cβ†’S) and `ComponentTreeItemList::getClientSideRepresentation()` (Sβ†’C) in favor of (de)normalizers even though it actually belongs under #3521002: [META] Maintainable client-side data model + internal HTTP API, because it's how we'll add JSON:API support. This is 🟒 because after #3468272, the implementation path for it became crystal clear.

wim leers’s picture

Issue summary: View changes

Added #3526127: Ensure deterministic config export order of config-defined component trees.

That doesn't mean I can remove the Config management issue queue component is yet to be triaged 🚨 though β€” that's still true.

wim leers’s picture

Issue summary: View changes

Bumped feature: ability to store variants of component trees for use cases like personalization from πŸ”΄ to 🟑 based on
#3525746-6: Update the React client preview view, and based on @penyaskito having proven in #3525565: Personalization ComponentSource PoC that the single new ComponentSource approach works. That means zero data storage changes are needed!

Tempted to mark 🟒, but would prefer to be cautious instead of overly confident. Will only mark as such once more of it is working.

wim leers’s picture

Issue summary: View changes

Triaged the Config management issue queue component, as well as the @todos in XB's config schema.

Added many newly created issuesΒ β€” all of them stable blockers unless marked otherwise:

  1. #3526703: Adopt `AtLeastOneOf` validation constraint for cardinality validation
  2. #3526706: Stop assuming default Field Widget settings sufficeΒ β€”Β add Field Widget settings support to `experience_builder.generated_field_explicit_input_ux: prop_field_definitions`
  3. #3526707: Tighten validation of `experience_builder.generated_field_explicit_input_ux: prop_field_definitions.[%key].default_value`
  4. #3526716: Tighten validation of `experience_builder.generated_field_explicit_input_ux: prop_field_definitions.[%key].expression`
  5. #3526721: Require Drupal 11.1.8 and remove `type: field.value.language` work-around
  6. #3518696: [2.x] [11.3-only] Remove `BetterConfigExistsContraint` and move back to `ConfigExistsContraint` β†’ post-stable priority
  7. #3481188: [later phase] Gracefully handle deleted regions in PageTemplate config entities" β†’ post-stable priority
  8. #3503272: JavaScriptComponent config entities should have mutable machineNames
  9. #3498525: [META] Allow Canvas to be used on any content entity type (bundle), as long as it has certain entity type characteristics
  10. #3518272: Support all entity types with configurable displays for content templates
  11. #3524345: Avoid deleting `PageRegion`s & `Pattern`s when uninstalling field type-providing module: implement `::onDependencyRemoval()` , replace with default StaticPropSource β†’ post-stable priority
  12. #3464042: Add ComponentAuditabilityTest
  13. #3525759: SdcPropKeysConstraintValidator::validate() should complain about extraneous keys too, not just missing keys
wim leers’s picture

Issue summary: View changes

Having reviewed #3519352 and written #3519352-55: Content templates, part 3b: store exposed slot subtrees on individual entities (specifically the MR review + issue/MR scope part), I'm bumping feature: content entities' XB component trees must be able to populate the exposed slots provided by the corresponding ContentTemplate from 🟑 to 🟒.

wim leers’s picture

Issue summary: View changes

Per #37, I triaged all config management issues this morning.

Didn't find anything.

So I can move 🟑 Configuration storage schema beta blockers to βœ… Configuration storage schema beta blockers πŸ₯³ Note that #3526127-5: Ensure deterministic config export order of config-defined component trees might become a beta blocker.


Validation beta blockers did not have an indicator yet β€” the sole issue there is well on its way, so marked it 🟒.

wim leers’s picture

Issue summary: View changes

#32 failed to update debt: we went with a JSON-heavy initial implementation from 🟒 to βœ… for the cited reasons.

wim leers’s picture

Issue summary: View changes

The untranslatable_inputs sample diff for post-stable was incomplete.

wim leers’s picture

wim leers’s picture

wim leers’s picture

wim leers’s picture

Issue summary: View changes

Bumping debt: the explicit input schema must be able to evolve from 🟑 to 🟒 given #46.

Also bumping feature: ability to store variants of component trees for use cases like personalization […] from 🟑 to 🟒 after discussing with @effulgentsia (he agreed), because A) we've discussed it in depth over the past ~2 weeks, B) @penyaskito has a working PoC of this at #3525565: Personalization ComponentSource PoC, which he'll be updating tomorrow (since a few blockers have landed for him).

That means everything now is either 🟒 or βœ…!

wim leers’s picture

Issue summary: View changes

The two new beta blockers that spun out from #3523841 last night have already landed: #3528167: Rename `version` field property to `component_version` + #3528159: Ensure deterministic version hashes for ComponentSource-specific settings, thanks to config schema-powered normalization β€” thanks to @larowlan!

Added #3528284: Add e2e tests that prove we can edit an old version of a component as a stable blocker (again: thanks Lee).

Finally: swapping the overall/catch-all #3525564: [META] Experience Builder Personalization for the more tightly scoped #3525728: Personalization ComponentSource plugin: test of fundamental functionality + working tech demo as proof for full confidence in this future feature:

  1. feature: ability to store variants of component trees for use cases like personalization (i.e. different component subtree for Belgians & Brits vs everyone else), responsive design (i.e. breakpoint-specific overrides), and arbitrary future use cases
wim leers’s picture

wim leers’s picture

catch’s picture

wim leers’s picture

wim leers credited tedbow.

wim leers’s picture

As described at #3520923-29: [PP-1] Use `json` schema type for SQLite and remove `text` workaround, that can't happen until 11.2.0 is tagged. It's a trivial MR to land though :)

So: THE BETA PARTS ARE DONE! πŸ₯³πŸ₯³πŸ₯³ Special thanks to:

  1. @larowlan, for the constant stream of reviews & MRs
  2. @catch, for the constant feedback (from a core committer!)
  3. @tedbow, for the translation test coverage + field type uninstall validator + component tree validation logic that was iterated upon but still lives on, these were all very valuable guardrails!
  4. @penyaskito, for helping get the dependency calculation logic in place, which in all of the recent iterations by Lee and I had proved crucial!
  5. @effulgentsia, for the original idea by and his chiming in when really necessary over the past few weeks πŸ™
wim leers’s picture

Status: Active Β» Patch (to be ported)

Signaling this is "done for now", i.e. for #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites but NOT for #3517885: Milestone 1.0.0: Production Sites, by marking this Patch (to be ported).

wim leers’s picture

larowlan’s picture

Ideally it would be good to do a spike on a BC loading layer for Paragraphs/Layout builder to triple check we've got a clean run at that in the future with the current data model

wim leers’s picture

That would be lovely indeed. But I don't think that's a hard blocker for beta. Let's first make sure we actually achieve #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites, and then let's do this. I don't see how expressing anything that can be built in Layout Builder/Paragraphs/… (see #3454519: [META] Support component types other than SDC, block, and code components) could not be expressed in XB, because XB has more freedom/is much less prescriptive?


Also … didn't we discuss (and in fact you proposed it! πŸ˜„) to only convert existing data into the client-side data model, so you can load an existing component-tree-esque thing (Layout Builder, Paragraphs, whatever) into XB and then choose to publish or not?

IOW: require human judgment, because anything automated is unlikely to A) be perfect (pixel perfect match), B) forgiving (a human could judge that actually, it makes sense to use some other component because it is better or simpler or $reason).

An automated batch migration is likely to result in problems, whereas this allows for a nice gradual migration: they can both live side-by-side until every relevant entity has been switched over to XB.

larowlan’s picture

Sorry, I wasn't clear, I'm not proposing any change to the discussed approach, just validating that the approach is viable. Will focus on #3515932: Milestone 1.0.0-beta1: Enable creation of non-throwaway sites

larowlan’s picture

Issue summary: View changes
larowlan’s picture

Issue summary: View changes
wim leers’s picture

wim leers’s picture

wim leers’s picture

wim leers’s picture

ADR updated and now IMHO ready to be merged. Hoping to get +1s from @effulgentsia, @lauriii, @larowlan and @catch.

larowlan’s picture

Status: Patch (to be ported) Β» Reviewed & tested by the community

ADR looks good to me, thanks for taking the time to collate that

wim leers’s picture

Issue summary: View changes

#3538487: Don't allow passing uncollapsed inputs if using default expression and #3538537: Update path tests should also run against non-SQLite DBs landed. πŸ₯³


Thanks for confirming the ADR looks good to you, @larowlan β€” I'd like a +1 from at least @lauriii still, given he's the one who asked for the consequences to be more complete, and to also list negative consequences.

lauriii’s picture

The changes look good and it gives a sense that we thought of both the pros and cons of these decisions πŸ‘

wim leers’s picture

Issue summary: View changes

Landed the stable-blocking subset of #3518272: Support all entity types with configurable displays for content templates already, hence moved the remainder to post-stable.

catch’s picture

Thanks for the updates. ADRs are still pretty new to me so I'm not sure what the format/verbosity is supposed to be like overall, but this all looks correct now! The one thing I wondered about is if it needs more details about the trade-offs between flexible storage for inputs vs them not being queryable, but if that's more detail than intended seems fine to leave it implied.

  • wim leers committed 3fa08048 on 1.x
    Issue #3520449 by wim leers, larowlan, catch, penyaskito, lauriii,...
wim leers’s picture

Yep, it's supposed to be prescriptive of high-level architecture, not implementation details. Some of that is subjective. But historically, we've been very bad at actually finishing (i.e. merging/publishing) ADRs, so @lauriii has urged us (rightfully so IMHO!) to err on the side of high-level. IOW: the details are for concrete implementation issues and detailed docs, the architectural choices and their rationales are for ADRs.

Very glad to have your blessing, so: merged! πŸ˜„πŸ₯³

wim leers’s picture

Issue summary: View changes
Status: Reviewed & tested by the community Β» Active
Issue tags: +Needs issue summary update

Landed last week: #3528284: Add e2e tests that prove we can edit an old version of a component.

We've met our beta goals, so un-RTBC'ing, to convey that now we're working towards stable blockers. However, the target scope of 1.0 has been reduced, which in turn reduces the number of stable blockers. @lauriii is going to document that scope at #3517885-3: Milestone 1.0.0: Production Sites.

This is a first pass at updating the issue summary.

Project: Experience Builder Β» Drupal Canvas
Version: 0.x-dev Β» 1.x-dev

Experience Builder has been renamed to Drupal Canvas in preparation for its beta release. You can now track issues on the new project page.

wim leers’s picture

wim leers’s picture

Issue summary: View changes
wim leers’s picture