Сервіс-класи в Laravel: чиста архітектура та масштабованість

✍️ Вступ:

У Laravel зручно розміщувати бізнес-логіку у контролерах або моделях. Але що робити, коли логіка розростається? Вірне рішення — винести її в окремі сервіс-класи. Це дозволяє дотримуватись принципу SRP (Single Responsibility Principle) та робити код масштабованим і зручним для тестування.


🧱 Структура сервіс-класу

Розглянемо приклад сервісу для створення замовлення:

app/
├── Services/
│   └── OrderService.php
namespace App\Services;

use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class OrderService
{
    public function create(array $data, User $user): Order
    {
        return DB::transaction(function () use ($data, $user) {
            $order = new Order();
            $order->user_id = $user->id;
            $order->total = $data['total'];
            $order->status = 'new';
            $order->save();

            // тут можна викликати події, надіслати email, тощо

            return $order;
        });
    }
}

🚀 Використання в контролері

namespace App\Http\Controllers;

use App\Http\Requests\OrderRequest;
use App\Services\OrderService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;

class OrderController extends Controller
{
    public function __construct(
        protected OrderService $orderService
    ) {}

    public function store(OrderRequest $request): JsonResponse
    {
        $order = $this->orderService->create($request->validated(), Auth::user());

        return response()->json([
            'message' => 'Order created successfully.',
            'order' => $order,
        ]);
    }
}

Переваги такого підходу:

  • Чиста архітектура: контролери “худнуть” і стають точками входу, а не логічним смітником.
  • Легкість тестування: можна мокати залежності та тестувати сервіси окремо.
  • Гнучкість: сервіс легко розширити без змін у контролерах.
  • Повторне використання: один сервіс можна викликати з різних частин системи (REST, CLI, Queue).

🧪 Як протестувати сервіс

use App\Models\User;
use App\Services\OrderService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class OrderServiceTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_creates_an_order()
    {
        $user = User::factory()->create();
        $data = ['total' => 1000];

        $service = new OrderService();
        $order = $service->create($data, $user);

        $this->assertDatabaseHas('orders', [
            'user_id' => $user->id,
            'total' => 1000,
        ]);
    }
}

🔚 Висновок:

Сервіс-класи — це не "overengineering", а шлях до стійкої та чистої архітектури. Вони допомагають будувати Laravel-проєкти, які зручно підтримувати і масштабувати.


Залишити відповідь