سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

CMS نویس مرجعی برای آموزش برنامه نویسی وب و PHP، توسعه افزونه‌های اختصاصی وردپرس، طراحی ورک‌فلو و اتوماسیون با n8n و توسعه CMS های اختصاصی است. هدف ما ارائه آموزش‌های کاربردی و راهکارهای حرفه‌ای برای توسعه‌دهندگان، کسب‌وکارها و علاقه‌مندان به دنیای برنامه‌نویسی است.

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
Service Layer Pattern در PHP چیست؟ آموزش الگوی Service Layer با مثال عملی

Service Layer Pattern در PHP چیست؟ آموزش الگوی Service Layer با مثال عملی

ممدیر کل سیستم ۲۵ شهریور ۱۴۰۵ ۲۲ دقیقه مطالعه ۲ بازدید
Service Layer Pattern در PHP چیست؟ آموزش الگوی Service Layer با مثال عملی

در پروژه‌های PHP، مخصوصاً پروژه‌های Laravel، معمولاً کار از یک Controller ساده شروع می‌شود.

مثلاً برای ثبت سفارش ممکن است ابتدا چنین کدی داشته باشیم:


public function store(Request $request)

{

    $product = Product::findOrFail($request->product_id);



    if ($product->stock < $request->quantity) {

        return back()->with('error', 'موجودی کافی نیست.');

    }



    $total = $product->price * $request->quantity;



    $order = Order::create([

        'user_id' => auth()->id(),

        'total' => $total,

    ]);



    $product->decrement('stock', $request->quantity);



    return redirect()

        ->route('orders.show', $order);

}

در یک پروژه کوچک شاید این کد مشکلی ایجاد نکند.

اما با بزرگ‌تر شدن پروژه، فرآیند ثبت سفارش می‌تواند شامل موارد بیشتری شود:


بررسی موجودی

↓

محاسبه قیمت

↓

اعمال تخفیف

↓

ایجاد سفارش

↓

کاهش موجودی

↓

ثبت تراکنش

↓

پرداخت

↓

ارسال Notification

↓

ثبت Log

اگر همه این منطق داخل Controller قرار بگیرد، Controller به‌مرور بسیار بزرگ و سخت‌نگهداری می‌شود.

اینجاست که مفهوم Service Layer Pattern اهمیت پیدا می‌کند.

Service Layer یک مرز مشخص برای عملیات سطح برنامه ایجاد می‌کند و می‌تواند چند جزء Domain، Repository و سایر سرویس‌ها را برای اجرای یک Use Case هماهنگ کند.


# Service Layer Pattern در PHP چیست؟

Service Layer Pattern یک الگوی معماری است که عملیات و منطق مربوط به Use Caseهای برنامه را در یک لایه یا مجموعه‌ای از Serviceها قرار می‌دهد.

هدف اصلی این است که Controller صرفاً وظیفه دریافت درخواست و برگرداندن پاسخ را انجام دهد و منطق اجرای فرآیند اصلی در Service قرار بگیرد.

به صورت ساده:


Request

   ↓

Controller

   ↓

Service Layer

   ↓

Domain / Repository / External Services

   ↓

Database

برای مثال به جای اینکه Controller خودش تمام مراحل ثبت سفارش را انجام دهد:


$orderService->createOrder($user, $data);

را صدا می‌زند.

این ایده باعث می‌شود یک عملیات مشخص برنامه یک نقطه ورود مشخص داشته باشد و بتوان آن را از چند Client یا Workflow مختلف استفاده کرد.


# Service Layer چه مشکلی را حل می‌کند؟

یکی از مشکلات رایج در پروژه‌های PHP، پراکنده شدن Application Logic است.

مثلاً فرض کنید ثبت سفارش از سه مسیر انجام می‌شود:


Web Controller

API Controller

Console Command

اگر منطق ثبت سفارش داخل Web Controller باشد، برای API باید همان منطق را دوباره بنویسیم.

مثلاً:


Web

└── create order logic



API

└── create order logic



Command

└── create order logic

این ساختار باعث Duplicate Code می‌شود.

اما با Service Layer:


Web Controller ──┐

                 │

API Controller ───┼──> OrderService

                 │

