Factory Pattern in Magento 2: Object Creation the Right Way

Introduction

Imagine a feature where you need to create product instances dynamically based on user input.

Your first instinct might be:

$product = new \Magento\Catalog\Model\Product();

This creates multiple problems:

  • Tight coupling to a concrete class
  • Breaks Magento’s Dependency Injection (DI) principles
  • Hard to test (no mocking possible)
  • Future upgrades become riskyIn Magento, you should NEVER instantiate models using new directly.
    This is where the Factory Pattern comes in.

What Is the Factory Pattern?

The Factory Pattern is a creational design pattern used to create objects without exposing instantiation logic.
Structure:
Client → Factory → Object Instance
Why it matters in Magento:

  • Decouples business logic from concrete classes
  • Ensures fresh instances (no shared mutable state)
  • Enables testability via mocking
  • Aligns with Magento’s DI system

How Magento 2 Implements Factories

Auto-Generated Factories (Important Correction)

You DO NOT need to define factories in di.xml.
This is unnecessary:

Correct approach: Just inject the factory

use Magento\Catalog\Model\ProductFactory;

public function __construct(
  ProductFactory $productFactory
) {
   $this->productFactory = $productFactory;
}

// Magento automatically generates:
generated/code/Magento/Catalog/Model/ProductFactory.php

How the Generated Factory Works

Magento internally generates something like:

public function create(array $data = [])
{
   return $this->_objectManager->create(
       \Magento\Catalog\Model\Product::class,
       $data
   );
}

Note:

  • Uses $data (not $arguments)
  • Fully qualified class constant
  • Managed by Object Manager internally (never use directly)

Important Modern Magento Best Practice

Do NOT use ProductFactory for creating products in business logic
Magento now strongly promotes:

Service Contracts + Repositories

Factories are for:

  • Internal model instantiation
  • Framework-level usage

For business logic (like product creation):
Use \Magento\Catalog\Api\Data\ProductInterfaceFactory

Updated Real Example: Product Creation

Service Class (Modern Approach)


namespace Vendor\ProductImport\Model;

use Magento\Catalog\Api\Data\ProductInterfaceFactory;
use Magento\Catalog\Api\ProductRepositoryInterface;

class ProductCreator
{
   private ProductInterfaceFactory $productFactory;
   private ProductRepositoryInterface $productRepository;

   public function __construct(
       ProductInterfaceFactory $productFactory,
       ProductRepositoryInterface $productRepository
   ) {
       $this->productFactory = $productFactory;
       $this->productRepository = $productRepository;
   }

   public function createFromData(array $data)
   {
       $product = $this->productFactory->create();

       $product->setSku($data['sku'] ?? '');
       $product->setName($data['name'] ?? '');
       $product->setPrice((float)($data['price'] ?? 0));
       $product->setAttributeSetId($data['attribute_set_id'] ?? 4);
       $product->setTypeId($data['type_id'] ?? 'simple');
       $product->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED);
       $product->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH);

       return $this->productRepository->save($product);
   }
}

Why this is correct:

  • Uses interface factory (recommended)
  • Works with service contracts
  • Future-proof and upgrade-safe

Key Correction: Factory vs Repository

Your original statement:
“Don’t inject ProductFactory alongside a repository.”

This is incorrect / misleading

Correct rule:

  • Use Factory → create new instance
  • Use Repository → persist / fetch

They are used together, not mutually exclusive.

Real-World Use Cases

1. Collections (Correct Example)

public function getProductsByCategory($categoryId)
{
   $collection = $this->productCollectionFactory->create();

   $collection->addCategoriesFilter(['in' => $categoryId]);
   $collection->addAttributeToFilter(
       'status',
       \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
   );

   return $collection;
}

Fixed:

  • Correct filter method
  • Avoid magic numbers (1)

2. Shipment Creation (Better Practice Note)

Your shipment example is valid but incomplete.
In real Magento:

  • Use ShipmentFactory + ShipmentRepository
  • Often handled via service classes (ShipmentManagementInterface)

Common Mistakes

Mistake: Using Model Factory Instead of Interface Factory

// Not recommended for service layer
\ProductFactory $productFactory
Use:

ProductInterfaceFactory $productFactory

Mistake: Using ObjectManager Directly
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$product = $objectManager->create(…);

Strictly forbidden in production code.

Mistake: VirtualType for Factory Customization

Factories are auto-generated → customization via virtualType is rarely needed and often overengineering.

When to Use Factories

Use when:

  • Creating multiple instances (loops, batch jobs)
  • Working with collections (CollectionFactory)
  • Using interface-based models (*InterfaceFactory)
  • Avoiding ObjectManager

When NOT to Use Factories

Do NOT use when:

  • You can fetch via repository (getById, getList)
  • Working with stateless services
  • You need shared instances (use DI instead)

Factories & Magento Upgrades

Your concept is correct, but slightly exaggerated.
Accurate statement:
Factories help because:

  • Constructor changes are abstracted
  • DI container handles dependencies
  • Reduces direct dependency on concrete classes

But:

  • They do NOT eliminate all upgrade issues
  • API changes (interfaces) still affect code

Factory vs Proxy (Clarified)

Pattern Purpose
Factory Create new instances
Proxy Lazy-load heavy dependencies

Example:

 
   name="productRepository" 
   xsi:type="object">
   Magento\Catalog\Api\ProductRepositoryInterface\Proxy

Conclusion

The Factory Pattern in Magento is not optional it’s foundational.
But modern Magento development requires a balanced approach:

Follow this hierarchy:

  1. Service Contracts (Interfaces)
  2. Repositories (Persistence)
  3. Factories (Instance creation)

Key Takeaways:

  • Prefer *InterfaceFactory over model factories
  • Use factories + repositories together
  • Never use new or ObjectManager
  • Keep services small and testable
  • Align with Magento’s DI architecture