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.