Console Command ──┘

هر سه مسیر از یک منطق مشترک استفاده می‌کنند.

Service Layer به همین دلیل می‌تواند یک نقطه ورود مشترک برای عملیات برنامه فراهم کند.


# Fat Controller چیست؟

اصطلاح Fat Controller به Controllerهایی گفته می‌شود که بیش از حد مسئولیت دارند.

برای مثال:


public function store(Request $request)

{

    // Validate



    // Find product



    // Check stock



    // Calculate price



    // Apply discount



    // Create order



    // Create order items



    // Update inventory



    // Charge payment



    // Send notification



    // Log operation

}

ممکن است این متد به صدها خط برسد.

مشکل فقط تعداد خطوط نیست.

مشکل اصلی این است که Controller به بخش‌های مختلف سیستم وابسته شده است.

مثلاً:


Controller

├── Database

├── Payment Gateway

├── Inventory

├── Notification

├── Discount

└── Logging

در نتیجه تغییر هر بخش می‌تواند Controller را تحت تأثیر قرار دهد.


# Thin Controller چیست؟

هدف Service Layer این نیست که Controller را کاملاً خالی کنیم.

Controller باید همچنان کارهای مربوط به Presentation Layer را انجام دهد.

مثلاً:


public function store(

    StoreOrderRequest $request,

    OrderService $service

) {

    $order = $service->create(

        auth()->user(),

        $request->validated()

    );



    return redirect()

        ->route('orders.show', $order);

}

Controller اینجا:

  1. Request را دریافت می‌کند.
  2. داده را Validate می‌کند.
  3. Service را صدا می‌زند.
  4. Response را برمی‌گرداند.

اما فرآیند اصلی ثبت سفارش در Service قرار دارد.


# Application Logic و Domain Logic چه تفاوتی دارند؟

این قسمت بسیار مهم است.

Service Layer الزاماً محل قرار دادن تمام Business Logic نیست.

باید بین Application Logic و Domain Logic تفاوت قائل شویم.

Application Logic

Application Logic مشخص می‌کند یک Use Case چگونه اجرا شود.

مثلاً:


Create Order

Cancel Order

Complete Payment

Register User

Publish Product

Service می‌تواند این فرآیند را هماهنگ کند.


Domain Logic

Domain Logic مربوط به قوانین خود Domain است.

مثلاً:


سفارش لغوشده دوباره قابل پرداخت نیست.



موجودی محصول نمی‌تواند منفی شود.



تخفیف VIP فقط برای کاربران واجد شرایط فعال است.

این قوانین می‌توانند در Domain Model، Value Object، Specification یا Domain Service قرار بگیرند.

پس Service Layer نباید به یک کلاس غول‌پیکر تبدیل شود که تمام منطق پروژه را در خود نگه می‌دارد.


# ساختار کلی Service Layer

یک معماری ساده می‌تواند چنین باشد:


Controller

    ↓

Service

    ↓

Repository

    ↓

Database

اما در پروژه‌های بزرگ‌تر:


Controller

    ↓

Application Service

    ↓

Domain

    ├── Entity

    ├── Value Object

    ├── Specification

    └── Domain Service

    ↓

Repository

    ↓

Database

در کنار آن ممکن است:


Payment Gateway

Notification

Queue

External API

نیز توسط Application Service هماهنگ شوند.


# پیاده‌سازی Service Layer در PHP خام

قبل از رفتن سراغ Laravel، یک مثال ساده PHP بسازیم.

فرض کنید کلاس Product داریم:


<?php



declare(strict_types=1);



final class Product

{

    public function __construct(

        public int $id,

        public string $name,

        public int $price,

        public int $stock

    ) {

    }

}

یک Repository ساده:


<?php



declare(strict_types=1);



interface ProductRepository

{

    public function findById(int $id): Product;



    public function save(Product $product): void;

}

حالا Service:


<?php



declare(strict_types=1);



final class OrderService

{

    public function __construct(

        private ProductRepository $products

    ) {

    }



