Laravel

Lesson 04

Service container bindings

Use Laravel's service container to declare infrastructure choices once and inject contracts into application services.

Good Code

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use App\Billing\PaymentGateway;
use App\Billing\StripePaymentGateway;
use Illuminate\Support\ServiceProvider;

final class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // The container owns the infrastructure choice for the payment contract.
        $this->app->bind(PaymentGateway::class, StripePaymentGateway::class);
    }
}

Bad Code

app/Services/CheckoutService.php
<?php

final class CheckoutService
{
    public function charge(Order $order): Receipt
    {
        // Creating infrastructure here couples the use case to env and SDK setup.
        $client = new Stripe\StripeClient(config('services.stripe.secret'));

        return $client->charges->create([
            'amount' => $order->total_cents,
            'currency' => 'usd',
        ]);
    }
}

Review Notes

What to review

Good Code

The good version declares the concrete payment gateway at a provider boundary. Services can request the contract without knowing which SDK or credential setup is used.

Bad Code

The bad version creates the SDK client inside the use case. That makes tests depend on configuration and pushes infrastructure choices into business code.

Takeaways

  • Application code should depend on contracts or services that can be replaced in tests and configured at provider boundaries.