8

In the Rules for using dependency injection, the Magento 2 devdocs state:

Newable objects should not hold a field reference to an injectable object nor should they request one in their constructor. This is a Law of Demeter violation.

I understand that this is a good goal to have, but how is this actually possible with Magento 2 models?

If we have a look at the Customer module, which has been presented as shining example for the new architecture, the customer model constructor signature looks like this:

public function __construct(
    \Magento\Framework\Model\Context $context,
    \Magento\Framework\Registry $registry,
    \Magento\Store\Model\StoreManagerInterface $storeManager,
    \Magento\Eav\Model\Config $config,
    \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
    \Magento\Customer\Model\ResourceModel\Customer $resource,
    \Magento\Customer\Model\Config\Share $configShare,
    \Magento\Customer\Model\AddressFactory $addressFactory,
    \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressesFactory,
    \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
    GroupRepositoryInterface $groupRepository,
    \Magento\Framework\Encryption\EncryptorInterface $encryptor,
    \Magento\Framework\Stdlib\DateTime $dateTime,
    CustomerInterfaceFactory $customerDataFactory,
    DataObjectProcessor $dataObjectProcessor,
    \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
    \Magento\Customer\Api\CustomerMetadataInterface $metadataService,
    \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
    \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
    array $data = []
)

And Magento\Framework\Model\Context alone, which is used by all models, takes five injectable arguments.

Apparently models, although newable, are not a good example at all for this rule.

I want to follow best practices with my own classes (not even models), but I need access to things like repositories of related entities or to the event manager. What would be the preferred way to handle this without breaking the rule from above?

I currently tend to ignore it or at best see it as friendly guideline, not as a rule.

2

In this example, I think a big part of the models logic must be extracted into external service classes, which execute code on the model.

This way, when for example loading the model through the repository, the repository class would have the dependencies on the new service classes which would have the dependencies on the classes that are currently in the constructor of the model.

I think as extension developers we could try to stick by the rules, but there is still much refactoring to be done by magento.

===edit===

We found something new. In the Customer Module, the Model is divided into a Data Model and a Service Class (the original Model).

For the DataInterface, now the DataModel is injected: https://github.com/magento/magento2/blob/develop/app/code/Magento/Customer/etc/di.xml#L17

The Data Model only contains the data and no business logic: https://github.com/magento/magento2/blob/develop/app/code/Magento/Customer/Model/Data/Customer.php

So now the original Model can be seen as a Service Class containing the business logic only making it totally O.K. that it references other injectables.

  • Good find. But the original model is still not injectable. Maybe this is just one step of refactoring? And we should not confuse "no dependencies to services" with "no business logic". Dumbing down models to plain data objects is going the other extreme and IMHO not something to strive for. – Fabian Schmengler Jun 7 '16 at 19:21
  • 1
    ok, but it's hard to include much business logic without injecting injectables (this sounds confusing). So I think it's a good step to divide them. The original model becomes an injectable as soon as it looses it's identity doesn't it? I think deprecating the save and load methods and extracting the data model was the first step doing this. Next step would be maybe getting rid of the complete "old" model and delegate the functionalities to more suited, smaller classes – David Verholen Jun 7 '16 at 19:31
1

We are trying to get people away from accessing the code inside a module directly, and instead going through a more controlled, well defined API. The goal is to allow smoother updates between releases - the more people use a defined API, the smoother upgrades will be.

An important concept in the di.xml files is the "preference" element. It allows code to reference interface definitions (and thus not be tied to a specific implementation), but when you ask for an instance it knows which class to use.

So if you want a CustomerInterface instance (that is a class that implements the interface), then you ask for an instance of the interface (you don't go looking for the implementation class). We will look for a element for you, and use that to identify the implementation class. That way another module could replace the di.xml file preference, and swap in a different implementation class (if there is some good reason to do so).

Also, when you create objects via object manager in this way, you do not have to specify the arguments to the constructor list. The object manager again uses the di.xml file to find all the required data structures and supplies them automatically to the constructor.

The difference between "service classes" and "data models" in a separate reply is "service classes" (which I assume means the interfaces in the Api directory) provide methods to be called. The "data models" (which I assume means the interfaces in the Api/Data directory) are data structures passed to/from "service classes". The goal is for someone outside the module to not need to know what class is implementing the interfaces. It could be a model, it could be a value object, it should not matter as long as you program to the interfaces and never mention class names directly (other than in the di.xml file).

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.