> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Service providers diferidos

> Explica cómo cargar servicios de forma diferida implementando la interfaz DeferrableProvider. Una funcionalidad discreta pero clave para optimizar el rendimiento de un paquete.

Los service providers registrados durante el arranque de Laravel se cargan siempre, incluso si sus funcionalidades no se usan ni una vez durante la petición. Los service providers diferidos (Deferred Service Provider) resuelven este problema: retrasan la carga del provider hasta que sus servicios se usan realmente.

<Info>
  Esta página da por sabido el contenido de [Fundamentos del desarrollo de paquetes](/es/advanced/package-development). Es recomendable entender primero cómo funciona un service provider normal.
</Info>

## Por qué necesitas providers diferidos

Un service provider normal ejecuta `register()` y `boot()` en cada petición. Es un desperdicio inicializar en todas las páginas funcionalidades como el envío de correos, colas o caché si no se usan en muchas de ellas.

```php theme={null}
// register() de este provider se llama en todas las peticiones
class ReportServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Enlazamos un servicio de reports pesado en cada request
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });
    }
}
```

Si haces este provider diferido, solo se inicializará en las peticiones que generen realmente un report.

***

## Interfaz DeferrableProvider

`Illuminate\Contracts\Support\DeferrableProvider` es una interfaz muy simple con un único método `provides()`.

```php theme={null}
namespace Illuminate\Contracts\Support;

interface DeferrableProvider
{
    public function provides();
}
```

Basta con implementar esta interfaz para que el provider pase a modo diferido.

***

## Implementación básica

La implementación de un provider diferido consta de tres pasos.

<Steps>
  <Step title="Implementar DeferrableProvider">
    ```php theme={null}
    use Illuminate\Contracts\Support\DeferrableProvider;
    use Illuminate\Support\ServiceProvider;

    class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
    {
        // ...
    }
    ```
  </Step>

  <Step title="Enlazar los servicios en register()">
    Igual que en un provider normal, define los bindings en `register()`.

    ```php theme={null}
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, function ($app) {
            return new ReportGenerator(
                $app->make(PdfRenderer::class),
                $app->make(ChartRenderer::class),
                $app->make(DataExporter::class),
            );
        });

        $this->app->singleton(ReportRepository::class, function ($app) {
            return new ReportRepository($app->make('db'));
        });
    }
    ```
  </Step>

  <Step title="Devolver los servicios registrados en provides()">
    `provides()` debe devolver **todos los servicios** que has enlazado en `register()`. Laravel usa esta lista para saber al pedir qué servicio debe cargar este provider.

    ```php theme={null}
    public function provides(): array
    {
        return [
            ReportGenerator::class,
            ReportRepository::class,
        ];
    }
    ```
  </Step>
</Steps>

***

## Cómo funciona el manifest de servicios

En el arranque, Laravel genera un archivo llamado `bootstrap/cache/services.php`. Este manifest contiene la lista de servicios que ofrecen los providers diferidos.

```php theme={null}
// Ejemplo de bootstrap/cache/services.php (generado automáticamente)
return [
    'providers' => [
        // La misma lista de bootstrap/providers.php
    ],
    'eager' => [
        // Providers de carga inmediata
        App\Providers\AppServiceProvider::class,
    ],
    'deferred' => [
        // Mapping servicio => clase del provider
        'App\Services\ReportGenerator' => ReportServiceProvider::class,
        'App\Repositories\ReportRepository' => ReportServiceProvider::class,
        'cache'     => Illuminate\Cache\CacheServiceProvider::class,
        'cache.store' => Illuminate\Cache\CacheServiceProvider::class,
        'queue'     => Illuminate\Queue\QueueServiceProvider::class,
    ],
    'when' => [],
];
```

Gracias a este manifest, Laravel sabe qué provider ofrece cada servicio sin necesidad de cargar el archivo del provider. El provider real solo se carga cuando el servicio se resuelve por primera vez.

<Tip>
  Cuando añadas o modifiques providers, regenera el manifest.

  ```shell theme={null}
  php artisan optimize:clear
  # o bien
  php artisan clear-compiled
  ```
