Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Controlling Observer Order and Priority

Controlling Observer Order and Priority

~6 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

Chapter 35 showed that a single event can have several independent observers - mironsoft_loyalty_customer_tier_changed could interest a marketing module AND an analytics module at the same time. What if their relative order actually matters? The short answer: unlike plugins, events.xml has no explicit sortOrder attribute. This chapter explains what the order depends on instead.

Order within one file

If the same events.xml file registers several <observer> nodes for the same <event>, they run in exactly the order they appear in the document - top to bottom, predictable.

Order across modules

If two different modules each register an observer on the same event, module load order determines execution order - the same topological sort driven by <sequence> entries in module.xml that also governs di.xml and layout merges. A module that needs to load after Mironsoft_Loyalty simply adds it to its own sequence:

<!-- app/code/Vendor/Analytics/etc/module.xml, illustrative -->
<module name="Vendor_Analytics">
    <sequence>
        <module name="Mironsoft_Loyalty"/>
    </sequence>
</module>

This guarantees that Vendor_Analytics's observer on mironsoft_loyalty_customer_tier_changed always runs after any observer Mironsoft_Loyalty itself might register on the same event - without touching a single line of events.xml.

The name attribute as a merge key

Two <observer> nodes with the same name - even from different modules - merge into a single one, exactly like block and layout XML. A later-loading module can therefore disable a third-party observer via disable="true" or override its instance, without touching that module's source code.

<!-- Disabling another module's observer, illustrative -->
<event name="mironsoft_loyalty_customer_tier_changed">
    <observer name="vendor_marketing_notify_tier_upgrade" disable="true"/>
</event>

The shared attribute

By default (shared="true", implicit), Magento reuses a single observer instance across every dispatch - harmless for AwardPointsOnOrderPlaced and ReversePointsOnCreditmemoSave, since both work purely through constructor injection and hold no mutable state between calls - the same statelessness PointsCalculator (chapter 5) has had from the start. shared="false" forces a fresh instance per dispatch and would only matter if an observer actually held its own mutable state between execute() calls - that never happens anywhere in this module.

When order genuinely matters

  1. Adjust module sequence (see above) - but this affects every shared event between the two modules, not just the one that currently matters.
  2. Merge both concerns into a single observer instead of relying on ordering between two separate ones - eliminates the problem, at the cost of decoupling.
  3. Move truly order-critical logic to a plugin, which does have an explicit, numeric sortOrder (chapter 37 leads directly there).

Tipp: These three options form a ranking, not an equal menu: try option 1 first, reach for option 2 only with genuine substantive cohesion between both concerns, and treat option 3 as a last resort for when order is truly a core requirement - not just a convenient assumption.

That nearly wraps up block 4's content - chapter 37 explicitly summarizes the "observer or plugin?" decision that's been implicit throughout, before block 5 starts covering plugins in detail.