Problem/Motivation

The domain_config module declares drupal:language as a hard dependency, and three of its four override classes extends/implements classes from the language module at PHP-declaration level. By transitive dependency, domain_config_ui also requires language to be enabled. As a result, monolingual sites that only need domain-specific config overrides (without language-aware variants) are forced to install and maintain the language module — even though they have no use for translations.

The two feature sets bundled in domain_config and domain_config_ui today are conceptually independent:

  1. Domain-only config overrides — different system.site, system.theme, etc. per domain. Self-contained, no need for the language module.
  2. Domain × language config overrides — different overrides per (domain, language) pair on multilingual sites. Fundamentally needs Drupal core's language collection machinery, which lives in the language module.

Splitting each of the two modules into a base module and a language-aware companion would let monolingual sites use the domain-only feature set without ever installing language, while keeping the multilingual feature set fully supported for sites that need it.

Proposed resolution

Split the two modules into four:

Module Contents Depends on
domain_config Domain-only override services and helpers (no language coupling) domain:domain
domain_config_language (new) Domain × language override services, language manager decorator domain:domain_config, drupal:language
domain_config_ui UI for domain-only overrides domain:domain_config
domain_config_language_ui (new) UI additions for language-aware overrides (admin column, language-aware delete logic, decorator) domain:domain_config_ui, domain:domain_config_language

Files that move (domain_config → domain_config_language)

  • src/Config/DomainLanguageConfigFactoryOverride.php + interface
  • src/Config/DomainLanguageConfigOverride.php
  • src/Config/DomainLanguageConfigCollectionNameTrait.php
  • src/DomainConfigLanguageManager.php
  • Services domain.language.config_factory_override and domain.language_manager from domain_config.services.yml
  • Tests under tests/ that install language

Files that move (domain_config_ui → domain_config_language_ui)

  • src/DomainConfigUILanguageManager.php (extends DomainConfigLanguageManager)
  • Service domain.language_manager decorator declaration

Refactors needed inside the UI module

Two classes today mix language-aware and language-agnostic logic and need extension points so the language UI can live in a separate submodule:

  1. Config/DomainConfigFactory.php — the inline language-override delete loop (~lines 269–273) becomes an event subscriber in domain_config_language_ui, listening to DomainConfigOverrideEvents::DELETE_OVERRIDE. Leans on the project's existing event infrastructure.
  2. Controller/DomainConfigUIController.php — the "Languages" column rendering (~lines 132–192) moves behind a hook (e.g. hook_domain_config_ui_listing_columns_alter) so the language UI submodule can add its own column without a hard dep from the base UI module.

Service IDs

Service string IDs stay stable across the split (domain.language.config_factory_override, domain.language_manager, etc.). Only the declaring module changes. Code calling \Drupal::service('domain.language.config_factory_override') keeps working unchanged.

PHP namespaces

The language-aware classes move from Drupal\domain_config\… to Drupal\domain_config_language\… (and from Drupal\domain_config_ui\… to Drupal\domain_config_language_ui\…). Since these classes are marked @internal following #3584261, the public API contract is the service IDs, not the FQCNs. No deprecated alias classes — those would reintroduce the language coupling we're trying to remove.

Update hooks

One domain_config_update_1000X() that auto-installs domain_config_language when language is enabled, plus an equivalent domain_config_ui_update_1000X() for domain_config_language_ui:

if (\Drupal::moduleHandler()->moduleExists('language')) {
  \Drupal::service('module_installer')->install(['domain_config_language']);
}

Every existing 3.x site already has language enabled (it was a hard dep), so this preserves their current behavior automatically.

Data migration

None needed. The config-collection storage format (domain.{id} and domain.{id}.language.{lang}) is collection-name driven, not module-owned. Existing overrides on disk continue to be served by the new module without any rename or move. The 2.x → 3.x migration in domain_config_update_10001 stays in place.

Remaining tasks

  1. Create the two new submodules (domain_config_language, domain_config_language_ui) with info.yml, services.yml, install file.
  2. Move the language-aware PHP classes and update namespaces.
  3. Refactor DomainConfigFactory's inline language delete to a subscribed event.
  4. Refactor DomainConfigUIController's language column to an alter hook.
  5. Move language-aware tests; update the shared DomainConfigTestBase as needed.
  6. Add the two update hooks for auto-installation when language is enabled.
  7. Update GitLab CI matrix to also run domain_config with language disabled (the new supported configuration) and domain_config_ui standalone.
  8. Update mkdocs.yml, the docs/domain_config/ pages and the CHANGELOG.

User interface changes

None for sites that currently have both domain_config_ui and language enabled. The update hook auto-enables domain_config_language_ui, so the admin listing keeps the "Languages" column and language-aware editing features.

For new sites (or sites that uninstall the language UI submodule), the admin listing and forms render only the domain-level overrides — cleaner UI for monolingual use cases.

API changes

  • The @internal classes Drupal\domain_config\Config\DomainLanguage* (i.e. DomainLanguageConfigFactoryOverride, DomainLanguageConfigFactoryOverrideInterface, DomainLanguageConfigOverride, DomainLanguageConfigCollectionNameTrait) and Drupal\domain_config\DomainConfigLanguageManager move to Drupal\domain_config_language\…. Custom code typehinting these would need a use statement update.
  • Service IDs are unchanged.
  • New event subscriber contract: DomainConfigOverrideEvents::DELETE_OVERRIDE now drives language-override cleanup. Anyone who already subscribes to that event keeps working.
  • New alter hook: hook_domain_config_ui_listing_columns_alter (or similar — exact name TBD during review).

Data model changes

None. Config collection naming is unchanged.

Effort estimate

~3–5 days of focused work. The domain_config half is mostly mechanical file moves; the domain_config_ui half is the bulk of the work because it involves designing two small extension points.

Release strategy

Ship as 3.1.0 with a clear release-notes call-out. The breaking change is limited to @internal namespace moves, which the project's API stability guarantees do not cover. Existing sites are migrated automatically via the update hooks.

Related issues

  • #3586509 — original report that surfaced the coupling problem (closed without code change after analysis showed the user's site had a misinstalled language module).
  • #3060758 — introduced the new Config/ class hierarchy in 3.x that made the cross-module coupling load-bearing at compile time.
  • #3584261 — marked the affected classes as @internal, which is what makes the namespace move feasible without a public-API breaking change.

Issue fork domain-3588828

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

mably created an issue. See original summary.

mably’s picture

Issue summary: View changes

mably’s picture

Status: Active » Needs review

  • mably committed 802731dc on 3.x
    task: #3588828 Split domain_config and domain_config_ui to make the...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • mably committed 0d75658b on 3.x
    fix: #3588828 update_10003 must not erase existing...

  • mably committed 2b6fa59c on 3.x
    fix: #3588828 DomainConfigOverrideMigration must not overwrite live...