Skip to content

Coordinate across Domains

One Service UseCase coordinates Orders and Billing through ConfirmOrder and PaymentGateway. It never calls an Action from another Action or one UseCase from another UseCase.

Before you begin

Run composer run reference:test:coordination and read cross-domain coordination.

Put the transaction in the coordinator

examples/reference-app/app/Pulsar/Services/Client/Modules/Orders/UseCases/FinalizeOrderPayment.php — One Service UseCase coordinates [Domain](/concepts/domain) Action and [Contract](/concepts/contract).

php
<?php

namespace App\Pulsar\Services\Client\Modules\Orders\UseCases;

use App\Pulsar\Domain\Billing\Contracts\PaymentGateway;
use App\Pulsar\Domain\Orders\Actions\ConfirmOrder;
use App\Pulsar\Domain\Orders\Models\Order;
use Illuminate\Support\Facades\DB;

final class FinalizeOrderPayment
{
    public function __construct(private readonly ConfirmOrder $confirmOrder, private readonly PaymentGateway $gateway) {}

    public function gatewayName(): string
    {
        return $this->gateway->name();
    }

    // #region cross-domain-coordination
    public function execute(int $orderId, string $idempotencyKey): Order
    {
        return DB::transaction(function () use ($orderId, $idempotencyKey): Order {
            $order = Order::query()->lockForUpdate()->findOrFail($orderId);
            $this->gateway->charge($order->amount_cents, $idempotencyKey);

            return $this->confirmOrder->execute($order);
        });
    }
    // #endregion
}

Verify and troubleshoot

The focused test confirms the Order after the fake payment boundary succeeds. Extract shared branching to an Operation when multiple UseCases need it; never hide workflow coordination in an Action.