Action vs Service в Laravel
Glossary overview

Action vs Service в Laravel

В Laravel-сообществе часто спорят: использовать Action-классы или Service-классы? На практике это не взаимоисключающие подходы — они решают разные задачи и отлично работают вместе.

Что такое Action

Action — это класс с одним публичным методом execute, который выполняет одно конкретное действие. Названия Action-классов обычно начинаются с глагола: CreateOrder, SendInvoice, ApproveComment.

class CreateOrderAction
{
    public function execute(OrderDTO $dto): Order
    {
        $order = Order::create([
            'user_id' => $dto->userId,
            'total' => $dto->total,
            'status' => OrderStatus::Pending,
        ]);

        event(new OrderCreated($order));

        return $order;
    }
}

Ключевые признаки Action:

  • Один публичный метод (execute, handle или __invoke)
  • Одна ответственность — одно бизнес-действие
  • Легко вызывается из контроллера, очереди, CLI, теста

Что такое Service

Service — это класс, который группирует несколько связанных операций в рамках одного домена. Например, PaymentService может содержать методы charge, refund, getHistory.

class PaymentService
{
    public function charge(Order $order): Payment
    {
        // логика списания
    }

    public function refund(Payment $payment): void
    {
        // логика возврата
    }

    public function getHistory(User $user): Collection
    {
        // история платежей
    }
}

Ключевые признаки Service:

  • Несколько публичных методов, объединённых одним доменом
  • Инкапсулирует логику работы с внешним сервисом или сложной подсистемой
  • Может использоваться внутри Action-классов

Сравнение

КритерийActionService
Публичные методыОдинНесколько
ОтветственностьОдно действиеГруппа операций одного домена
ИменованиеГлагол: CreateOrderСуществительное: OrderService
Вызывается изКонтроллер, очередь, CLIAction, другой Service
Типичный размер20–60 строк50–200 строк

Как они работают вместе

Action выступает как точка входа и оркестратор — он вызывает нужные сервисы в правильном порядке. Контроллер при этом остаётся тонким.

class CreateOrderAction
{
    public function __construct(
        private OrderService $orderService,
        private PaymentService $paymentService,
        private NotificationService $notificationService,
    ) {}

    public function execute(OrderDTO $dto): Order
    {
        $order = $this->orderService->create($dto);
        $this->paymentService->charge($order);
        $this->notificationService->orderCreated($order);

        return $order;
    }
}

Контроллер вызывает только Action:

class OrderController extends Controller
{
    public function store(StoreOrderRequest $request)
    {
        $dto = OrderDTO::fromRequest($request);

        $order = app(CreateOrderAction::class)->execute($dto);

        return new OrderResource($order);
    }
}

Когда что выбирать

Используй Action, когда есть конкретное бизнес-действие, которое может вызываться из разных мест: контроллер, Artisan-команда, очередь, другой Action.

Используй Service, когда нужно сгруппировать несколько связанных операций: работа с платёжным шлюзом, отправка уведомлений через разные каналы, сложные выборки данных.

Используй оба вместе, когда Action оркестрирует вызовы нескольких сервисов для выполнения одной бизнес-операции.

Полная цепочка в контроллере

Итоговый паттерн тонкого контроллера выглядит так:

Form Request → валидация
DTO → типизированный объект из validated-данных
Action → оркестрация бизнес-логики
Service → переиспользуемые операции внутри Action
Resource → форматирование ответа

// Контроллер — максимум 5 строк
public function store(StoreOrderRequest $request)
{
    $dto = OrderDTO::fromRequest($request);
    $order = app(CreateOrderAction::class)->execute($dto);
    return new OrderResource($order);
}

Жёстких правил нет — главное консистентность в проекте. Если Action содержит одну строку без оркестрации, возможно, сервиса достаточно. Если сервис разрастается и методы не связаны — пора разбивать на Action-классы.