Icanup
0% прочитано

Я використала Laravel Pipeline для розрахунку комісії і логіка стала значно зрозумілішою

Як впорядкувати складну бізнес-логіку без нескінченних if? Показую, як Laravel Pipeline зробив розрахунок комісії простішим і зрозумілішим.

21 липня 2026 р. 4 хв читанняЛаравель

Коли послідовність умов стала окремим процесом

Кожне правило саме по собі було невеликим. Проблемою стала не складність окремої перевірки, а їхня кількість і порядок виконання.

Мені було важливо, щоб:

  • кожне правило залишалося окремим класом;

  • порядок пріоритетів був видимий в одному місці;

  • правило могло або повернути результат, або передати обробку далі;

  • кожен крок можна було тестувати незалежно.

Для цього добре підійшов Laravel Pipeline.

Назви доменних класів і деякі деталі прикладу змінено. Структура Pipeline та принцип взаємодії між кроками залишені такими самими, як у робочому рішенні.

Спільний контракт для кроків

Кожен крок отримує контекст розрахунку і callback наступного кроку.
<?php declare(strict_types=1); namespace App\Services\Commission\Steps; use App\DTO\CommissionContext;use Closure; interface CommissionStepInterface{    public function handle(        CommissionContext $context,        Closure $next,    ): array;}

Такий контракт робить усі правила однаковими з погляду Pipeline. Усередині вони можуть перевіряти зовсім різні умови, але взаємодіють із процесом однаково.

Порядок правил видно в одному місці

php
<?php declare(strict_types=1); namespace App\Services\Commission; use App\DTO\CommissionContext;use App\Services\Commission\Steps\CampaignCommissionStep;use App\Services\Commission\Steps\CustomerTierCommissionStep;use App\Services\Commission\Steps\DefaultCommissionStep;use App\Services\Commission\Steps\PreferredPartnerCommissionStep;use App\Services\Commission\Steps\ProductCategoryCommissionStep;use App\Services\Commission\Steps\VolumeCommissionStep;use Illuminate\Pipeline\Pipeline; final readonly class CommissionPipeline{    public function __construct(        private Pipeline $pipeline,    ) {}     public function calculate(CommissionContext $context): array    {        return $this->pipeline            ->send($context)            ->through($this->steps())            ->thenReturn();    }     private function steps(): array    {        return [            PreferredPartnerCommissionStep::class,            ProductCategoryCommissionStep::class,            CustomerTierCommissionStep::class,            VolumeCommissionStep::class,            CampaignCommissionStep::class,            DefaultCommissionStep::class,        ];    }}

Ця частина рішення мені подобається найбільше.

Щоб зрозуміти пріоритет розрахунку, не потрібно відкривати великий метод і шукати вкладені if. Достатньо подивитися на список кроків зверху вниз.

Першими виконуються спеціальні правила. Якщо жодне з них не підходить, останній крок повертає стандартну комісію.

Один із кроків

Інші кроки поводяться дещо інакше.
<?php declare(strict_types=1); namespace App\Services\Commission\Steps; use App\DTO\CommissionContext;use App\Helpers\Percent;use App\Services\Commission\CommissionPolicy;use Closure; final readonly class DefaultCommissionStep implements CommissionStepInterface{    public function __construct(        private MinimumCommissionApplier $minimumCommissionApplier,        private CommissionPolicy $commissionPolicy,    ) {}     public function handle(        CommissionContext $context,        Closure $next,    ): array {        $percent = $this->commissionPolicy->defaultPercent(            $context->subject,            $context->amount,        );         return [            'commission' => $this->minimumCommissionApplier->apply(                Percent::amount($context->amount, $percent),                $context,            ),            'original_percent' => $percent,            'percent' => $percent,            'rule' => 'default',        ];    }}

Що змінилося після цього?

Розрахунок не став коротшим у сенсі кількості бізнес-правил. Але його структура стала значно зрозумілішою.

Тепер:

  • кожне правило має окрему відповідальність;

  • порядок виконання видно одразу;

  • новий сценарій можна додати окремим кроком;

  • окремі правила простіше тестувати;

  • головний сервіс більше не росте разом із кожною новою умовою.

Для мене це був один із тих випадків, коли патерн не просто зробив код красивішим, а допоміг показати сам бізнес-процес у структурі програми.

Якимів Альона

Висновок

Laravel Pipeline часто асоціюється з middleware або послідовною обробкою даних. У цьому випадку він добре підійшов для набору пріоритетних правил розрахунку.

Найбільша користь для мене була не в тому, що зникли умовні оператори. Вони просто перемістилися у відповідні класи.

Вона була в іншому: порядок правил став явним, кожен крок отримав власну відповідальність, а розрахунок перестав виглядати як один великий метод.