</Tip>

### Flujo interno de funcionamiento

```mermaid theme={null}
sequenceDiagram
    participant App as Aplicación<br>Laravel
    participant Container as Contenedor<br>de servicios
    participant Manifest as Manifest<br>services.php
    participant Provider as ReportService<br>Provider

    App->>Manifest: carga el manifest al arrancar
    Manifest-->>App: devuelve la lista de servicios diferidos
    Note over App: en este punto el provider aún no se ha cargado

    App->>Container: $app->make(ReportGenerator::class)
    Container->>Manifest: comprueba si es un servicio diferido
    Manifest-->>Container: lo proporciona ReportServiceProvider
    Container->>Provider: llama a register() + boot()
    Provider-->>Container: registra los bindings
    Container-->>App: devuelve la instancia de ReportGenerator
```

***

## Importancia del método provides()

Si en `provides()` **te olvidas** de algún servicio, ese servicio no se resolverá nunca.

```php theme={null}
public function register(): void
{
    $this->app->singleton(ReportGenerator::class, fn ($app) => new ReportGenerator());

    // Binding adicional
    $this->app->singleton('report', fn ($app) => $app->make(ReportGenerator::class));
}

public function provides(): array
{
    return [
        ReportGenerator::class,
        'report',  // ← no olvides incluir también las claves por string
    ];
}
```

Lo mismo aplica si usas las propiedades `$bindings` / `$singletons`.

```php theme={null}
class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public $singletons = [
        AnalyticsClient::class => DefaultAnalyticsClient::class,
    ];

    public function provides(): array
    {
        // Devuelve todas las claves declaradas en $singletons
        return [
            AnalyticsClient::class,
        ];
    }
}
```

***

## Limitaciones de los providers diferidos

Los providers diferidos son adecuados solo para providers cuyo único cometido es **registrar bindings en el contenedor**. Los providers que hacen lo siguiente en `boot()` no pueden ser diferidos.

| Limitación                                                      | Motivo                                                                                     |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| Registro de rutas                                               | Las rutas se resuelven al arrancar la aplicación, así que en modo diferido no funcionaría. |
| Registro de middlewares globales                                | Debe estar registrado antes de procesar la petición.                                       |
| Registro de listeners de eventos (los que se necesitan siempre) | Si el listener no está registrado antes de que se dispare el evento, no se ejecuta.        |
| Añadir directivas de Blade                                      | Debe estar registrado antes de compilar las vistas.                                        |

<Warning>
  Puedes escribir un método `boot()` en un provider diferido, pero su contenido no se ejecutará hasta que se resuelva el servicio. Si en `boot()` haces cosas «que siempre se necesitan», como rutas o middlewares, obtendrás un comportamiento inesperado.
</Warning>

***

## Método when() — registro por evento

Con el método `when()` puedes hacer que un provider se registre cuando se dispare un evento concreto. Es útil para providers que solo se necesitan en contextos específicos, como procesos de jobs.

```php theme={null}
class ReportServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->app->singleton(ReportGenerator::class, fn () => new ReportGenerator());
    }

    public function provides(): array
    {
        return [ReportGenerator::class];
    }

    /**
     * Además de al resolver los bindings,
     * carga el provider cuando se disparen los eventos indicados.
     */
    public function when(): array
    {
        return [
            \App\Events\ReportRequested::class,
        ];
    }
}
```

Cuando se dispare uno de los eventos devueltos por `when()`, el provider se cargará aunque no se haya resuelto directamente ningún servicio.

***

## Uso en el desarrollo de paquetes

Al distribuir un paquete de terceros, los providers diferidos también contribuyen al rendimiento de las aplicaciones de tus usuarios.

### Patrón recomendado

