---
title: "Strategy pattern in Laravel: selezione dinamica di implementazioni con Service Container, contextual binding e Pennant"
description: "Il Service Container di Laravel risolve lo Strategy pattern con bind(), when()->needs()->give() e Feature::active() per switching dinamico basato su feature flag."
canonical: "https://www.mauriziofonte.it/blog/post/flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.html"
author: "Maurizio Fonte"
date: "2025-02-24T08:13:39+01:00"
modified: "2026-04-14T17:48:55+02:00"
section: "Laravel"
tags: ["Laravel", "Architettura Software"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Laravel](https://www.mauriziofonte.it/blog/cat/laravel.html) [Architettura Software](https://www.mauriziofonte.it/blog/cat/architettura_software.html)

# [Strategy pattern in Laravel: selezione dinamica di implementazioni con Service Container, contextual binding e Pennant](https://www.mauriziofonte.it/blog/post/flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.html)

Lunedì 24 Febbraio 2025, alle 08:13 ![Strategy pattern in Laravel: selezione dinamica di implementazioni con Service Container, contextual binding e Pennant](https://www.mauriziofonte.it/image/001420200-flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.jpg)

In una piattaforma marketplace con migliaia di utenti attivi, il sistema di pricing aveva tre implementazioni: prezzo standard, prezzo con sconto enterprise, e un nuovo motore di pricing dinamico basato su domanda e offerta. La selezione dell'implementazione era gestita da un `if/elseif/else` in un Service Provider con 47 righe di logica condizionale che mescolava controlli su ruoli utente, piani di abbonamento, feature flag custom e fallback di configurazione. Ogni nuova implementazione richiedeva di modificare quella catena condizionale - violando l'Open/Closed Principle che il [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) identifica come principio fondante dello [Strategy pattern](https://refactoring.guru/design-patterns/strategy/php/example): il contesto che usa la strategia non dovrebbe cambiare quando si aggiunge una nuova strategia.

## Come implementa Laravel lo Strategy pattern senza factory custom?

Il [Service Container](https://laravel.com/docs/12.x/container) di Laravel è, di fatto, un resolver di Strategy pattern. Martin Fowler lo descrive come [Dependency Injection container](https://martinfowler.com/articles/injection.html): un oggetto che sa quale implementazione concreta fornire quando il codice richiede un'interfaccia astratta. In Laravel, tre meccanismi coprono i tre livelli di dinamicità:

`bind()` con closure definisce la strategia di default con logica di selezione a runtime. `when()->needs()->give()` - il contextual binding, presente fin da Laravel 5.0 - fornisce implementazioni diverse della stessa interfaccia a consumer diversi. `Feature::active()` di [Pennant](https://www.mauriziofonte.it/blog/post/feature-flag-laravel-pennant-custom-l9-l10-laravel-12-refactoring.html) dentro la closure di `bind()` aggiunge lo switching basato su feature flag:

```php
/* AppServiceProvider::register() */
use App\Contracts\PricingEngineInterface;
use App\Services\StandardPricing;
use App\Services\EnterprisePricing;
use App\Services\DynamicPricing;
use Laravel\Pennant\Feature;

/* 1. Binding dinamico con Pennant per lo switching globale */
$this->app->bind(PricingEngineInterface::class, function ($app) {
    $user = auth()->user();

    if ($user && Feature::for($user)->active('dynamic-pricing')) {
        return $app->make(DynamicPricing::class);
    }

    if ($user?->subscription()?->onPlan('enterprise')) {
        return $app->make(EnterprisePricing::class);
    }

    return $app->make(StandardPricing::class);
});

/* 2. Contextual binding: AdminController usa SEMPRE DynamicPricing per le preview */
$this->app->when(AdminPricingController::class)
    ->needs(PricingEngineInterface::class)
    ->give(DynamicPricing::class);
```

Il contextual binding ha precedenza sul binding globale: quando `AdminPricingController` richiede `PricingEngineInterface`, il container restituisce sempre `DynamicPricing`, indipendentemente dalla feature flag o dal piano utente. Per tutti gli altri consumer, la closure valuta le condizioni a runtime. Nessuna factory custom, nessuna catena `if/elseif` nel controller - la selezione è centralizzata nel provider e il controller riceve l'implementazione corretta via dependency injection.

## Come testare che il container risolva l'implementazione corretta?

Il test verifica il binding, non la logica di business dell'implementazione. `Feature::fake()` di Pennant e `actingAs()` permettono di controllare le condizioni di selezione:

```php
use App\Contracts\PricingEngineInterface;
use App\Services\StandardPricing;
use App\Services\DynamicPricing;
use App\Services\EnterprisePricing;
use App\Models\User;
use Laravel\Pennant\Feature;

public function test_standard_pricing_for_basic_user(): void
{
    Feature::fake(['dynamic-pricing' => false]);
    $user = User::factory()->create();
    $this->actingAs($user);

    $this->app->forgetInstance(PricingEngineInterface::class);
    $engine = $this->app->make(PricingEngineInterface::class);

    $this->assertInstanceOf(StandardPricing::class, $engine);
}

public function test_dynamic_pricing_when_feature_active(): void
{
    Feature::fake(['dynamic-pricing' => true]);
    $user = User::factory()->create();
    $this->actingAs($user);

    $this->app->forgetInstance(PricingEngineInterface::class);
    $engine = $this->app->make(PricingEngineInterface::class);

    $this->assertInstanceOf(DynamicPricing::class, $engine);
}

public function test_admin_controller_always_gets_dynamic_pricing(): void
{
    Feature::fake(['dynamic-pricing' => false]);
    $user = User::factory()->create();
    $this->actingAs($user);

    $controller = $this->app->make(AdminPricingController::class);
    $engine = (new \ReflectionProperty($controller, 'pricingEngine'))->getValue($controller);

    $this->assertInstanceOf(DynamicPricing::class, $engine);
}
```

`$this->app->forgetInstance()` è necessario quando il binding è `singleton` - senza di esso, il container restituisce l'istanza cachata dalla prima risoluzione, ignorando le condizioni aggiornate nel test.

## Errori comuni nella selezione dinamica dei servizi

Il primo errore è usare `singleton` per binding che dipendono dal contesto utente. Un singleton viene risolto una sola volta per l'intera vita dell'applicazione - se la prima richiesta è di un utente basic, tutti gli utenti successivi (inclusi quelli enterprise) riceveranno `StandardPricing`. Per binding dipendenti dall'utente corrente, usare `bind()` (risoluzione a ogni richiesta) invece di `singleton()`.

Il secondo è mettere la logica di selezione nel controller. Un `if (Feature::active('x'))` nel controller per decidere quale service usare annulla il vantaggio della dependency injection - il controller diventa dipendente dalla logica di selezione oltre che dal servizio. Il Service Provider è l'unico posto dove questa logica appartiene.

Il terzo è non avere un'interfaccia. Senza `PricingEngineInterface`, il controller dipende dalla classe concreta - aggiungere una nuova implementazione richiede di modificare il controller. L'interfaccia è il contratto che permette al container di sostituire l'implementazione senza che il consumer cambi.

Il quarto è non gestire il fallback. Se `Feature::active()` lancia un'eccezione (database Pennant non raggiungibile, scope non risolvibile), il binding fallisce e il controller riceve un errore 500. Un `try/catch` nella closure con fallback all'implementazione di default è la rete di sicurezza necessaria in produzione.

Lo Strategy pattern tramite Service Container è il meccanismo architetturale che rende un applicativo estensibile senza modificare il codice esistente. I [test automatici](https://www.mauriziofonte.it/blog/post/test-automatici-codebase-php-legacy-senza-riscrittura.html) dei binding garantiscono che ogni implementazione venga risolta nel contesto corretto, e i [middleware Laravel](https://www.mauriziofonte.it/blog/post/laravel-12-middleware-avanzati-sicurezza-logica-business-pmi-refactoring.html) possono aggiungere guardie trasversali alle rotte che usano implementazioni diverse. Per conoscere il mio approccio all'architettura di applicativi Laravel, visita [la mia pagina professionale](https://www.mauriziofonte.it/pages/about). Se il tuo applicativo ha catene condizionali per la selezione dei servizi che crescono a ogni nuova implementazione, [contattami per una consulenza dedicata](https://www.mauriziofonte.it/pages/contattami) - partiamo dall'inventario delle interfacce e dalla definizione dei binding.

Dall'articolo al lavoro vero

Se il codice di cui parla questo articolo è il tuo

Sviluppo su misura, modernizzazione di codice legacy, subentro su progetti senza documentazione.

[Vedi tre interventi con i numeri](https://www.mauriziofonte.it/programmatore-php.html) Ultima modifica: Martedì 14 Aprile 2026, alle 17:48 [« Articolo precedenteValidazione in Laravel 12: da closure inline a Rule Objects con ValidationRule per regole testabili, riutilizzabili e type-safe21 Febbraio 2025](https://www.mauriziofonte.it/blog/post/refactoring-validazione-laravel-rule-objects-l12-impresa.html)[Articolo successivo »UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB25 Febbraio 2025](https://www.mauriziofonte.it/blog/post/refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.html)

### Articoli correlati a Strategy pattern in Laravel: selezione dinamica di implementazioni con Service Container, contextual binding e Pennant

- [Feature flag in Laravel: modernizzare la gestione da approcci custom L9/L10 a Laravel Pennant per applicazioni aziendali scalabili in L12](https://www.mauriziofonte.it/blog/post/feature-flag-laravel-pennant-custom-l9-l10-laravel-12-refactoring.html)

  Le feature flag gestite con config e tabelle custom generano debito tecnico che cresce con ogni funzionalità aggiunta. Laravel Pennant offre · 12 Febbraio 2025

  ![Feature flag in Laravel: modernizzare la gestione da approcci custom L9/L10 a Laravel Pennant per applicazioni aziendali scalabili in L12](https://www.mauriziofonte.it/image/001340070-feature-flag-laravel-pennant-custom-l9-l10-laravel-12-refactoring.jpg)
- [Dependency injection avanzato in PHP 8: costruire servizi testabili e sostituibili](https://www.mauriziofonte.it/blog/post/dependency-injection-avanzato-php8-servizi-testabili-sostituibili.html)

  La dependency injection è il pattern che più di ogni altro determina la testabilità del codice PHP. Vi mostro i pattern avanzati che uso in progetti · 14 Gennaio 2026

  ![Dependency injection avanzato in PHP 8: costruire servizi testabili e sostituibili](https://www.mauriziofonte.it/image/003270070-dependency-injection-avanzato-php8-servizi-testabili-sostituibili.jpg)
- [Lazy loading in PHP 8.4: ottimizzare l'inizializzazione di oggetti costosi](https://www.mauriziofonte.it/blog/post/lazy-loading-php-84-ottimizzare-inizializzazione-oggetti-costosi.html)

  PHP 8.4 introduce lazy objects nativi che permettono di ritardare l'inizializzazione di servizi pesanti fino al primo utilizzo, senza proxy manuali. · 2 Febbraio 2026

  ![Lazy loading in PHP 8.4: ottimizzare l'inizializzazione di oggetti costosi](https://www.mauriziofonte.it/image/002440070-lazy-loading-php-84-ottimizzare-inizializzazione-oggetti-costosi.jpg)
- [Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili](https://www.mauriziofonte.it/blog/post/modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.html)

  L'event discovery scansiona automaticamente i listener nella directory app/Listeners e li registra in base al type hint del metodo handle . Introdotto · 26 Febbraio 2025

  ![Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili](https://www.mauriziofonte.it/image/001440070-modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.jpg)
- [Fat Controller in Laravel 12: dal controller da 200 righe a Service Layer, Action pattern e Dependency Injection](https://www.mauriziofonte.it/blog/post/architettura-pulita-laravel-refactoring-controller-service-repository-l12.html)

  Robert C. Martin definisce il Single Responsibility Principle come "un modulo deve essere responsabile verso un solo attore". Un controller Laravel · 11 Marzo 2025

  ![Fat Controller in Laravel 12: dal controller da 200 righe a Service Layer, Action pattern e Dependency Injection](https://www.mauriziofonte.it/image/001520070-architettura-pulita-laravel-refactoring-controller-service-repository-l12.jpg)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "Strategy pattern in Laravel: selezione dinamica di implementazioni con Service Container, contextual binding e Pennant", mauriziofonte.it, 2025. https://www.mauriziofonte.it/blog/post/flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.html

Pubblicato: 2025-02-24 · Aggiornato: 2026-04-14.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
