Welcome to our blog post, where we delve into the practical realm of Extension Attributes in Adobe Commerce (formerly Magento). If you've been working with Magento 2 for a while, you've probably encountered Extension Attributes and harnessed their capabilities. However, it's surprising how often developers overlook the valuable benefits that Extension Attributes offer, especially when compared to outdated methods of entity extension.
In this article, we aim to shed light on the advantages of Extension Attributes and how they can enhance your development projects. We'll explore their integration with the Service Layer, providing you with insights on how to write clean, modular code that avoids unnecessary dependencies on other layers or extensions.
Whether you're an experienced Adobe Commerce developer seeking to broaden your understanding or a newcomer eager to learn more, this article guides you to a bit deeper understanding of Extension Attributes' place & role in Adobe Commerce.
Join us in exploring Extension Attributes. Let's dive in and discover the practical benefits they bring to your development workflow. Let's get started!
As you may know already, Adobe Commerce has four Architecture layers:
See more details here: Architectural layers overview
The service layer is vital for understanding extension attributes, especially the principle of "service contracts" that is used as a system pillar in Adobe Commerce. If you do not know about "service contracts," — read this article Service contracts and keep in mind that this is an agreement between parts of the system on the functionality list available for each party and how these functionalities could be triggered. REST API and API folder in modules are two bright examples of service contract implementation.
The service layer allows to build long term, strictly defined relations between services, both internal and external.
While developing your extension and needing to change/extend one or another functionality of the system — you would need to rely on the Service layer, not any other. Only the Service layer would be able to provide you with a warranty to be changed in the nearest perspective — so there are no troubles with upgrades all the time, as the API definition is not changed often. I'm not even mentioning the benefits of explicit entry point definition, its parameters, and predictable output.
Imagine you're working with a fundamental entity like an order in Adobe Commerce. This entity has its dedicated table called
sales_order and a considerable list of predefined properties. But what if you find
yourself in need of adding another property to the order entity?
Sure, you could take the easy route and directly modify the sales_order table,
implementing custom observers and models to handle the new property. However, doing so would take you into the Domain layer of
another extension, which isn't good. The domain layer of other modules is the last resort for an extension, as they are changed
quite often by the vendor. It means meddling with the system's innerworkings — a system you are out of control of, risking
compatibility issues during future upgrades.
Open–closed principle —
the system should be closed for direct changes but open for extending. In this case, modifying the entity directly would be
considered changing the system controllable by the vendor Magento — not you.
So, how can we add a property to the entity using the "right" tools?
The answer lies in the power of Extension Attributes. 🙌
Extension Attributes provide a sanctioned approach for extending entities without tampering with the core system's code. Instead of directly modifying the entity table, we leverage Extension Attributes to add new properties in a clean and modular way. By utilizing this method, we ensure our modifications align with the principles of the system's architecture, reducing the risk of compatibility issues and enabling smoother upgrades in the long run.
In the following chapter, we'll explore how Extension Attributes work hand in hand with the Service Layer, enabling you to extend entities effectively and maintain a flexible and scalable codebase.
Extension Attributes provide a "legitimate" way to modify Service contracts while maintaining backward compatibility for both providers and consumers. Although a simplified definition, it captures the essence of Extension Attributes in Adobe Commerce.
Adobe Commerce adopts the principle of "composition over inheritance" to achieve this flexibility within its development ecosystem.
Each entity supporting Extension Attributes has a setter and getter for the Extension object. In the case of orders, it is an
instance of a class that implements
\Magento\Sales\Api\Data\OrderExtensionInterface.
The behavior of the Extension object is configured through simple and straightforward XML configurations. It is in this configuration where we specify which extension attributes need to be added, define their types, and associate them with the respective entities.
For example:
<!-- @see vendor/magento/module-sales/etc/extension_attributes.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
<extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
<attribute code="shipping_assignments"
type="Magento\Sales\Api\Data\ShippingAssignmentInterface[]" />
</extension_attributes>
</config>
How does Adobe Commerce know that the order entity supports extension attributes?
Well, \Magento\Sales\Api\Data\OrderInterface extends
\Magento\Framework\Api\ExtensibleDataInterface, which informs Adobe Commerce that the
order entity will have Extension Attributes. The XML configuration specifies that the property
shipping_assignments is one of those Extension Attributes.
Now that Adobe Commerce is aware of the Extension Attributes, what comes next?
It generates \Magento\Sales\Api\Data\OrderExtensionInterface in the
generated/code folder based on the collected XML configurations from
extension_attributes.xml.
The generated interface looks like this:
<?php
interface OrderExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
{
/**
* @return \Magento\Sales\Api\Data\ShippingAssignmentInterface[]|null
*/
public function getShippingAssignments();
/**
* @param \Magento\Sales\Api\Data\ShippingAssignmentInterface[] $shippingAssignments
* @return $this
*/
public function setShippingAssignments($shippingAssignments);
// ...
}
Additionally, the implementation of the contract/interface,
\Magento\Sales\Api\Data\OrderExtension, is also generated.
In essence, Adobe Commerce has automatically generated a new Service Contract for us based on our configurations.
We can now use this contract to effortlessly set and retrieve values of Extension Attributes for the order entity,
without the need to navigate through other layers apart from the Service Layer of the
Magento_Sales module.
This outstanding move allows your newly added Extension Attribute to become an integral part of the Service Contracts and Service Layer. It remains independent of the implementation details of any modules, extensions, or the system itself that you do not have control over.
Congratulations on leveraging this remarkable feature of Adobe Commerce!
In the next chapter, we'll explore how to not get unwanted dependencies while further implementing Extension Attributes.
It's not always necessary to save Extension Attribute data persistently. Sometimes, it suffices to calculate their values on the fly when needed. However, let's assume we do need to save an attribute.
The Adobe Docs article, Add extension attributes to entities, provides a comprehensive guide on how to achieve this. Let's go through a quick recap. To add data to an Extension Attribute, you typically follow these steps:
While there can be variations, this is the main scenario, requiring at least two plugins for saving and loading operations.
Now, that's a brilliant question that leaves many stumped, but not you! 😄
One option is to save the Extension Attribute data in the same table as the entity — for example, the
sales_order table for orders. However, this approach introduces a dependency on the
Domain Layer of the Magento_Sales module.
And yes, you're right — it's not "legal" in our paradigm. We aim to extend the software, not modify it.
In my opinion, the best approach is to have your own entity to save and retrieve the Extension Attribute data. It doesn't matter what persistence layer you choose — whether it's a database, session, file, API, carrier pigeons, or even Morse code written on paper.
It may seem like overkill, but it's the most sensible way to avoid unwanted dependencies on unmodifiable parts of the system. Your Extension Attribute implementation should not impact the core system or violate the Open-Closed principle. By cutting down all connections and dependencies on the extendable module, except for the Extension Attributes definition, you ensure the integrity of the Service Contracts and keep the system functioning as intended.
This approach allows you to maintain a clean and decoupled codebase, free from unnecessary entanglements with uncontrollable system components.
As we conclude this article on Extension Attributes in Adobe Commerce, let's summarize the key takeaways:
By adhering to these takeaways, you can build robust and scalable extensions that align with the principles of Adobe Commerce's architecture. Remember, the Service Layer and Service Contracts offer a stable foundation, while Extension Attributes provide a flexible and effective approach to extending functionality.
Thank you for joining us in exploring Extension Attributes in Adobe Commerce. We hope you found this article informative and insightful.
Happy developing!
Connect with Vlad, our Adobe Commerce expert, to optimize your codebase with Extension Attributes.