```php theme={null}
namespace Acme\Analytics;

use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class AnalyticsServiceProvider extends ServiceProvider implements DeferrableProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/analytics.php', 'analytics');

        $this->app->singleton(AnalyticsManager::class, function ($app) {
            return new AnalyticsManager($app->make('config')->get('analytics'));
        });

        $this->app->singleton('analytics', fn ($app) => $app->make(AnalyticsManager::class));
    }

    public function boot(): void
    {
        // Limita boot() al registro de publicaciones
        if ($this->app->runningInConsole()) {
            $this->publishes([
                __DIR__.'/../config/analytics.php' => config_path('analytics.php'),
            ], 'analytics-config');
        }
    }

    public function provides(): array
    {
        return [
            AnalyticsManager::class,
            'analytics',
        ];
    }
}
```

<Info>
  `mergeConfigFrom()` comprueba internamente si la configuración ya está cacheada, así que es seguro llamarlo dentro del `register()` de un provider diferido. Eso sí, si la configuración está cacheada, no hará nada.
</Info>

### Separar el registro de comandos Artisan con `runningInConsole()`

Como el registro de comandos solo es necesario al arrancar Artisan, protégelo con `runningInConsole()`. Si además quieres diferir el provider que registra los comandos, incluye las clases de comando en `provides()` o proporciona un provider separado dedicado a los comandos.

```php theme={null}
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            AnalyticsFlushCommand::class,
        ]);
    }
}
```

***

## Ejemplos en el core de Laravel

Muchos de los providers del core de Laravel son diferidos. Es un diseño pensado para no cargar de forma inmediata todos los servicios que no se usan en cada petición.

| Provider                     | Servicios que provee                    |
| ---------------------------- | --------------------------------------- |
| `CacheServiceProvider`       | `cache`, `cache.store`, `RateLimiter`   |
| `QueueServiceProvider`       | `queue`, `queue.worker`, `queue.failer` |
| `MailServiceProvider`        | `mail.manager`, `mailer`                |
| `RedisServiceProvider`       | `redis`, `redis.connection`             |
| `HashServiceProvider`        | `hash`, `hash.driver`                   |
| `ValidationServiceProvider`  | `validator`, `validation.presence`      |
| `TranslationServiceProvider` | `translator`                            |
| `BroadcastServiceProvider`   | `Broadcast`                             |

En una aplicación con solo endpoints de API puede darse el caso de que `MailServiceProvider` o `BroadcastServiceProvider` no se carguen nunca durante una petición.

***

## Criterios para decidir si diferir

<AccordionGroup>
  <Accordion title="Servicios adecuados para diferir">
    * Servicios que no se usan en todas las peticiones (correo, reports, clientes de APIs externas, etc.)
    * Servicios cuya inicialización requiere conexiones externas o lectura de archivos.
    * Servicios con grafos de objetos pesados.
    * Providers que solo aportan comandos usados por CLI.
  </Accordion>

  <Accordion title="Servicios no adecuados para diferir">
    * Providers que registran rutas (`loadRoutesFrom`, etc.).
    * Providers que registran middlewares o handlers de excepciones activos permanentemente.
    * Providers que registran global scopes u observers de Eloquent.
    * Servicios ligeros usados en la mayoría de peticiones (donde la sobrecarga del diferido puede ser mayor).
  </Accordion>
</AccordionGroup>

***

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Fundamentos del desarrollo de paquetes" icon="box" href="/es/advanced/package-development">
    Aprende cómo desarrollar paquetes Laravel centrados en los service providers.
  </Card>

  <Card title="Gestión de la compatibilidad de versiones de paquetes" icon="layers" href="/es/advanced/package-versioning">
    Estrategias de mantenimiento de paquetes ante grandes actualizaciones de versión de Laravel y PHP.
  </Card>
</Columns>


## Related topics

- [Service providers](/es/service-providers.md)
- [FAQ de la nueva estructura de aplicación de Laravel 11 y posteriores](/es/advanced/app-structure-faq.md)
- [Ciclo de vida de la solicitud](/es/lifecycle.md)
- [Actualización de Laravel 10 a 11](/es/blog/upgrade-10-to-11.md)
- [Estructura de aplicación en Laravel 11 y posteriores](/es/advanced/app-structure.md)
