Skip to content
advanced Phase 99 · Architecture Principles

Architecture Principles for Magento

Architecture principles including clean architecture, hexagonal architecture, domain-driven design, and their application in Magento

1h
0 problems
Topic Progress 0%

Clean Architecture

Clean Architecture Layers

External Layer (Frameworks)
  |
  +-- Interface Adapters
  |     +-- Controllers
  |     +-- Gateways
  |     +-- Presenters
  |
  +-- Application Business Rules
  |     +-- Use Cases
  |     +-- Application Services
  |
  +-- Enterprise Business Rules
        +-- Entities
        +-- Domain Models
        +-- Value Objects

Magento Clean Architecture Example

// Entity (Domain Layer)
class Order
{
    private OrderId $id;
    private CustomerId $customerId;
    private Money $total;
    private OrderStatus $status;
    
    public function __construct(
        OrderId $id,
        CustomerId $customerId,
        Money $total,
        OrderStatus $status
    ) {
        $this->id = $id;
        $this->customerId = $customerId;
        $this->total = $total;
        $this->status = $status;
    }
    
    public function cancel(): void
    {
        if ($this->status->equals(OrderStatus::SHIPPED)) {
            throw new \DomainException('Cannot cancel shipped order');
        }
        $this->status = OrderStatus::CANCELLED;
    }
}

// Use Case (Application Layer)
class PlaceOrderUseCase
{
    private $orderRepository;
    private $paymentService;
    
    public function execute(PlaceOrderCommand $command): OrderId
    {
        $order = new Order(
            OrderId::generate(),
            $command->getCustomerId(),
            $command->getTotal(),
            OrderStatus::PENDING
        );
        
        $this->paymentService->charge($order);
        $this->orderRepository->save($order);
        
        return $order->getId();
    }
}

// Controller (Interface Adapter)
class CreateOrderController
{
    private $placeOrderUseCase;
    
    public function execute(Request $request): Response
    {
        $command = new PlaceOrderCommand(
            $request->getCustomerId(),
            $request->getItems()
        );
        
        $orderId = $this->placeOrderUseCase->execute($command);
        
        return new JsonResponse(['order_id' => $orderId->getValue()]);
    }
}

Dependency Rule

External -> Adapters -> Use Cases -> Entities

Dependencies point INWARD
Entities depend on NOTHING
Use Cases depend on Entities
Adapters depend on Use Cases

Key Takeaway

Clean architecture separates concerns into layers. Dependencies point inward. Entities contain business rules, use cases orchestrate, adapters handle external concerns.

Hexagonal Architecture

Hexagonal (Ports and Adapters) Architecture

         +------------------+
         |   Application    |
         |                  |
    +----+----+       +----+----+
    |  Port   |       |  Port   |
    | (Input) |       | (Output)|
    +----+----+       +----+----+
         |                  |
    +----+----+       +----+----+
    | Adapter |       | Adapter |
    | (Web)   |       | (DB)    |
    +---------+       +---------+

Ports (Interfaces)

// Input Port (Use Case Interface)
interface PlaceOrderInterface
{
    public function execute(PlaceOrderCommand $command): OrderId;
}

// Output Port (Repository Interface)
interface OrderRepositoryInterface
{
    public function save(Order $order): void;
    public function findById(OrderId $id): ?Order;
}

// Output Port (Payment Gateway)
interface PaymentGatewayInterface
{
    public function charge(Order $order): PaymentResult;
}

Adapters (Implementations)

// Web Adapter (Controller)
class RestCreateOrderController
{
    private PlaceOrderInterface $placeOrder;
    
    public function __construct(PlaceOrderInterface $placeOrder)
    {
        $this->placeOrder = $placeOrder;
    }
    
    public function execute(): Response
    {
        $command = $this->buildCommand();
        $orderId = $this->placeOrder->execute($command);
        return new JsonResponse(['order_id' => $orderId->getValue()]);
    }
}

// Database Adapter (Repository)
class MysqlOrderRepository implements OrderRepositoryInterface
{
    private $resource;
    
    public function save(Order $order): void
    {
        $connection = $this->resource->getConnection();
        $connection->insert('sales_order', [
            'entity_id' => $order->getId()->getValue(),
            'customer_id' => $order->getCustomerId()->getValue(),
            'total' => $order->getTotal()->getAmount(),
            'status' => $order->getStatus()->getValue(),
        ]);
    }
}

// GraphQL Adapter
class GraphQLPlaceOrderResolver
{
    private PlaceOrderInterface $placeOrder;
    
    public function resolve($root, $args): array
    {
        $command = new PlaceOrderCommand($args['customer_id'], $args['items']);
        $orderId = $this->placeOrder->execute($command);
        return ['order_id' => $orderId->getValue()];
    }
}

Key Takeaway

Hexagonal architecture uses ports (interfaces) and adapters (implementations). The application core is independent of external concerns like web framework, database, or API.

Domain-Driven Design

DDD Building Blocks

Domain
  |
  +-- Entities (Order, Product, Customer)
  +-- Value Objects (Money, Address, OrderId)
  +-- Aggregates (Order + OrderItems)
  +-- Domain Events (OrderPlaced, PaymentReceived)
  +-- Repositories (OrderRepository)
  +-- Services (ShippingService)

Entities and Value Objects

// Entity - has identity
class Order
{
    private OrderId $id;  // Identity
    // ...
}

// Value Object - no identity, immutable
class Money
{
    private int $amount;
    private string $currency;
    
    public function __construct(int $amount, string $currency)
    {
        $this->amount = $amount;
        $this->currency = $currency;
    }
    
    public function add(Money $other): Money
    {
        if ($this->currency !== $other->currency) {
            throw new \DomainException('Cannot add different currencies');
        }
        return new Money($this->amount + $other->amount, $this->currency);
    }
}