    public function createOrder(

        int $productId,

        int $quantity

    ): array {

        $product = $this->products->findById($productId);



        if ($product->stock < $quantity) {

            throw new RuntimeException(

                'موجودی محصول کافی نیست.'

            );

        }



        $total = $product->price * $quantity;



        $product->stock -= $quantity;



        $this->products->save($product);



        return [

            'product_id' => $product->id,

            'quantity' => $quantity,

            'total' => $total,

        ];

    }

}

حالا Controller یا هر Client دیگری فقط Service را صدا می‌زند:


$order = $orderService->createOrder(

    productId: 10,

    quantity: 2

);


# چرا Dependency Injection در Service Layer مهم است؟

یکی از اصول مهم در طراحی Service، استفاده از Dependency Injection است.

مثلاً به جای اینکه داخل Service بنویسیم:


$this->repository = new MysqlProductRepository();

بهتر است Dependency را از بیرون دریافت کنیم:


public function __construct(

    private ProductRepository $products

) {

}

حالا Service به Interface وابسته است:


OrderService

     ↓

ProductRepository

     ↓

Implementation

نه اینکه مستقیماً به یک Implementation خاص وابسته باشد.

این ساختار تست Service را نیز ساده‌تر می‌کند.


# استفاده از Repository در Service Layer

یکی از ترکیب‌های رایج:


Controller

    ↓

Service

    ↓

Repository

    ↓

Database

است.

برای مثال:


final class OrderService

{

    public function __construct(

        private ProductRepository $products,

        private OrderRepository $orders

    ) {

    }



    public function create(

        int $productId,

        int $quantity

    ): Order {

        $product = $this->products->findById($productId);



        // Business/Application logic...



        return $this->orders->create([

            'product_id' => $product->id,

            'quantity' => $quantity,

        ]);

    }

}

در این معماری:

  • Service فرآیند را هماهنگ می‌کند.
  • Repository مسئول Persistence است.
  • Domain Object قوانین Domain را نگه می‌دارد.

این موضوع با الگوهایی مثل Repository و Data Mapper ارتباط نزدیکی دارد. Data Mapper به‌طور مشخص وظیفه انتقال داده بین Representation داخل حافظه و Data Store را بر عهده می‌گیرد و آن‌ها را از یکدیگر مستقل نگه می‌دارد.


# مدیریت Transaction در Service Layer

یکی از کاربردهای مهم Service Layer مدیریت یک فرآیند چندمرحله‌ای است.

فرض کنید ثبت سفارش شامل این عملیات باشد:


Create Order

↓

Create Order Items

↓

Decrease Stock

↓

Create Payment Transaction

اگر مرحله سوم موفق شود ولی مرحله چهارم شکست بخورد، ممکن است داده‌ها ناسازگار شوند.

بنابراین می‌توانیم Transaction را در مرز Use Case قرار دهیم.

در PHP خام، مفهوم می‌تواند به شکل زیر باشد:


$db->beginTransaction();



try {

    $order = $orderRepository->create($data);



    $inventory->decrease(

        $productId,

        $quantity

    );



    $payment->createTransaction(

        $order->id,

        $order->total

    );



    $db->commit();



    return $order;

} catch (Throwable $e) {

    $db->rollBack();



    throw $e;

}

در چنین شرایطی Service نقطه مناسبی برای هماهنگ کردن این عملیات است، چون Service مرز اجرای Use Case را مشخص می‌کند.


# پیاده‌سازی Service Layer در Laravel

حالا به Laravel برسیم.

فرض کنید ساختار پروژه:


app/

├── Http/

│   └── Controllers/

│       └── OrderController.php

│

├── Services/

│   └── OrderService.php

│

├── Models/

│   ├── Order.php

│   └── Product.php

│

└── Repositories/

    └── OrderRepository.php

می‌توانیم Service را ایجاد کنیم:


app/Services/OrderService.php

محتوا:


<?php



namespace App\Services;



use App\Models\Order;

use App\Models\Product;

use Illuminate\Support\Facades\DB;

use RuntimeException;



final class OrderService

{

