Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
LaravelPHPJune 23, 20264 min read

Laravel Middleware Decorators: Dynamic Service Swapping Explained

Learn how to use Laravel middleware and PSR-15 decorators for dynamic dependency injection and runtime service swapping to build flexible, testable apps.

LaravelPHPMiddlewareDependency InjectionArchitecturePSR-15Backend

Last month, our team hit a wall while trying to implement a multi-tenant feature that required different payment gateway implementations based on the request's origin. We were already using Laravel service container binding: Master interface-driven design, but standard service provider bindings felt too static for a request-specific requirement. We needed a way to change the implementation after the request started but before the controller logic fired.

The solution wasn't found in a complex package, but in the power of PSR-15 middleware combined with the Laravel container's runtime modification capabilities.

Implementing PSR-15 Middleware for Dynamic Service Swapping

When you're building high-scale applications, you often reach a point where standard Laravel dependency injection: A Practical Guide to Method Injection isn't enough. You don't just need a service; you need the right service for this specific HTTP context.

We first tried using app()->bind() inside a controller, but it felt dirty—it leaked infrastructure concerns into our application layer. Instead, we moved this logic into a middleware decorator.

Here is how you can implement a pattern to swap implementations on the fly:

PHP
namespace App\Http\Middleware;

use Closure;
use App\Contracts\PaymentGateway;
use App\Services\StripeGateway;
use App\Services\PayPalGateway;
use Illuminate\Http\Request;

class SwapPaymentGateway
{
    public function handle(Request $request, Closure $next)
    {
        $provider = $request->header('X-Payment-Provider');

        #6A9955">// Swap the implementation in the container at runtime
        app()->bind(PaymentGateway::class, function () use ($provider) {
            return $provider === 'paypal' 
                ? new PayPalGateway() 
                : new StripeGateway();
        });

        return $next($request);
    }
}

By binding the interface to a different concrete class inside the middleware, any class resolved via the container after this point—including your controllers—will automatically receive the swapped implementation.

The Power of Runtime Architecture

This approach is essentially a form of runtime architecture. It allows you to decouple your core business logic from the infrastructure constraints of the incoming request. When you master Laravel interfaces and service contracts for cleaner architecture, you stop worrying about which class you're injecting and start focusing on what the object does.

However, there's a caveat. If you resolve your dependency before the middleware executes—perhaps in a Service Provider's boot method—the swap won't take effect. You must ensure that your service resolution is deferred until the request cycle is well underway.

Why use Middleware instead of Service Providers?

You might ask why we wouldn't just use `Laravel Service Providers: A Beginner’s Guide to Bootstrapping](/blog/laravel-service-providers-a-beginner-s-guide-to-bootstrapping) to handle this. Service providers are typically instantiated early in the lifecycle. By the time they run, the request context is often not fully resolved or is too generic. Middleware, on the other hand, sits perfectly inside the request-response cycle.

Common Pitfalls to Watch For

  1. Singleton Overwrites: If you've registered your service as a singleton in the container, app()->bind() won't work as expected because the instance is already cached. Use app()->instance() or re-register the singleton if you absolutely must swap it.
  2. Performance Overhead: Don't do heavy logic inside the middleware. Keep the binding logic lightweight—roughly 2-3 milliseconds of overhead is acceptable, but anything more will compound across your request stack.
  3. Type-Hinting: Ensure your controllers are type-hinting the Interface, not the Concrete class. If you type-hint StripeGateway, the container cannot swap it for PayPalGateway without throwing a TypeError.

FAQ: Common Questions on Runtime Injection

Q: Can I use this to swap database connections? A: Yes. You can use the same pattern to dynamically change the database config using config(['database.connections.tenant.host' => '...']) inside your middleware.

Q: Is this "magic" bad for maintainability? A: It can be. Document your middleware clearly. If a developer looks at a controller and sees PaymentGateway, they should be able to find the middleware responsible for the swap easily. Use a consistent naming convention for your middleware classes.

Q: Does this work with Queues? A: No. Middleware only runs during the HTTP request cycle. If you need to swap dependencies for queued jobs, you'll need to handle that via the Job's constructor or a dedicated Job middleware.

Architecting your application this way feels a bit like "hacking" the container, but it's a standard practice in robust Laravel development. It keeps your code dry and ensures that your services remain focused on the task at hand rather than the context of the request. Next time, I’d probably look into using a dedicated Factory pattern to clean up the binding logic inside the middleware, as the if/else block can get messy once you support more than two providers. We're still iterating on this, but for now, it's saved us roughly three days of refactoring time across our multi-tenant modules.

Back to Blog

Similar Posts

LaravelPHPJune 23, 20264 min read

Laravel Pipelines for Traffic Shaping and Canary Deployments

Master Laravel Pipelines for traffic shaping and canary deployments. Move request routing logic into your application layer for precise, deterministic control.

Read more
LaravelPHP
June 22, 2026
4 min read

Laravel Tail Latency: Implementing Speculative Execution Middleware

Laravel tail latency can kill your p99 performance. Learn to implement speculative execution middleware to hedge requests and stabilize your microservices.

Read more
LaravelPHPJune 23, 20264 min read

Laravel Queues: Implementing Redis Lua Scripting Rate Limiting

Laravel Queues and Redis Lua scripting provide the foundation for high-throughput architecture. Learn to implement deterministic rate limiting today.

Read more