4 min readCalm pace · scan the outline anytime

Laravel composition root

Module ServiceProviders bind ports to adapters, register routes and listeners, and merge config — no business rules.

Composition root

On Laravel (13.x), each module’s ServiceProvider is the composition root. Register providers in bootstrap/providers.php. Framework APIs are welcome here only for wiring.

Allowed in Providers

  • bind / singleton Port → Adapter
  • Load UI routes
  • Merge module config
  • Register queue / event listeners (Infrastructure listeners)

Forbidden in Providers

  • Eligibility, reservation, pricing, stock policy
  • Calling repositories to “set up” business state
  • Orchestrating multi-step features (that is a Use Case)

Typical binds

namespace App\Modules\Ordering\Application\Providers;

use App\Modules\Ordering\Domain\Ports\Order\OrderRepositoryInterface;
use App\Modules\Ordering\Domain\Ports\Acl\WarehouseAvailabilityPortInterface;
use App\Modules\Ordering\Infrastructure\ExternalServices\WarehouseAvailabilityAclAdapter;
use App\Modules\Ordering\Infrastructure\Persistence\Eloquent\Repositories\OrderRepository;
use Illuminate\Support\ServiceProvider;

class OrderingServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(
            OrderRepositoryInterface::class,
            OrderRepository::class,
        );

        $this->app->bind(
            WarehouseAvailabilityPortInterface::class,
            WarehouseAvailabilityAclAdapter::class,
        );
    }

    public function boot(): void
    {
        $this->loadRoutesFrom(__DIR__ . '/../../UI/Routes/api.php');
        $this->mergeConfigFrom(__DIR__ . '/../../Infrastructure/Config/ordering.php', 'ordering');
    }
}

Register the module provider from the host application’s provider list (or package discovery) so the composition root actually runs.

Providers vs Use Cases

ConcernPlace
Route::prefix(...)->group(...)Provider
config()->merge(...) / mergeConfigFromProvider
Event::listen(...) / listener discoveryProvider
Place order / fulfill orderUse Case
Map Eloquent ↔ EntityRepository adapter
Map peer ModuleInterface ↔ local portACL adapter

Why Providers are special

Application/Providers needs Laravel to bind the container and load routes. That privilege must not be copied into Use Cases or Domain. If a “rule” appears in a Provider, move it to a Use Case or Domain service.

Core reference: Strictness ladder.

Modular Hexagonal Domain-Driven Design
Core 1.0.0-draft