    public function create(

        int $userId,

        int $productId,

        int $quantity

    ): Order {

        return DB::transaction(function () use (

            $userId,

            $productId,

            $quantity

        ) {

            $product = Product::query()

                ->lockForUpdate()

                ->findOrFail($productId);



            if ($product->stock < $quantity) {

                throw new RuntimeException(

                    'موجودی محصول کافی نیست.'

                );

            }



            $total = $product->price * $quantity;



            $order = Order::create([

                'user_id' => $userId,

                'total' => $total,

                'status' => 'pending',

            ]);



            $product->decrement(

                'stock',

                $quantity

            );



            return $order;

        });

    }

}

این Service یک Use Case مشخص دارد:


Create Order


# استفاده از Service در Controller

Controller:


<?php



namespace App\Http\Controllers;



use App\Http\Requests\StoreOrderRequest;

use App\Services\OrderService;



final class OrderController extends Controller

{

    public function store(

        StoreOrderRequest $request,

        OrderService $service

    ) {

        $order = $service->create(

            userId: auth()->id(),

            productId: $request->integer('product_id'),

            quantity: $request->integer('quantity')

        );



        return redirect()

            ->route('orders.show', $order);

    }

}

در اینجا Controller بسیار ساده شده است.

منطق اصلی:


Find Product

Check Stock

Calculate Total

Create Order

Decrease Stock

Transaction

داخل Service قرار گرفته است.


# استفاده از Service Container در Laravel

Laravel دارای Service Container است و می‌تواند Dependencyهای کلاس‌ها را Resolve کند.

برای مثال اگر Service ما:


final class OrderService

{

    public function __construct(

        private OrderRepository $orders

    ) {

    }

}

باشد، می‌توان Dependency را از طریق Constructor دریافت کرد.

در Controller نیز:


public function __construct(

    private OrderService $service

) {

}

یا در Method:


public function store(

    StoreOrderRequest $request,

    OrderService $service

) {

    // ...

}

می‌توان Service را دریافت کرد.

این روش باعث می‌شود وابستگی‌های کلاس‌ها صریح باشند و برای تست نیز مناسب‌تر باشد.


# Service Layer و API

یکی از مزیت‌های مهم Service Layer زمانی مشخص می‌شود که یک Use Case چند Client داشته باشد.

مثلاً:


Web

 ↓

OrderService



API

 ↓

OrderService



Admin Panel

 ↓

OrderService



Queue Job

 ↓

OrderService



Console Command

 ↓

OrderService

برای مثال API Controller:


public function store(

    StoreOrderRequest $request,

    OrderService $service

) {

    $order = $service->create(

        auth()->id(),

        $request->integer('product_id'),

        $request->integer('quantity')

    );



    return response()->json([

        'data' => $order,

    ]);

}

همان Service می‌تواند توسط Web Controller نیز استفاده شود.


# استفاده از Service Layer در Job

فرض کنید می‌خواهیم سفارش‌های معلق را پردازش کنیم.

Job:


final class ProcessPendingOrderJob implements ShouldQueue

{

    public function __construct(

        private int $orderId

    ) {

    }



    public function handle(

        OrderService $service

    ): void {

        $service->process(

            $this->orderId

        );

    }

}

در این حالت Business Workflow داخل Job نوشته نشده است.

Job فقط وظیفه اجرای کار پس‌زمینه را دارد.


# استفاده از Service Layer در Artisan Command

همین Service را می‌توان از Command نیز استفاده کرد:


final class ProcessOrdersCommand extends Command

{

    protected $signature = 'orders:process';



    public function handle(

        OrderService $service

    ): int {

        $service->processPendingOrders();



        $this->info(

            'Orders processed successfully.'

        );



        return self::SUCCESS;

    }

}

بنابراین:


Controller

Job

Command

API

همگی می‌توانند یک Application Service را صدا بزنند.


# Service Layer و Eloquent

یک سؤال مهم:

آیا Service Layer باید Eloquent را کنار بگذارد؟

خیر.

در Laravel کاملاً می‌توان Serviceای داشت که مستقیماً با Eloquent کار کند.

مثلاً:


final class ProductService

{

    public function create(array $data): Product

    {

        return Product::create($data);

    }

}

برای پروژه‌های ساده این روش کاملاً قابل استفاده است.

مشکل زمانی ایجاد می‌شود که Serviceها به کلاس‌های بسیار بزرگ تبدیل شوند:


ProductService

├── create

├── update

├── delete