Aggregates

// Aggregate Root
class Order
{
    private OrderId $id;
    private array $items = [];
    
    public function addItem(ProductId $productId, int $quantity, Money $price): void
    {
        $this->items[] = new OrderItem($productId, $quantity, $price);
    }
    
    public function removeItem(ProductId $productId): void
    {
        $this->items = array_filter(
            $this->items,
            fn($item) => !$item->getProductId()->equals($productId)
        );
    }
    
    public function getTotal(): Money
    {
        $total = new Money(0, 'USD');
        foreach ($this->items as $item) {
            $total = $total->add($item->getSubtotal());
        }
        return $total;
    }
}

Domain Events

// Event
class OrderPlaced
{
    private OrderId $orderId;
    private CustomerId $customerId;
    private Money $total;
    
    public function __construct(OrderId $orderId, CustomerId $customerId, Money $total)
    {
        $this->orderId = $orderId;
        $this->customerId = $customerId;
        $this->total = $total;
    }
}

// Event Dispatcher
class OrderPlacedHandler
{
    public function __construct(
        private SendOrderConfirmation $sendConfirmation,
        private UpdateInventory $updateInventory
    ) {}
    
    public function handle(OrderPlaced $event): void
    {
        $this->sendConfirmation->execute($event->getOrderId());
        $this->updateInventory->execute($event->getOrderId());
    }
}

Key Takeaway

DDD identifies entities, value objects, aggregates, and domain events. Aggregates maintain consistency boundaries. Domain events decouple side effects from business logic.

Applying Principles in Magento

Module Structure

app/code/Vendor/Order/
  |
  +-- Api/
  |     +-- Data/           # Value Objects (DTOs)
  |     +-- OrderRepositoryInterface.php  # Port
  |
  +-- Domain/
  |     +-- Model/          # Entities
  |     +-- Event/          # Domain Events
  |     +-- Service/        # Domain Services
  |
  +-- Application/
  |     +-- UseCase/        # Use Cases
  |     +-- Service/        # Application Services
  |
  +-- Infrastructure/
  |     +-- Repository/     # Repository Implementations
  |     +-- Gateway/        # External Service Adapters
  |
  +-- Api/Http/             # REST Controllers (Adapters)
  +-- Api/GraphQl/          # GraphQL Resolvers (Adapters)
  +-- Observer/             # Event Handlers

Service Contracts (Ports)

// Api/Data/OrderInterface.php (Value Object)
interface OrderInterface
{
    public function getOrderId(): ?int;
    public function getCustomerId(): ?int;
    public function getStatus(): ?string;
}

// Api/OrderRepositoryInterface.php (Port)
interface OrderRepositoryInterface
{
    public function save(OrderInterface $order): OrderInterface;
    public function get(int $orderId): OrderInterface;
    public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface;
}

Plugin System (Adapters)

// Plugin for extending behavior
class OrderPlugin
{
    public function aroundPlace(
        Order $subject,
        callable $proceed,
        OrderInterface $order
    ): OrderInterface {
        // Before
        $this->validateOrder($order);
        
        // Execute
        $result = $proceed($order);
        
        // After
        $this->sendNotification($result);
        
        return $result;
    }
}

Repository Pattern (Adapters)

// Repository Implementation
class OrderRepository implements OrderRepositoryInterface
{
    public function save(OrderInterface $order): OrderInterface
    {
        $connection = $this->resource->getConnection();
        $data = $this->toArray($order);
        
        if ($order->getOrderId()) {
            $connection->update('sales_order', $data, ['entity_id' => $order->getOrderId()]);
        } else {
            $connection->insert('sales_order', $data);
        }
        
        return $order;
    }
}

Key Takeaway

Apply architecture principles in Magento through service contracts (ports), repository pattern (adapters), plugin system (cross-cutting concerns), and proper module structure.

Quiz

1. What is the dependency rule in clean architecture?

Question 1 options

2. What is a port in hexagonal architecture?

Question 2 options

3. What is an aggregate in DDD?

Question 3 options

4. What is a value object?

Question 4 options

5. How does Magento apply hexagonal architecture?

Question 5 options

Flashcards

Question

What is clean architecture?

Answer

Layered architecture with dependencies pointing inward to entities

Question

What is hexagonal architecture?

Answer

Ports (interfaces) and adapters (implementations) pattern

Question

What is an aggregate?

Answer

Cluster of entities with consistency boundary maintained by aggregate root

Question

What is a value object?

Answer

Immutable object with no identity (e.g., Money, Address)

Question

How to apply in Magento?

Answer

Service contracts as ports, repositories as adapters, plugins for cross-cutting concerns

Question

What is domain-driven design?

Answer

Design approach focused on domain model and business logic

Revision Notes

Key Takeaways

  • 1. Clean architecture: dependencies point inward to entities
  • 2. Hexagonal: ports (interfaces) and adapters (implementations)
  • 3. DDD: entities, value objects, aggregates, domain events
  • 4. Apply in Magento via service contracts and repository pattern
  • 5. Module structure follows layered architecture

Interview Tips

  • Explain clean architecture and dependency rule
  • Compare hexagonal vs layered architecture
  • Describe DDD building blocks
  • Discuss how to apply principles in Magento

Cheat Sheet

Architecture Principles

Clean Architecture:
Entities -> Use Cases -> Adapters -> Frameworks
Dependencies point INWARD

Hexagonal:
Ports (interfaces)
Adapters (implementations)
Application core is independent

DDD:
Entities: have identity
Value Objects: immutable, no identity
Aggregates: consistency boundary
Domain Events: decouple side effects

Magento Application:
Service Contracts = Ports
Repositories = Adapters
Plugins = Cross-cutting concerns