Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Use Case Laravel Package

openclassrooms/use-case

A lightweight PHP package for expressing business logic as explicit, single-responsibility use cases. Keeps controllers and models thin, makes domain code testable and framework-agnostic, and integrates cleanly with Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and inspect it before wiring anything:

composer require openclassrooms/use-case

Start with these files in vendor/openclassrooms/use-case:

  • README.md — the canonical usage example
  • src/UseCaseBus.php — how use cases are executed and how middleware is applied
  • src/UseCase.php / src/UseCaseRequest.php / src/UseCaseResponse.php — the contracts you implement
  • src/Middleware/ — built-in cross-cutting concerns, if any
  • composer.json — check for extra.laravel.providers; this package is likely framework-agnostic, so you will usually register it manually

A minimal Laravel wiring looks like this:

// app/Providers/AppServiceProvider.php
use OpenClassrooms\UseCase\UseCaseBus;

public function register(): void
{
    $this->app->singleton(UseCaseBus::class, function ($app) {
        $bus = new UseCaseBus($app); // Laravel's container is PSR-11 compatible

        // Adjust to the package's actual middleware API.
        $bus->addMiddleware(new \App\UseCases\Middleware\TransactionMiddleware($app['db']));
        $bus->addMiddleware(new \App\UseCases\Middleware\LoggingMiddleware($app['log']));

        return $bus;
    });
}

Your first use case should be a real business action, not CRUD for its own sake. For example, RegisterUser:

use OpenClassrooms\UseCase\UseCase;

final class RegisterUser implements UseCase
{
    public function __construct(private UserRepository $users)
    {
    }

    public function execute(RegisterUserRequest $request): RegisterUserResponse
    {
        $user = $this->users->create(
            email: $request->email,
            password: $request->password,
        );

        return new RegisterUserResponse($user->id);
    }
}

Then call it from a controller after Laravel has validated the HTTP input:

public function store(RegisterUserHttpRequest $httpRequest, UseCaseBus $bus)
{
    $response = $bus->execute(
        RegisterUser::class, // or an instance, depending on the bus API
        new RegisterUserRequest(
            email: $httpRequest->validated('email'),
            password: $httpRequest->validated('password'),
        ),
    );

    return new RegisterUserResource($response);
}

The first useful milestone is: HTTP request → Laravel validation → use case request DTO → bus → use case → response DTO → Laravel resource.

Implementation Patterns

Keep controllers and jobs thin. The controller validates HTTP concerns, maps to a use case request, calls the bus, and maps the response back to HTTP. The use case owns the business transaction.

final class CreateInvoice implements UseCase
{
    public function __construct(
        private InvoiceRepository $invoices,
        private CustomerRepository $customers,
    ) {
    }

    public function execute(CreateInvoiceRequest $request): CreateInvoiceResponse
    {
        $customer = $this->customers->findOrFail($request->customerId);

        $invoice = $this->invoices->create(
            customer: $customer,
            lines: $request->lines,
        );

        return new CreateInvoiceResponse($invoice->id);
    }
}

Use the bus as the integration point. Controllers, queued jobs, artisan commands, and event listeners should all execute the same use case through the bus. This keeps cross-cutting behavior consistent.

// app/Jobs/SendInvoiceEmail.php
public function handle(UseCaseBus $bus): void
{
    $bus->execute(
        SendInvoiceEmail::class,
        new SendInvoiceEmailRequest($this->invoiceId),
    );
}

Put cross-cutting concerns in bus middleware. Typical middleware:

  • Database transaction wrapper
  • Structured logging of use case name, request ID, duration, and outcome
  • Authorization checks
  • Domain event dispatching after success
  • Metrics/tracing
final class TransactionMiddleware
{
    public function __construct(private \Illuminate\Database\DatabaseManager $db)
    {
    }

    public function handle(object $useCase, object $request, callable $next): object
    {
        return $this->db->transaction(fn () => $next($useCase, $request));
    }
}

Keep transaction middleware close to the use case. Do not wrap the entire HTTP request unless you deliberately want validation, authorization, and response rendering inside the transaction.

Register use cases and dependencies in the container. If the bus resolves use cases by class name, Laravel's container must be able to construct them. Bind repository interfaces in a service provider:

$this->app->bind(UserRepository::class, EloquentUserRepository::class);
$this->app->bind(InvoiceRepository::class, EloquentInvoiceRepository::class);

If the bus accepts a use case instance instead, resolve it from the container at the call site or inject it into the controller.

Test at three levels.

  • Unit-test the use case directly with mocked repositories.
  • Test bus middleware in isolation with a fake use case.
  • Feature-test the HTTP endpoint to verify validation, response shape, and transaction boundaries.
public function test_it_registers_a_user(): void
{
    $bus = $this->app->make(UseCaseBus::class);

    $response = $bus->execute(
        RegisterUser::class,
        new RegisterUserRequest('[email protected]', 'secret'),
    );

    $this->assertNotNull($response->userId);
    $this->assertDatabaseHas('users', ['email' => '[email protected]']);
}

Use request and response DTOs as boundaries. Do not return Eloquent models from use cases. Return a response DTO, then let a Laravel JsonResource or controller transform it for the transport layer.

final class RegisterUserResponse
{
    public function __construct(public readonly int $userId)
    {
    }
}

This makes the use case reusable from HTTP, queues, commands, and tests without leaking persistence details.

Gotchas and Tips

There is probably no Laravel auto-discovery. Check composer.json for extra.laravel. If it is absent, you must register the bus and any middleware yourself. Do not assume a service provider exists.

Confirm the bus API before writing many use cases. The package is small, so read UseCaseBus.php first. Depending on the version, execute() may accept a use case instance, a class name, or a request object with an embedded use case. Standardize on one style in your codebase.

Watch the contract signatures. If the package's UseCase contract declares execute(UseCaseRequest $request): UseCaseResponse, your concrete request/response classes must implement those interfaces. If the contract is a marker interface, you have more freedom. Check the source before designing DTOs.

Do not nest transactions accidentally. If Laravel's DB::transaction() is already active in a controller or job, the transaction middleware should either join the existing transaction or be skipped. Nested transactions can hide failures and make rollback behavior confusing.

Middleware order matters. A typical order is:

  1. Logging/metrics
  2. Authorization
  3. Transaction
  4. Use case execution
  5. Domain event dispatch

Authorization should generally happen before opening a transaction. Event dispatch should happen after the business operation succeeds.

Be careful with Laravel container resolution. If the bus resolves use case class names, every constructor dependency must be container-resolvable. Bind interfaces, avoid scalar constructor arguments unless you provide contextual bindings, and test resolution with app()->make(YourUseCase::class).

Do not let the pattern become ceremony. Use cases are most valuable for actions with business rules, multiple side effects, or reuse across HTTP and queues. Simple CRUD endpoints can stay as conventional Laravel controllers. Forcing every index and show action through a use case adds indirection without much payoff.

Debug with Laravel's existing tools. Log the use case class, a request correlation ID, and the response type from middleware. Telescope can show the surrounding HTTP request, but it will not automatically understand the bus pipeline. Add explicit bus-level logging if you need to trace failures.

Extension points are likely custom middleware and decorators. If the package does not provide a needed concern, implement it as bus middleware rather than adding it to every use case. If the bus itself is too limited, decorate it behind your own application-level interface so you can swap implementations later.

Treat the package as low-support infrastructure. With zero stars and zero dependents, community examples will be scarce. Pin the version in composer.lock, read the source, and keep your integration isolated behind a small application service provider. That way, replacing the package later is a contained change rather than a rewrite.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core