├── import

├── export

├── sync

├── calculate

├── notify

├── publish

├── archive

└── ...

در پروژه‌های بزرگ بهتر است Serviceها بر اساس Use Case یا مسئولیت مشخص‌تر تقسیم شوند.


# Service Layer و Repository چه تفاوتی دارند؟

این دو Pattern اغلب با هم اشتباه گرفته می‌شوند.

Repository

Repository بیشتر با دسترسی به داده سروکار دارد.

مثلاً:


$product = $repository->findById(10);

یا:


$repository->save($product);


Service Layer

Service یک عملیات یا Use Case را هماهنگ می‌کند.

مثلاً:


$orderService->createOrder(...);

و ممکن است درون آن:


Repository

Payment

Inventory

Notification

Domain Object

Transaction

را هماهنگ کند.

بنابراین:


Repository

→ Data Access



Service

→ Application Workflow / Use Case


# Service Layer و Domain Service چه تفاوتی دارند؟

این دو اصطلاح نیز نباید با یکدیگر اشتباه شوند.

Application Service

معمولاً Use Case برنامه را هماهنگ می‌کند.

مثلاً:


CreateOrder

CancelOrder

RegisterUser

ProcessPayment


Domain Service

منطق Domainای را نگه می‌دارد که به‌طور طبیعی متعلق به یک Entity خاص نیست.

مثلاً:


CalculateShippingCost

CurrencyExchange

ComplexPricingRule

بنابراین:


Application Service

→ اجرای Use Case



Domain Service

→ Business Rule سطح Domain

این تفکیک در معماری‌های Domain-Driven Design اهمیت بیشتری پیدا می‌کند.


# Service Layer و Facade چه تفاوتی دارند؟

Facade یک Interface ساده برای دسترسی به یک مجموعه پیچیده از عملیات فراهم می‌کند.

Service Layer بیشتر یک مرز معماری برای Application Operations است.

در Laravel نیز Facadeهای این فریم‌ورک را نباید صرفاً به خاطر وجود کلمه Service با Service Layer یکی بدانیم.

مثلاً:


DB::transaction(...)

یک Facade است.

اما:


$orderService->create(...)

یک Application Service است.


# مزایای Service Layer Pattern

۱. Controllerهای کوچک‌تر

Controller به جای صدها خط Business Logic، فقط Use Case را اجرا می‌کند.


۲. استفاده مجدد از منطق

یک Service را می‌توان از:


Web

API

Job

Command

استفاده کرد.


۳. تست‌پذیری بهتر

Service را می‌توان مستقل از HTTP Request تست کرد.


۴. جداسازی مسئولیت‌ها

به صورت ساده:


Controller

→ HTTP



Service

→ Application Workflow



Repository

→ Persistence



Domain

→ Business Rules


۵. مدیریت بهتر فرآیندهای چندمرحله‌ای

Service برای Use Caseهایی که چند عملیات را هماهنگ می‌کنند، نقطه مناسبی ایجاد می‌کند.


# معایب Service Layer Pattern

Service Layer نیز مانند هر Pattern دیگری بدون Trade-off نیست. انتخاب Pattern باید بر اساس مسئله واقعی پروژه انجام شود و نباید صرفاً به خاطر نام Pattern از آن استفاده کرد.

۱. افزایش تعداد کلاس‌ها

ممکن است پروژه‌ای داشته باشید که برای هر عملیات یک Service ساخته شده است:


CreateUserService

UpdateUserService

DeleteUserService

GetUserService

اگر این کار بدون نیاز واقعی انجام شود، ساختار پروژه بیش از حد پیچیده خواهد شد.


۲. God Service

یکی از بدترین حالت‌ها:


UserService

که ۳۰۰۰ خط کد دارد.

وجود Service Layer به خودی خود به معنی Clean Architecture نیست.

اگر Service همه چیز را انجام دهد، فقط Fat Controller را به Fat Service تبدیل کرده‌ایم.


۳. Abstraction بیش از حد

گاهی برای یک عملیات ساده چنین ساختاری ساخته می‌شود:


Controller

↓

Interface

↓

Service

↓

Interface

↓

Repository

↓

Eloquent

