Skip to content
intermediate Phase 9 · DI & Patterns

Service Layer Pattern

Service Layer pattern for business logic encapsulation and how Magento service contracts implement this pattern

45m
0 problems
Topic Progress 0%

What is the Service Layer?

Definition

A Service Layer defines an application's boundary with a layer of services that receives requests (from controllers, CLI, or other services) and coordinates responses. It contains business logic but not persistence logic.

Layered Architecture

┌─────────────────────┐
│   Controllers / API  │  ← Receives HTTP requests
├─────────────────────┤
│   Service Layer      │  ← Business logic (THIS LAYER)
├─────────────────────┤
│   Domain / Models    │  ← Domain entities, value objects
├─────────────────────┤
│   Data Access (Repo) │  ← Persistence (repositories, resource models)
└─────────────────────┘

Service Layer vs Repository

Aspect Service Layer Repository
Responsibility Business logic, orchestration Data access, CRUD
Methods placeOrder(), calculateDiscount() get(), save(), getList()
Knows about Business rules, workflows Database schema, queries
Examples OrderService, CartService ProductRepository, OrderRepository

Example: Order Service Layer

namespace Vendor\Sales\Api;

interface OrderManagementInterface
{
    public function place(\Magento\Sales\Api\Data\OrderInterface $order): string;
    public function cancel(string $orderId): bool;
    public function addComment(string $orderId, string $comment): void;
}

namespace Vendor\Sales\Model;

class OrderManagement implements \Vendor\Sales\Api\OrderManagementInterface
{
    public function __construct(
        private \Magento\Sales\Api\OrderRepositoryInterface $orderRepo,
        private InventoryChecker $inventory,
        private PaymentProcessor $payment,
        private OrderNotifier $notifier,
        private ValidatorInterface $validator
    ) {}

    public function place(\Magento\Sales\Api\Data\OrderInterface $order): string
    {
        // Business Rule 1: Validate order
        $this->validator->validate($order);

        // Business Rule 2: Check inventory
        foreach ($order->getItems() as $item) {
            if (!$this->inventory->isAvailable($item->getSku(), $item->getQty())) {
                throw new \Magento\Framework\Exception\LocalizedException(
                    __("Insufficient stock for {$item->getSku()}")
                );
            }
        }

        // Business Rule 3: Process payment
        $paymentResult = $this->payment->process($order);
        if (!$paymentResult->isSuccess()) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Payment processing failed')
            );
        }

        // Business Rule 4: Save order
        $order->setStatus('processing');
        $savedOrder = $this->orderRepo->save($order);

        // Business Rule 5: Reserve inventory
        $this->inventory->reserve($order->getItems());

        // Business Rule 6: Send notification
        $this->notifier->sendOrderConfirmation($savedOrder);

        return $savedOrder->getIncrementId();
    }
}

The Service Layer orchestrates: validation → inventory → payment → persistence → notification. Each step is a business rule.

Magento Service Contracts as Service Layer

How Magento Implements Service Contracts

Magento 2's service contracts (Api/ directories) are the official Service Layer:

Vendor/Module/
├── Api/
│   ├── Data/
│   │   └── EntityInterface.php      # Data transfer object
│   └── EntityRepositoryInterface.php # CRUD operations
├── Model/
│   ├── Data/
│   │   └── Entity.php               # Data implementation
│   └── EntityRepository.php          # Repository implementation

Complete Example: Review Service

// 1. Data Interface (DTO)
namespace Vendor\Review\Api\Data;

interface ReviewInterface
{
    public const ID = 'id';
    public const PRODUCT_ID = 'product_id';
    public const RATING = 'rating';
    public const COMMENT = 'comment';
    public const STATUS = 'status';

    public function getId(): ?int;
    public function getProductId(): int;
    public function getRating(): int;
    public function getComment(): string;
    public function getStatus(): int;

    public function setId(?int $id): ReviewInterface;
    public function setProductId(int $productId): ReviewInterface;
    public function setRating(int $rating): ReviewInterface;
    public function setComment(string $comment): ReviewInterface;
    public function setStatus(int $status): ReviewInterface;
}

// 2. Repository Interface (CRUD)
namespace Vendor\Review\Api;

interface ReviewRepositoryInterface
{
    public function getById(int $id): \Vendor\Review\Api\Data\ReviewInterface;
    public function save(\Vendor\Review\Api\Data\ReviewInterface $review);
    public function delete(\Vendor\Review\Api\Data\ReviewInterface $review);
    public function getList(\Magento\Framework\Api\SearchCriteriaInterface $criteria);
}

// 3. Service Interface (Business Logic)
namespace Vendor\Review\Api;

interface ReviewManagementInterface
{
    public function submitReview(int $productId, int $rating, string $comment): bool;
    public function approveReview(int $reviewId): bool;
    public function rejectReview(int $reviewId, string $reason): bool;
    public function getProductAverageRating(int $productId): float;
}

// 4. Service Implementation
namespace Vendor\Review\Model;

class ReviewManagement implements \Vendor\Review\Api\ReviewManagementInterface
{
    public function __construct(
        private \Vendor\Review\Api\ReviewRepositoryInterface $reviewRepo,
        private \Magento\Catalog\Api\ProductRepositoryInterface $productRepo,
        private RatingCalculator $ratingCalc,
        private ReviewNotifier $notifier
    ) {}

    public function submitReview(int $productId, int $rating, string $comment): bool
    {
        $product = $this->productRepo->getById($productId);

        if ($rating < 1 || $rating > 5) {
            throw new \Magento\Framework\Exception\LocalizedException(
                __('Rating must be between 1 and 5')
            );
        }

        $review = $this->reviewFactory->create();
        $review->setProductId($productId)
            ->setRating($rating)
            ->setComment($comment)
            ->setStatus(0); // Pending approval

        $this->reviewRepo->save($review);
        return true;
    }

    public function getProductAverageRating(int $productId): float
    {
        return $this->ratingCalc->calculate($productId);
    }
}

The three interfaces (Data, Repository, Service) together form the complete service contract.

Quiz

1. What is the primary responsibility of the Service Layer?

Question 1 options

2. How does the Service Layer differ from a Repository?

Question 2 options

3. In Magento, service contracts consist of which interfaces?

Question 3 options

Flashcards

Question

What does the Service Layer handle?

Answer

Business logic, validation, orchestration between other layers

Question

Service Layer vs Repository?

Answer

Service = business rules; Repository = data access/CRUD

Question

What are Magento's three service contract types?

Answer

Data interfaces (DTOs), Repository interfaces (CRUD), Service interfaces (logic)

Revision Notes

Key Takeaways

  • 1. Service Layer sits between controllers and data access, handling business logic
  • 2. Repositories handle CRUD; Services handle business rules and orchestration
  • 3. Magento service contracts: Data + Repository + Service interfaces
  • 4. Services coordinate between multiple repositories and external systems
  • 5. Services are injected via DI and can be mocked for testing

Interview Tips

  • Give a concrete example: OrderService places order (inventory + payment + save + notify)
  • Explain the difference between a Repository and a Service Layer
  • Discuss where validation belongs (Service Layer, not Controller)

Cheat Sheet

Service Layer:
  Receives requests → Applies business rules → Returns result
  Uses repositories for data access
  Orchestrates: validation, calculation, notification

Magento Service Contracts:
  Api/Data/XxxInterface          → DTO
  Api/XxxRepositoryInterface     → CRUD
  Api/XxxManagementInterface     → Business logic

Rule: Controllers call Services, Services use Repositories