PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

GoF patterns in Magento 2 – where to find them and how they work

by Henryk Tews / Tuesday, 21 June 2022 / Published in Magento 2, Wzorce projektowe

After covering all 23 GoF patterns individually, time to see where they actually live inside Magento 2. The platform is a textbook example of design patterns applied at scale – DI, plugins, observers, repositories, factories all map directly to specific GoF patterns. This post is a guided tour of patterns hiding in plain sight in Magento’s architecture.

Creational patterns in Magento 2

Factory Method – *Factory classes

<?php

// Every *Factory class is a Factory Method
// Magento auto-generates them from model class names

use Magento\Catalog\Model\ProductFactory;
use Magento\Sales\Model\Order\ItemFactory;

class MyService
{
    public function __construct(
        private ProductFactory $productFactory,
        private ItemFactory $itemFactory
    ) {}

    public function createProduct(): \Magento\Catalog\Model\Product
    {
        // create() is the factory method - always returns a new instance
        return $this->productFactory->create(['data' => ['sku' => 'NEW-001']]);
    }
}

Builder – SearchCriteriaBuilder

<?php

// SearchCriteriaBuilder is a textbook Builder
$criteria = $this->searchCriteriaBuilder
    ->addFilter('status', 1)
    ->addFilter('price', 100, 'gt')
    ->addSortOrder($this->sortOrderBuilder->setField('name')->setAscendingDirection()->create())
    ->setPageSize(20)
    ->setCurrentPage(1)
    ->create(); // builds the immutable SearchCriteria object

Prototype – Object Manager cloneObject

<?php

// Magento uses Prototype for quote item duplication
// When splitting an order, quote items are cloned
$clonedItem = clone $originalItem; // PHP's built-in clone = Prototype

Structural patterns in Magento 2

Decorator – Plugins (Interceptors)

<?php

// Every Magento plugin IS a Decorator
// It wraps a method call and adds behaviour before/after/around

namespace Vendor\Module\Plugin;

class ProductPricePlugin
{
    // "around" plugin = classic Decorator pattern
    public function aroundGetPrice(
        \Magento\Catalog\Model\Product $subject,
        callable $proceed
    ): float {
        $price = $proceed(); // call the real method (wrapped object)
        return $price * 0.9; // add behaviour (10% discount)
    }
}
// Magento compiles this into an Interceptor class - the generated Decorator

Proxy – generated \Proxy classes

<!-- In di.xml - declare Proxy for lazy-loading an expensive service -->
<type name="Vendor\Module\Console\HeavyCommand">
    <arguments>
        <argument name="heavyService" xsi:type="object">
            Vendor\Module\Model\HeavyService\Proxy
        </argument>
    </arguments>
</type>
<!-- HeavyService is not instantiated until the first method call -->

Composite – Layout blocks and category tree

<?php

// Layout rendering uses Composite:
// AbstractBlock can contain child blocks
// Rendering the parent renders all children recursively

$block = $layout->getBlock('product.info.main');
$block->append($layout->createBlock('Magento\Catalog\Block\Product\View\Description'));
$block->append($layout->createBlock('Magento\Catalog\Block\Product\View\Attributes'));

// toHtml() renders self + all children - Composite pattern
echo $block->toHtml();

Behavioural patterns in Magento 2

Observer – events.xml system

<?php

// Magento's EventManager is the Subject (Publisher)
$this->_eventManager->dispatch('catalog_product_save_after', ['product' => $product]);

// Any module can register an Observer in events.xml
// The EventManager notifies all of them - classic Observer/Pub-Sub

Strategy – shipping carriers and payment methods

<?php

// Each shipping carrier implements CarrierInterface - the Strategy interface
// ShippingModel selects and uses the active carrier without knowing its type

$rates = $this->shippingModel->collectCarrierRates(
    $carrier,   // Strategy: FlatRateCarrier, DHLCarrier, UPSCarrier...
    $request
);

Template Method – AbstractModel and AbstractBlock

<?php

// AbstractModel defines _beforeSave() and _afterSave() hooks
// Subclasses override them to add behaviour

class MyModel extends \Magento\Framework\Model\AbstractModel
{
    protected function _beforeSave(): static
    {
        $this->setUpdatedAt(date('Y-m-d H:i:s'));
        return parent::_beforeSave(); // call template method
    }

    protected function _afterSave(): static
    {
        // additional logic after save
        return parent::_afterSave();
    }
}

State – Order state machine

<?php

// Order transitions through states: new -> processing -> complete/closed/cancelled
// Each state permits or blocks certain actions

$order->getState(); // 'processing', 'complete', 'cancelled', etc.
$order->setState(\Magento\Sales\Model\Order::STATE_PROCESSING);
$order->setStatus('processing');

// Magento validates state transitions internally
// You cannot complete an order that is in 'new' state without going through payment

Pattern density in a single request

A typical Magento product page request uses all of the following patterns simultaneously:

  • Factory Method – creating product and collection instances
  • Builder – SearchCriteria for related products
  • Decorator – Plugins modifying product data
  • Proxy – lazy-loading optional services
  • Observer – layout generation events
  • Strategy – tax calculation strategy
  • Composite – block tree rendering
  • Template Method – block _toHtml() lifecycle

Summary

Magento 2’s architecture is a reference implementation of the GoF patterns in a large PHP application. Understanding the patterns makes the platform less mysterious – when you see a *Factory class, a plugin, an events.xml observer, or a \Proxy suffix, you immediately know the intent and the constraints. Pattern knowledge is not academic overhead; it is the vocabulary that makes working with Magento predictable.

About Henryk Tews

What you can read next

Strategy pattern in PHP – and how Magento 2 uses it in pricing
Repository pattern – interface, implementation, SearchCriteria, testing with mock
Interpreter pattern – own discount rule grammar, parser, expression tree

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}