در حالی که Use Case بسیار ساده است.

Service Layer باید به اندازه مسئله پیچیدگی داشته باشد، نه بیشتر.


# چه زمانی از Service Layer استفاده کنیم؟

Service Layer زمانی ارزش بیشتری پیدا می‌کند که:

  • Business Workflow پیچیده باشد.
  • یک Use Case از چند مرحله تشکیل شده باشد.
  • منطق یک عملیات از چند Controller یا Client استفاده شود.
  • نیاز به Transaction داشته باشیم.
  • Job و Command نیز همان فرآیند را اجرا کنند.
  • Integration با سرویس‌های خارجی وجود داشته باشد.
  • پروژه در حال رشد باشد.

مثلاً:


Create Subscription

ممکن است شامل:


Validate Plan

↓

Calculate Price

↓

Create Subscription

↓

Charge Wallet

↓

Create Transaction

↓

Activate Service

↓

Send Notification

باشد.

این Use Case کاندید بسیار مناسبی برای یک Application Service است.


# چه زمانی Service Layer اضافه است؟

برای یک CRUD بسیار ساده:


public function show(Product $product)

{

    return $product;

}

ساختن:


ProductService

صرفاً برای اینکه همه‌چیز Service داشته باشد، الزاماً ارزشمند نیست.

همچنین:


public function store(Request $request)

{

    Product::create(

        $request->validated()

    );

}

اگر هیچ Workflow یا Business Logic قابل توجهی وجود ندارد، ممکن است Service Layer فقط یک Wrapper اضافی باشد.

هدف معماری نباید افزایش تعداد فایل‌ها باشد.


# اشتباهات رایج در طراحی Service Layer

اشتباه اول: انتقال تمام کد Controller به Service

اگر Controller این بود:


// 500 lines

و فقط همان ۵۰۰ خط را به:


OrderService

منتقل کنیم، مشکل معماری کاملاً حل نشده است.

باید مسئولیت‌ها نیز تفکیک شوند.


اشتباه دوم: Service بسیار بزرگ

اگر Service مربوط به سفارش شامل:


Payment

Shipping

Inventory

Notification

Reporting

User

Discount

باشد، احتمالاً باید آن را به چند Use Case یا Component تقسیم کنیم.


اشتباه سوم: قرار دادن Validation مربوط به HTTP داخل Service

مثلاً:


$request->validate(...)

به Presentation Layer وابسته است.

بهتر است HTTP Validation در Form Request باشد:


StoreOrderRequest

و Service داده معتبرشده را دریافت کند.


اشتباه چهارم: وابستگی شدید به Request

این طراحی:


public function create(Request $request)

Service را به HTTP وابسته می‌کند.

بهتر است:


public function create(

    int $userId,

    int $productId,

    int $quantity

)

یا DTO دریافت کند.

در این صورت Service را می‌توان از API، Job یا Command نیز استفاده کرد.


# استفاده از DTO در Service Layer

در پروژه‌های بزرگ‌تر می‌توان به جای ارسال آرایه‌های بزرگ از DTO استفاده کرد.

مثلاً:


final readonly class CreateOrderData

{

    public function __construct(

        public int $userId,

        public int $productId,

        public int $quantity,

    ) {

    }

}

Service:


final class OrderService

{

    public function create(

        CreateOrderData $data

    ): Order {

        // ...

    }

}

حالا Controller:


$data = new CreateOrderData(

    userId: auth()->id(),

    productId: $request->integer('product_id'),

    quantity: $request->integer('quantity'),

);



$order = $service->create($data);

این روش Contract بین Presentation و Application Layer را واضح‌تر می‌کند.


# تست Service Layer با PHPUnit

یکی از مزیت‌های Service Layer این است که می‌توانیم Use Case را بدون اجرای HTTP تست کنیم.

مثلاً:


<?php



use Tests\TestCase;



final class OrderServiceTest extends TestCase

{

    public function test_order_can_be_created(): void

