Cleanly Managing Module Dependencies (the module.xml sequence)
Cleanly Managing Module Dependencies (the module.xml sequence)
~6 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026
The <sequence> in module.xml has grown five times across this series - always for the same reason: a foreign key or an EAV dependency needed the guarantee that another table already existed before db_schema.xml or a data patch touched it. This chapter pulls the five individual decisions from chapters 2, 11, 19/20, and 22 together into one final module.xml - and explains, just as precisely, why four seemingly obvious candidates deliberately aren't on the list.
The final sequence
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Mironsoft_Loyalty">
<sequence>
<module name="Magento_Customer"/>
<module name="Magento_Sales"/>
<module name="Magento_Eav"/>
<module name="Magento_Catalog"/>
<module name="Magento_Company"/>
</sequence>
</module>
</config>Why each individual entry
Magento_Customer(chapter 2): the foreign keycustomer_idon the points ledger (chapter 3) referencescustomer_entity.entity_id, and it's also the target of the customer attributesloyalty_points_balance/loyalty_tier(chapter 21).Magento_Sales(chapter 2): the foreign keyorder_idon the ledger referencessales_order.entity_id, and it's the target of the sales attributesloyalty_points_earned/loyalty_points_redeemed(chapters 23/63).Magento_Eav(chapter 11): the reward entity (chapters 10-18) depends on the entire EAV table set (eav_attribute,eav_attribute_set,eav_entity_type), which onlyMagento_Eavitself creates.Magento_Catalog(chapters 19/20): product and category attributes, plus - from block 9 onward - the custom product type register againstcatalog_product/catalog_categoryand against core base classes likeType\Virtual.Magento_Company(chapter 22): the company attributeloyalty_tier_overrideattaches toCompanyInterfaceviaextension_attributes.xml- without this module present, XML validation itself fails, not merelysetup:upgrade.
What's deliberately missing - and why that's not an oversight
Four more modules show up in the code without ever getting their own sequence entry: Magento_Quote (the new loyalty_points_to_redeem column, chapter 63), Magento_Checkout/Magento_Payment (the payment method, chapters 62-65), Magento_Shipping (the shipping method, chapters 66-69), and Magento_Widget/Magento_PageBuilder (chapters 55-61).
- Widget and Page Builder integrate purely through their own, purely declarative configuration files (
widget.xml,etc/pagebuilder/content_type.xml) - these mechanisms read at runtime, not during setup, so there's no race against a table that doesn't exist yet. - Payment, checkout, and shipping are registered the classic way, through
etc/config.xml(payment/*/model,carriers/*/model) andetc/di.xml- also resolved at runtime, no schema access during setup. - Quote is the one genuinely debatable case:
loyalty_points_to_redeem(chapter 63) is appended to the foreignquotetable viadb_schema.xml- the same kind of dependency as the ledger. Chapter 63 explicitly justifies skipping its own entry by noting thatMagento_Quoteitself internally depends onMagento_Customer,Magento_Sales, andMagento_Eav- all three already sit in our ownsequence. Since Magento's module sorter combines thesequencedeclarations of every installed module into one global order,Magento_Quoteends up transitively ahead ofMironsoft_Loyaltyanyway.
Achtung: This transitive reasoning is a deliberate, documented trade-off of this series - not a general rule for your own practice. It only works as long as Magento's core modules don't change their own internal dependencies, which is outside your control. For a real production module, the more robust choice is almost always an explicit, direct sequence entry for every table you actually touch - even when it supposedly already follows transitively.
What the sequence actually controls - and what it doesn't
A common misconception: sequence does not control whether a plugin, observer, or preference from a foreign module takes effect at runtime - that's governed entirely by di.xml/events.xml, independent of module order. sequence only affects setup: the order in which setup:upgrade applies db_schema.xml changes and runs data patches. A plugin on a foreign class therefore, unlike a foreign-key column, fundamentally doesn't need its own sequence entry just because the target module has to exist - a Composer require already covers that.
Tipp: A rule of thumb for your own practice: add a sequence entry exactly when your own db_schema.xml or a data patch (EavSetup, SalesSetup, a foreign table) touches schema elements of another module. For purely runtime extensions (plugin, observer, preference, widget, payment method), the Composer dependency is enough.
With a correct sequence in place, the last open question of the base architecture is settled - chapter 99 turns next to the order in which a module this large actually gets rolled out to production.