Extension Attributes in Adobe Commerce: Achieving Clean and Decoupled Code
Tags: magento2, adobecommerce, webdev, development
Published: 2023-07-13 20:21:44

### Table Of Contents
* [Introduction](#introduction)
* [Chapter 1: Understanding the Adobe Commerce Service Layer](#chapter-1-understanding-the-adobe-commerce-service-layer)
* [Chapter 2: Exploring the Idea of Extension Attributes](#chapter-2-exploring-the-idea-of-extension-attributes)
* [Chapter 3: Extension Attributes and Service Contracts](#chapter-3-extension-attributes-and-service-contracts)
* [Chapter 4: Saving Data for Extension Attributes](#chapter-4-saving-data-for-extension-attributes)
* [Chapter 5: Key Takeaways](#chapter-5-key-takeaways)

## Introduction

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!

-------

## Chapter 1: Understanding the Adobe Commerce Service Layer

As you may know already, Adobe Commerce has four Architecture layers:
* Presentation layer - point of user interaction with Adobe Commerce.
* Service layer - it is a "glue" between layers and a set of APIs used by modules and external systems to communicate with each other. Service contracts are in use here.
* Domain layer - contains the business logic of the module.
* Persistence layer - resource level, knows about DB and other storages.

See more details here: [Architectural layers overview](https://developer.adobe.com/commerce/php/architecture/layers/)

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](https://developer.adobe.com/commerce/php/development/components/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.

-------

## Chapter 2: Exploring the Idea of Extension Attributes

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 inner workings - system you are out of control, risking compatibility issues during future upgrades. [Open–closed principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed_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.

-------

## Chapter 3: Extension Attributes and Service Contracts

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.

#### Extension Object: Leveraging Composition over Inheritance

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`.

#### Configuring Extension Attributes with XML

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:
```xml
// @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>
```

#### Support for Extension Attributes: The Role of ExtensibleDataInterface
How does Adobe Commerce knows that 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.

#### Generated Service Contracts

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.

#### Summary

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` model.

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!
![Outstanding move - Adobe Commerce](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/knr8pbizzl731svw0ocd.png)


In the next chapter, we'll explore how to not get unwanted dependencies while further Extension Attributes implementation.

-------

## Chapter 4: Saving Data for 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. 

#### Main Steps for Saving Extension Attribute Data

The Adobe Docs article, [Add extension attributes to entities](https://developer.adobe.com/commerce/php/development/components/add-attributes/), 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:
1. Create a plugin that intercepts the save operation of the entity to retrieve the Extension Attribute data and save it at the resource level.
2. Create a plugin that intercepts the load operation of the entity to retrieve the Extension Attribute data and populate the entity using the Extension object.

While there can be variations, this is the main scenario, requiring at least two plugins for saving and loading operations.

#### Where to Save Extension Attribute Data?

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.

#### The Best Approach: Have Your Own Entity

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.

-------

## Chapter 5: Key Takeaways

As we conclude this article on Extension Attributes in Adobe Commerce, let's summarize the key takeaways:

1. **Service Layer and Service Contracts**: The Service Layer and Service Contracts serve as excellent foundations for extending functionality. They provide stability, predictability, and explicit definitions, making them ideal for seamless extension development.

2. **Persistence, Domain, and Presentation Layers**: On the other hand, the Persistence, Domain, and Presentation Layers are not suitable for direct extension. These layers can undergo changes with each release, making them less reliable for long-term extension development.

3. **Extension Attributes and Service Contracts**: Extension Attributes are an integral part of the Service Layer and Service Contracts. Leveraging Extension Attributes enables effective extension development and facilitates communication across services within Adobe Commerce.

4. **Avoid Saving Extension Attributes Inside the Entity**: It is crucial to refrain from saving Extension Attributes within the entity you are trying to extend. Doing so would introduce a dependency on the Persistence Layer of the extendable module, rendering the benefits of the Service Layer useless. Instead, opt for storing Extension Attribute data in a separate entity to maintain a clean and decoupled architecture.

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!