    {

        $product = Product::factory()->create([

            'price' => 100000,

            'stock' => 10,

        ]);



        $user = User::factory()->create();



        $service = app(OrderService::class);



        $order = $service->create(

            userId: $user->id,

            productId: $product->id,

            quantity: 2

        );



        $this->assertDatabaseHas('orders', [

            'id' => $order->id,

            'user_id' => $user->id,

        ]);



        $this->assertDatabaseHas('products', [

            'id' => $product->id,

            'stock' => 8,

        ]);

    }

}

در اینجا به جای اینکه حتماً HTTP Request ایجاد کنیم، مستقیماً Use Case را تست می‌کنیم.


# ساختار پیشنهادی پروژه Laravel

برای پروژه متوسط:


app/

├── Http/

│   ├── Controllers/

│   └── Requests/

│

├── Models/

│

├── Services/

│   ├── OrderService.php

│   ├── PaymentService.php

│   └── ProductService.php

│

├── Repositories/

│   ├── OrderRepository.php

│   └── ProductRepository.php

│

└── DTOs/

    └── CreateOrderData.php

اما برای پروژه بزرگ‌تر می‌توان ساختار Domain-oriented داشت:


app/

├── Domain/

│   ├── Order/

│   │   ├── Entities/

│   │   ├── Services/

│   │   ├── Specifications/

│   │   └── Repositories/

│   │

│   └── Product/

│       ├── Entities/

│       ├── Services/

│       └── Repositories/

│

├── Application/

│   ├── Order/

│   │   ├── Services/

│   │   └── DTOs/

│   │

│   └── Product/

│       ├── Services/

│       └── DTOs/

│

└── Infrastructure/

    ├── Persistence/

    └── ExternalServices/

در این مدل، Service Layer می‌تواند در Application Layer قرار بگیرد و Domain Serviceها نیز در Domain Layer باقی بمانند.


# آیا Laravel خودش Service Layer دارد؟

Laravel به صورت اجباری معماری‌ای با نام Service Layer تعریف نمی‌کند.

Service Layer بیشتر یک انتخاب معماری در پروژه است که می‌تواند با امکانات Laravel مانند Dependency Injection و Service Container پیاده‌سازی شود. منابع مربوط به معماری Laravel نیز Service Layer را معمولاً به عنوان Application-level organization معرفی می‌کنند، نه یک جزء اجباری از Framework.

بنابراین:


Laravel

≠

Service Layer

بلکه:


Laravel

+

Service Layer Architecture

یک ترکیب رایج برای سازمان‌دهی پروژه‌های بزرگ‌تر است.


# Service Layer در پروژه‌های بزرگ

فرض کنید یک سیستم فروشگاهی داریم:


Order

Payment

Inventory

Shipping

Wallet

Notification

ممکن است Serviceهای مشخصی داشته باشیم:


CreateOrderService

CancelOrderService

ProcessPaymentService

RefundPaymentService

ReserveInventoryService

ShipOrderService

هر Service یک Use Case مشخص را مدیریت می‌کند.

به جای یک Service عظیم:


OrderService

می‌توان عملیات را بر اساس رفتار و Use Case تقسیم کرد.

این رویکرد باعث می‌شود نام کلاس نیز هدف آن را بهتر توضیح دهد.


# Service Layer و معماری Clean Architecture

در معماری‌های تمیز، معمولاً Application Layer وظیفه هماهنگ‌کردن Use Caseها را بر عهده دارد.

برای مثال:


Presentation

      ↓

Application

      ↓

Domain

      ↓

Infrastructure

یک Application Service می‌تواند:


Use Case

↓

Domain Objects

↓

Repository Interface

↓

Infrastructure Implementation

را هماهنگ کند.

این ساختار البته یک الزام برای تمام پروژه‌های Laravel نیست.

اگر پروژه ساده باشد، چنین معماری‌ای ممکن است بیش از نیاز پروژه پیچیده باشد.


# Service Layer را با Service Container اشتباه نگیریم

این دو مفهوم کاملاً متفاوت هستند.

Service Layer

یک مفهوم معماری است:


Application Logic

Service Container

یک مکانیزم Dependency Resolution در Laravel است.

مثلاً:


app(OrderService::class);

یا:


public function __construct(

    OrderService $service

) {

}

Service Container به Laravel کمک می‌کند Dependencyهای کلاس را Resolve کند.

اما Service Layer مشخص می‌کند Application Logic کجا قرار بگیرد.


