utilitarian-dev/utilitarian-laravel-toolkit

Laravel implementation of Utilitarian Architecture: CQRS operations (Query, Command, Action), State Machine, and Data mapping utilities

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/utilitarian-dev/utilitarian-laravel-toolkit

dev-main 2026-02-06 09:14 UTC

This package is not auto-updated.

Last update: 2026-02-07 08:20:11 UTC


README

Laravel implementation of Utilitarian Architecture — a pragmatic approach to organizing applications around business operations (use cases) rather than technical layers.

Requirements

  • PHP 8.3+
  • Laravel 11+

Installation

composer require utilitarian-dev/utilitarian-laravel-toolkit

The service provider is registered automatically via Laravel's package discovery.

Optionally, publish the configuration:

php artisan vendor:publish --tag=utilitarian-config

Core Components

Query

Read-only operations. Execute via QueryBus or dispatch().

class GetUserQuery extends Query
{
    public function __construct(
        private readonly int $userId,
    ) {}

    protected function boot(UserRepository $repo): void
    {
        $this->repository = $repo;
    }

    public function execute(): ?User
    {
        return $this->repository->find($this->userId);
    }
}

// Execute
$user = GetUserQuery::make(userId: 1)->dispatch();
// or
$user = QueryBus::execute(GetUserQuery::make(userId: 1));

Command

Write-only operations. Execute via CommandBus or dispatch().

class CreateUserCommand extends Command
{
    public function __construct(
        private readonly string $email,
        private readonly string $name,
    ) {}

    protected function boot(UserRepository $repo): void
    {
        $this->repository = $repo;
    }

    public function execute(): User
    {
        return $this->repository->create([
            'email' => $this->email,
            'name' => $this->name,
        ]);
    }
}

// Execute
$user = CreateUserCommand::make(email: '...', name: '...')->dispatch();

Action

Business logic and orchestration. Execute directly via run().

class RegisterUserAction extends Action
{
    public function __construct(
        private readonly string $email,
        private readonly string $name,
    ) {}

    public function execute(): User
    {
        $user = CreateUserCommand::make(
            email: $this->email,
            name: $this->name
        )->dispatch();

        SendWelcomeEmailCommand::make(userId: $user->id)->dispatch();

        return $user;
    }
}

// Execute
$user = RegisterUserAction::make(email: '...', name: '...')->run();

Middleware

Configure middleware in config/utilitarian.php:

return [
    'cqrs' => [
        'query_middleware' => [
            \Utilitarian\Cqrs\Middleware\CachingMiddleware::class,
        ],
        'command_middleware' => [
            \Utilitarian\Cqrs\Middleware\TransactionMiddleware::class,
        ],
        'action_middleware' => [
            \Utilitarian\Cqrs\Middleware\AuthorizationMiddleware::class,
        ],
    ],
];

Per-operation middleware:

class MyQuery extends Query
{
    protected function middleware(): array
    {
        return [CustomMiddleware::class];
    }

    protected function excludeMiddleware(): array
    {
        return [UnwantedMiddleware::class];
    }
}

Built-in middleware:

  • TransactionMiddleware — wraps execution in database transaction
  • CachingMiddleware — caches query results
  • ValidationMiddleware — validates operation data
  • AuthorizationMiddleware — checks access permissions

State Machine

Manage state for Eloquent models with three levels of complexity:

use Utilitarian\StateMachine\Traits\HasStateMachine;

class Order extends Model
{
    use HasStateMachine;
}

Key-Value Storage

$order->state()->set('notes', 'Special handling');
$order->state()->get('notes');
$order->state()->has('notes');
$order->state()->forget('notes');

Enum States (no rules)

$order->state()->transitionTo(OrderState::Paid);
$order->state()->current(); // OrderState::Paid
$order->state()->is(OrderState::Paid); // true

Enum States (with transition rules)

enum OrderState: string implements FlowHandler
{
    case Pending = 'pending';
    case Paid = 'paid';
    case Shipped = 'shipped';

    public function canTransitionTo(UnitEnum $target): bool
    {
        return match ($this) {
            self::Pending => $target === self::Paid,
            self::Paid => $target === self::Shipped,
            self::Shipped => false,
        };
    }
}

$order->state()->transitionTo(OrderState::Paid); // OK
$order->state()->transitionTo(OrderState::Shipped); // throws InvalidTransitionException

Database Setup:

Migrations are loaded automatically. Run:

php artisan migrate

Data Mapping

Lightweight DTOs with automatic property mapping:

use Utilitarian\Data\Data;

class UserData extends Data
{
    public function __construct(
        public readonly int $id,
        public readonly string $email,
        public readonly string $name,
    ) {}
}

// Create from array
$userData = UserData::from([
    'id' => 1,
    'email' => 'user@example.com',
    'name' => 'John Doe',
]);

// Convert back to array
$array = $userData->toArray();

Custom field mapping:

use Utilitarian\Data\Attributes\MapField;

class UserData extends Data
{
    public function __construct(
        public readonly int $id,
        #[MapField('email_address')]
        public readonly string $email,
    ) {}
}

Testing

composer test                      # Run tests
composer test:coverage            # Run with coverage
vendor/bin/pest --filter TestName # Run specific test

Code Quality

composer lint                     # Fix code style
composer lint:test               # Check code style

License

Licensed under the Apache License 2.0. See LICENSE for details.

Credits

Created by Vasilii Shvakin