Backend Development 8 min read

Applying the Interface Programming Principle in Laravel: From SMS to Email Notifications

This article explains the interface‑programming principle, demonstrates its benefits with SMS and email notification services in Laravel, shows how to refactor code using an INotification interface, and configures the service container to switch implementations seamlessly, illustrating a flexible, maintainable backend design.

php中文网 Courses
php中文网 Courses
php中文网 Courses
Applying the Interface Programming Principle in Laravel: From SMS to Email Notifications

Adhering to the "program to an interface" philosophy, this article explores how defining clear interfaces rather than concrete implementations improves flexibility, modularity, testability, and extensibility of software systems, aligning with core design principles such as Open/Closed.

Without the Principle

First, a simple SMS notification service is created:

// app/Services/SmsNotificationService.php

namespace App\Services;

use App\Models\User;

class SmsNotificationService{
    public function sendRegistrationNotification(User $user){
        // retrieve user phone number
        // send registration SMS
    }
    public function subscriptionExpired(User $user, User $admin){
        // retrieve user phone number
        // notify user of expired subscription
        // retrieve admin phone number
        // notify admin
    }
}

The service is then used directly in a controller and a console command:

// app/Http/Controllers/AuthController.php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Services\SmsNotificationService;
use Illuminate\Http\Request;

class AuthController extends Controller{
    public function __construct(private SmsNotificationService $smsService){}
    public function Register(Request $request){
        // register user
        ...
        $this->smsService->sendRegistrationNotification($user);
    }
}
// app/Console/Commands/CheckSubscriptionExpirationCommand.php
// runs daily

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\SmsNotificationService;

class CheckSubscriptionExpirationCommand extends Command{
    protected $signature = 'subscriptions:check';
    public function handle(SmsNotificationService $smsService){
        $adminUser = // retrieve admin
        $expiredUsers = // retrieve expired users
        // for each user
        $smsService->subscriptionExpired($user, $admin);
        return 0;
    }
}

When the owner decides to switch notifications from SMS to email, every place that directly uses the SMS service must be changed.

Principle

Introduce an interface that defines the contract for notification services:

// app/Interfaces/INotification.php

namespace App\Interfaces;

use App\Models\User;

class INotification{
    public function sendRegistrationNotification(User $user);
    public function subscriptionExpired(User $user, User $admin);
}

Implement both SMS and Email services adhering to this interface:

// app/Services/SmsNotificationService.php

namespace App\Services;

use App\Models\User;
use App\Interfaces\INotification;

class SmsNotificationService implements INotification{
    public function sendRegistrationNotification(User $user){
        // send SMS
    }
    public function subscriptionExpired(User $user, User $admin){
        // send SMS about expiration
    }
}
// app/Services/EmailNotificationService.php

namespace App\Services;

use App\Models\User;
use App\Interfaces\INotification;

class EmailNotificationService implements INotification{
    public function sendRegistrationNotification(User $user){
        // send email
    }
    public function subscriptionExpired(User $user, User $admin){
        // send email about expiration
    }
}

Update the controller and command to depend on the interface instead of a concrete class:

// app/Http/Controllers/AuthController.php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Interfaces\INotification;
use Illuminate\Http\Request;

class AuthController extends Controller{
    public function __construct(private INotification $notificationService){}
    public function Register(Request $request){
        // register user
        ...
        $this->notificationService->sendRegistrationNotification($user);
    }
}
// app/Console/Commands/CheckSubscriptionExpirationCommand.php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Interfaces\INotification;

class CheckSubscriptionExpirationCommand extends Command{
    protected $signature = 'subscriptions:check';
    public function handle(INotification $notificationService){
        $adminUser = // retrieve admin
        $expiredUsers = // retrieve expired users
        // for each user
        $notificationService->subscriptionExpired($user, $admin);
        return 0;
    }
}

Finally, bind the interface to the desired implementation in the service container so the framework resolves the correct class automatically:

// app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Interfaces\INotification;
use App\Services\EmailNotificationService;

class AppServiceProvider extends ServiceProvider{
    public function register(){
        $this->app->bind(
            INotification::class,
            EmailNotificationService::class
        );
    }
}

With this setup, switching the notification method only requires changing the binding in one place, demonstrating how programming to an interface decouples code, enhances maintainability, and prepares the system for future extensions.

Backend Developmentphpdependency injectioninterface programmingLaravelService Container
php中文网 Courses
Written by

php中文网 Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.