# جمع‌بندی

Service Layer Pattern در PHP یک الگوی معماری برای سازمان‌دهی عملیات سطح Application و Use Caseهای برنامه است.

ایده اصلی این است که Controllerها مستقیماً تمام مراحل Business Workflow را اجرا نکنند.

به جای:


Controller

├── Validation

├── Database

├── Payment

├── Inventory

├── Notification

└── Transaction

می‌توانیم داشته باشیم:


Controller

      ↓

OrderService

      ↓

 ┌────┼──────────┐

 ↓    ↓          ↓

Domain Repository External API

      ↓

   Database

مهم‌ترین مزیت Service Layer این است که یک عملیات پیچیده می‌تواند یک نقطه ورود مشخص داشته باشد و از مسیرهای مختلف مانند Web، API، Queue و Command استفاده شود.

اما نباید هر متد ساده را به Service تبدیل کنیم.

اگر Service Layer باعث شود:


Controller

↓

Service

↓

Repository

↓

Model

برای یک عملیات دوخطی ایجاد شود، ممکن است صرفاً پیچیدگی اضافی ایجاد کرده باشیم.

پس اصل مهم این است:

> Service Layer باید پیچیدگی واقعی Application را سازمان‌دهی کند، نه اینکه صرفاً تعداد کلاس‌های پروژه را افزایش دهد.


# سوالات متداول

Service Layer Pattern در PHP چیست؟

Service Layer یک الگوی معماری برای قرار دادن و سازمان‌دهی Application Logic و Use Caseهای برنامه در Serviceهای مشخص است.

آیا Service Layer همان Repository است؟

خیر.

Repository بیشتر مسئول دسترسی و Persistence داده است، در حالی که Service Layer معمولاً یک Use Case را هماهنگ می‌کند.


Repository

→ Data Access



Service

→ Application Workflow

آیا استفاده از Service Layer در Laravel ضروری است؟

خیر. Laravel استفاده از Service Layer را اجباری نمی‌کند. این یک تصمیم معماری است که بر اساس اندازه و پیچیدگی پروژه گرفته می‌شود.

آیا Service Layer باید با Eloquent کار کند؟

الزامی نیست. در پروژه‌های ساده می‌تواند مستقیماً از Eloquent استفاده کند؛ در معماری‌های پیچیده‌تر می‌توان Persistence را پشت Repository یا Interface قرار داد.

آیا Transaction باید داخل Service Layer باشد؟

برای Use Caseهایی که چند عملیات وابسته را در یک فرآیند انجام می‌دهند، قرار دادن مرز Transaction در Application Service می‌تواند انتخاب مناسبی باشد.

تفاوت Service Layer و Domain Service چیست؟

Application Service بیشتر اجرای یک Use Case را هماهنگ می‌کند، در حالی که Domain Service برای Business Logicای استفاده می‌شود که به‌طور طبیعی متعلق به یک Entity یا Value Object خاص نیست.

آیا Service Layer باعث Clean Architecture می‌شود؟

خیر. صرف ایجاد Service Class به معنی Clean Architecture نیست. Service Layer تنها یکی از ابزارهای سازمان‌دهی Application است و معماری کلی پروژه به Dependencyها، مرزهای لایه‌ها و نحوه قرارگیری Domain و Infrastructure نیز بستگی دارد.

آیا برای هر Model باید Service بسازیم؟

خیر. ساختن Service برای هر Model یک قانون اجباری نیست. Service بهتر است زمانی ایجاد شود که یک Use Case یا Workflow دارای منطق و هماهنگی قابل توجه باشد.

آیا Service Layer برای پروژه‌های کوچک مناسب است؟

ممکن است، اما همیشه لازم نیست. برای CRUDهای ساده، استفاده مستقیم از Controller، Form Request و Eloquent می‌تواند ساختار ساده‌تری ایجاد کند.

Service Layer در Laravel کجا قرار می‌گیرد؟

در پروژه‌های ساده می‌توان Serviceها را در:


app/Services

قرار داد.

در پروژه‌های بزرگ‌تر، می‌توان Serviceها را بر اساس Domain و Application Layer سازمان‌دهی کرد.

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه