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

# Structure d'application Laravel 11+

> Vue d'ensemble du Slim Application Skeleton introduit dans Laravel 11, avec l'implémentation interne allant de Application::configure() à ApplicationBuilder.

## Comparaison avec Laravel 10 et antérieur

Laravel 11 introduit le « Slim Application Skeleton », une refonte majeure de la structure d'application. Le changement le plus important est la suppression de la dispersion de la configuration au profit d'une centralisation dans un seul endroit : `bootstrap/app.php`.

| Élément                    | Laravel 10 et antérieur                         | Laravel 11+                                    |
| -------------------------- | ----------------------------------------------- | ---------------------------------------------- |
| Kernel HTTP                | `app/Http/Kernel.php`                           | Supprimé (intégré au framework)                |
| Kernel Console             | `app/Console/Kernel.php`                        | Supprimé (déplacé dans `routes/console.php`)   |
| Gestionnaire d'exceptions  | `app/Exceptions/Handler.php`                    | Supprimé (centralisé dans `bootstrap/app.php`) |
| Service providers          | 5 fichiers                                      | Un seul fichier `AppServiceProvider.php`       |
| Fichiers de routes         | `web.php` / `api.php` par défaut                | Seul `web.php` par défaut, `api.php` en opt-in |
| Bootstrap                  | Configuration dispersée dans plusieurs fichiers | Centralisée dans `bootstrap/app.php`           |
| Base de données par défaut | MySQL/PostgreSQL                                | SQLite                                         |

<Info>
  Ces changements concernent **les nouveaux projets**. Les applications Laravel 10 existantes mises à niveau continuent de fonctionner avec l'ancienne structure telle quelle.
</Info>

## Nouvelle structure de répertoires et de fichiers

### Structure du squelette

```
laravel-app/
├── app/
│   ├── Http/
│   │   └── Controllers/
│   ├── Models/
│   │   └── User.php
│   └── Providers/
│       └── AppServiceProvider.php
├── bootstrap/
│   ├── app.php          ← Cœur de la configuration de l'application
│   ├── cache/
│   └── providers.php    ← Liste des service providers
├── config/
├── database/
├── public/
│   └── index.php        ← Point d'entrée
├── resources/
├── routes/
│   ├── web.php          ← Routes web (par défaut)
│   └── console.php      ← Commandes Artisan et tâches planifiées
├── storage/
└── tests/
```

### `bootstrap/app.php` — le cœur de la configuration de l'application

```php theme={null}
<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
```

Ce seul fichier permet de configurer le routage, les middlewares et la gestion des exceptions. La configuration qui était dispersée dans `app/Http/Kernel.php`, `app/Console/Kernel.php` et `app/Exceptions/Handler.php` sous Laravel 10 est ici centralisée.

### `bootstrap/providers.php` — liste des service providers

```php theme={null}
<?php

use App\Providers\AppServiceProvider;

return [
    AppServiceProvider::class,
];
```

Ce fichier est l'endroit où enregistrer les service providers. Sous Laravel 10, ils étaient déclarés dans le tableau `providers` de `config/app.php`, mais ils sont désormais séparés dans `bootstrap/providers.php`. Par défaut sous Laravel 11, seul `AppServiceProvider` est présent.

<Tip>
  Lorsque vous installez un package avec `composer require`, celui-ci peut mettre à jour automatiquement `bootstrap/providers.php`. `config/app.php` n'est pas devenu obsolète, mais l'emplacement recommandé pour les nouveaux enregistrements est désormais `bootstrap/providers.php`.
</Tip>

### Changements dans le répertoire `routes/`

```
routes/
├── web.php      ← Routes web (chargées par défaut)
└── console.php  ← Définition des commandes Artisan et des tâches planifiées
```

`api.php` et `channels.php` ne sont plus présents par défaut. Générez-les si nécessaire avec les commandes Artisan.

```shell theme={null}
# Ajouter les routes API (installe api.php + Sanctum)
php artisan install:api

# Ajouter le broadcasting (installe channels.php + Reverb, etc.)
php artisan install:broadcasting
```

`routes/console.php` permet également de définir les tâches planifiées.

```php theme={null}
<?php

use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schedule;

Artisan::command('inspire', function () {
    $this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');

Schedule::command('emails:send')->daily();
```

### Fichiers supprimés

<AccordionGroup>
  <Accordion title="Suppression de app/Http/Kernel.php">
    Le kernel HTTP a été intégré dans `Illuminate\Foundation\Http\Kernel` au sein du framework. La personnalisation des middlewares se fait via `withMiddleware()` dans `bootstrap/app.php`.

    ```php theme={null}
    // Laravel 10 et antérieur : app/Http/Kernel.php
    protected $middleware = [
        \Illuminate\Http\Middleware\TrustProxies::class,
        // ...
    ];

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            // ...
        ],
    ];
    ```

    ```php theme={null}
    // Laravel 11+ : bootstrap/app.php
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->web(append: [
            EnsureUserIsSubscribed::class,
        ]);

        $middleware->validateCsrfTokens(except: ['stripe/*']);
    })
    ```
  </Accordion>

  <Accordion title="Suppression de app/Console/Kernel.php">
    Les deux responsabilités du kernel Console ont été séparées. Les commandes Artisan sont placées dans `app/Console/Commands/` et détectées automatiquement, et les tâches planifiées sont écrites dans `routes/console.php`.

    ```php theme={null}
    // Laravel 10 et antérieur : app/Console/Kernel.php
    protected function schedule(Schedule $schedule): void
    {
        $schedule->command('emails:send')->daily();
    }
    ```

    ```php theme={null}
    // Laravel 11+ : routes/console.php
    use Illuminate\Support\Facades\Schedule;

    Schedule::command('emails:send')->daily();
    ```
  </Accordion>

  <Accordion title="Suppression de app/Exceptions/Handler.php">
    Le gestionnaire d'exceptions a été intégré dans `Illuminate\Foundation\Exceptions\Handler` au sein du framework. La personnalisation se fait via `withExceptions()` dans `bootstrap/app.php`.

    ```php theme={null}
    // Laravel 10 et antérieur : app/Exceptions/Handler.php
    public function register(): void
    {
        $this->reportable(function (InvalidOrderException $e) {
            // ...
        });
    }
    ```

    ```php theme={null}
    // Laravel 11+ : bootstrap/app.php
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->report(function (InvalidOrderException $e) {
            // ...
        });
    })
    ```
  </Accordion>
</AccordionGroup>

## Fonctionnement de `Application::configure()`

### Implémentation interne dans le framework

`Application::configure()` est une méthode statique de `Illuminate\Foundation\Application`.

```php theme={null}
// Extrait de Illuminate\Foundation\Application
public static function configure(?string $basePath = null)
{
    $basePath = match (true) {
        is_string($basePath) => $basePath,
        default => static::inferBasePath(),
    };

    return (new Configuration\ApplicationBuilder(new static($basePath)))
        ->withKernels()
        ->withEvents()
        ->withCommands()
        ->withProviders();
}
```

Cette méthode effectue les opérations suivantes :

1. Détermine le répertoire racine de l'application à partir de `basePath`
2. Crée une instance de `Application`
3. L'enveloppe dans `ApplicationBuilder` et applique les paramètres par défaut
4. Retourne l'instance de `ApplicationBuilder`

Le point important est que `withKernels()` / `withEvents()` / `withCommands()` / `withProviders()` **sont déjà appelées à l'intérieur de `configure()`**. Il n'est pas nécessaire de les appeler à nouveau dans `bootstrap/app.php`.

### Le flux de configuration par chaînage de méthodes

```php theme={null}
Application::configure(basePath: dirname(__DIR__))  // retourne ApplicationBuilder
    ->withRouting(...)       // configure le routage et retourne $this
    ->withMiddleware(...)    // configure les middlewares et retourne $this
    ->withExceptions(...)    // configure la gestion des exceptions et retourne $this
    ->create();              // retourne l'instance Application
```

L'appel de `create()` extrait l'instance `Application` depuis `ApplicationBuilder`, et c'est cette instance `Application` que `bootstrap/app.php` retourne (`return`).

## Flux depuis la requête jusqu'au démarrage de l'application

```mermaid theme={null}
flowchart TD
    A["public/index.php<br>Point d'entrée"] --> B["require de bootstrap/app.php<br>pour obtenir l'Application"]
    B --> C["Application::configure()<br>Crée un ApplicationBuilder"]
    C --> D["withKernels()<br>Enregistre les kernels HTTP/Console"]
    D --> E["withEvents()<br>Configure la découverte d'événements"]
    E --> F["withCommands()<br>Configure les chemins des commandes Artisan"]
    F --> G["withProviders()<br>Charge bootstrap/providers.php"]
    G --> H["withRouting()<br>Configure le routage"]
    H --> I["withMiddleware()<br>Configure les middlewares"]
    I --> J["withExceptions()<br>Configure la gestion des exceptions"]
    J --> K["create()<br>Retourne l'instance Application"]
    K --> L["Le HTTP Kernel traite la requête<br>middlewares → routeur → contrôleur"]
    L --> M["Retourne la réponse"]
```

`public/index.php` sert de point d'entrée, charge `bootstrap/app.php` et récupère l'`Application`. Le HTTP Kernel fait ensuite passer la requête par le pipeline de middlewares, puis le routeur la dispatche vers le contrôleur.

## Analyse des principales méthodes de `ApplicationBuilder`

### `withRouting()` — traitement interne de l'enregistrement du routage

```php theme={null}
public function withRouting(
    ?Closure $using = null,
    array|string|null $web = null,
    array|string|null $api = null,
    ?string $commands = null,
    ?string $channels = null,
    ?string $pages = null,
    ?string $health = null,
    string $apiPrefix = 'api',
    ?callable $then = null
)
```

En interne, un callback est enregistré via `AppRouteServiceProvider::loadRoutesUsing()`, puis `AppRouteServiceProvider` est enregistré au moment du booting de l'application.

```php theme={null}
// Traitement interne de withRouting() (version simplifiée)
protected function buildRoutingCallback(...)
{
    return function () use ($web, $api, $pages, $health, $apiPrefix, $then) {
        if (is_string($api) || is_array($api)) {
            Route::middleware('api')->prefix($apiPrefix)->group($api);
        }

        if (is_string($health)) {
            Route::get($health, function () {
                Event::dispatch(new DiagnosingHealth);
                return response(View::file(...), status: 200);
            });
        }

        if (is_string($web) || is_array($web)) {
            Route::middleware('web')->group($web);
        }

        if (is_callable($then)) {
            $then($this->app);
        }
    };
}
```

**Points clés :**

* Les routes `api` reçoivent automatiquement le groupe de middlewares `api` et le préfixe `/api`
* En passant une chaîne à `health`, un endpoint de health check (par défaut `/up`) est automatiquement enregistré
* Le chemin `health` est exclu même en mode maintenance (configuré via `PreventRequestsDuringMaintenance::except()`)
* Notez que `api` est enregistré avant `web`. Si vous définissez des routes web et API sur le même chemin, la route API est prioritaire
* En passant une chaîne à `pages`, le routage [Laravel Folio](https://github.com/laravel/folio) est activé

### `withMiddleware()` — personnalisation des middlewares

```php theme={null}
public function withMiddleware(?callable $callback = null)
{
    $this->app->afterResolving(HttpKernel::class, function ($kernel) use ($callback) {
        $middleware = (new Middleware)
            ->redirectGuestsTo(fn () => route('login'));

        if (! is_null($callback)) {
            $callback($middleware);
        }

        $kernel->setGlobalMiddleware($middleware->getGlobalMiddleware());
        $kernel->setMiddlewareGroups($middleware->getMiddlewareGroups());
        $kernel->setMiddlewareAliases($middleware->getMiddlewareAliases());
        // ...
    });

    return $this;
}
```

`withMiddleware()` exécute le callback **après** que `HttpKernel` ait été résolu. C'est parce qu'il utilise le hook `afterResolving()`. L'objet `Middleware` passé au callback propose de nombreuses méthodes de personnalisation.

```php theme={null}
->withMiddleware(function (Middleware $middleware) {
    // Ajouter un middleware global
    $middleware->append(MyGlobalMiddleware::class);

    // Ajouter un middleware au groupe web
    $middleware->web(append: [EnsureUserIsSubscribed::class]);

    // Remplacer un middleware dans le groupe api
    $middleware->api(replace: [
        OldMiddleware::class => NewMiddleware::class,
    ]);

    // Configurer les exclusions CSRF
    $middleware->validateCsrfTokens(except: ['stripe/*', 'webhook/*']);

    // Modifier la redirection des utilisateurs non authentifiés
    $middleware->redirectGuestsTo('/custom-login');

    // Définir l'ordre de priorité des middlewares
    $middleware->priority([
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    ]);
})
```

### `withExceptions()` — configuration de la gestion des exceptions

```php theme={null}
public function withExceptions(?callable $using = null)
{
    $this->app->singleton(
        \Illuminate\Contracts\Debug\ExceptionHandler::class,
        \Illuminate\Foundation\Exceptions\Handler::class
    );

    if ($using !== null) {
        $this->app->afterResolving(
            \Illuminate\Foundation\Exceptions\Handler::class,
            fn ($handler) => $using(new Exceptions($handler)),
        );
    }

    return $this;
}
```

`withExceptions()` enregistre la classe `Handler` du framework en tant que singleton, puis configure le callback via `afterResolving()`. Un objet wrapper `Exceptions` est passé au callback.

```php theme={null}
->withExceptions(function (Exceptions $exceptions) {
    // Ne pas reporter certaines exceptions
    $exceptions->dontReport(MissedFlightException::class);

    // Rapport personnalisé pour certaines exceptions
    $exceptions->report(function (InvalidOrderException $e) {
        // Notification Slack par exemple
    });

    // Personnaliser la réponse HTTP pour certaines exceptions
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json(['message' => 'Not Found'], 404);
        }
    });

    // Throttling (ne pas reporter la même exception en boucle)
    $exceptions->throttle(function (Throwable $e) {
        return Limit::perMinute(20);
    });
})
```

### `withProviders()` — enregistrement des service providers

```php theme={null}
public function withProviders(array $providers = [], bool $withBootstrapProviders = true)
{
    RegisterProviders::merge(
        $providers,
        $withBootstrapProviders
            ? $this->app->getBootstrapProvidersPath()
            : null
    );

    return $this;
}
```

`withProviders()` est appelée par défaut à l'intérieur de `Application::configure()`, donc `bootstrap/providers.php` est chargé automatiquement. Pour passer des providers supplémentaires, il faut l'appeler explicitement dans `bootstrap/app.php`.

```php theme={null}
Application::configure(basePath: dirname(__DIR__))
    ->withProviders([
        // Ajouter des providers en plus du bootstrap/providers.php par défaut
        App\Providers\CustomServiceProvider::class,
    ])
    ->withRouting(...)
    ->create();
```

<Warning>
  Passer `withBootstrapProviders: false` empêche le chargement de `bootstrap/providers.php`. Sauf raison particulière, omettez cet argument.
</Warning>

### Autres méthodes importantes

| Méthode                              | Description                                                                                   |
| ------------------------------------ | --------------------------------------------------------------------------------------------- |
| `withKernels()`                      | Enregistre les kernels HTTP / Console en singletons. Appelé automatiquement par `configure()` |
| `withEvents()`                       | Active la découverte d'événements. Appelé automatiquement par `configure()`                   |
| `withCommands(array $commands)`      | Enregistre des classes de commandes Artisan ou des répertoires supplémentaires                |
| `withSchedule(callable $callback)`   | Définit les tâches planifiées dans `bootstrap/app.php`                                        |
| `withBroadcasting(string $channels)` | Enregistre le fichier des channels de broadcasting                                            |
| `withBindings(array $bindings)`      | Enregistre des bindings du conteneur                                                          |
| `withSingletons(array $singletons)`  | Enregistre des bindings singletons                                                            |
| `registered(callable $callback)`     | Ajoute un callback exécuté après l'enregistrement des service providers                       |
| `booting(callable $callback)`        | Ajoute un callback exécuté au moment du booting                                               |
| `booted(callable $callback)`         | Ajoute un callback exécuté au moment du booted                                                |
| `create()`                           | Retourne l'instance de `Application`                                                          |

## Intention de conception : pourquoi il en est ainsi

### Une configuration « code-first »

Sous Laravel 10 et antérieur, `app/Http/Kernel.php` utilisait un style de listing des middlewares dans des tableaux. C'était proche d'un fichier de configuration, avec pour inconvénient un faible support du système de types PHP et des IDE.

Laravel 11 est passé à un style callback `withMiddleware(function (Middleware $middleware) { ... })`. Cela permet la complétion typée et une écriture naturelle de configurations dynamiques utilisant conditions, boucles et autre logique.

### De la « convention plutôt que configuration » à la « configuration explicite »

Rendre `api.php` opt-in a permis de résoudre le fait que le groupe de middlewares `api` était toujours chargé même dans les applications n'utilisant pas de routes API. Le principe est : les fonctionnalités non utilisées n'existent pas par défaut.

### Utilisation du hook `afterResolving()`

Utiliser `afterResolving()` dans `withMiddleware()` et `withExceptions()` permet d'éviter les problèmes d'ordre de configuration. Les méthodes de `ApplicationBuilder` sont appelées avant le démarrage complet de l'application, mais le traitement réel (application des paramètres au kernel) est exécuté de manière différée lors de la première résolution du kernel.

## Exemples pratiques de personnalisation

### Coexistence d'API et de Web

```php theme={null}
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        apiPrefix: 'api/v1',  // change le préfixe par défaut /api
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
```

### Personnalisation des middlewares

```php theme={null}
->withMiddleware(function (Middleware $middleware) {
    // Ajouter un middleware de vérification d'authentification aux routes web
    $middleware->web(append: [
        \App\Http\Middleware\EnsureEmailIsVerified::class,
    ]);

    // Exclure certains middlewares des routes API
    $middleware->api(remove: [
        \Illuminate\Session\Middleware\StartSession::class,
    ]);

    // Exclure les endpoints webhook du CSRF
    $middleware->validateCsrfTokens(except: [
        'webhook/*',
        'stripe/webhook',
    ]);

    // Définir un alias pour certains middlewares
    $middleware->alias([
        'subscribed' => \App\Http\Middleware\EnsureUserIsSubscribed::class,
    ]);
})
```

### Centraliser les tâches planifiées dans `bootstrap/app.php`

Les tâches planifiées peuvent être écrites dans `routes/console.php`, mais `withSchedule()` permet de les regrouper dans `bootstrap/app.php`.

```php theme={null}
use Illuminate\Console\Scheduling\Schedule;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withSchedule(function (Schedule $schedule) {
        $schedule->command('emails:send')->daily();
        $schedule->command('reports:generate')->weeklyOn(1, '8:00');
        $schedule->job(new PruneOldRecords)->daily();
    })
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();
```

### Personnalisation de la gestion des exceptions

```php theme={null}
->withExceptions(function (Exceptions $exceptions) {
    // Toujours retourner en JSON pour les requêtes API
    $exceptions->render(function (Throwable $e, Request $request) {
        if ($request->is('api/*') || $request->wantsJson()) {
            $status = match (true) {
                $e instanceof NotFoundHttpException => 404,
                $e instanceof AuthenticationException => 401,
                $e instanceof AuthorizationException => 403,
                $e instanceof ValidationException => 422,
                default => 500,
            };

            return response()->json([
                'message' => $e->getMessage(),
            ], $status);
        }
    });

    // Notifier Slack uniquement en production
    if (app()->isProduction()) {
        $exceptions->report(function (Throwable $e) {
            app(SlackNotifier::class)->notify($e);
        })->stop();
    }
})
```

### Gérer les bindings du conteneur dans `bootstrap/app.php`

Pour une application de petite taille, vous pouvez écrire des bindings simples directement dans `bootstrap/app.php` plutôt que dans `AppServiceProvider`.

```php theme={null}
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withSingletons([
        \App\Contracts\PaymentGateway::class => \App\Services\StripeGateway::class,
        \App\Contracts\MailService::class => \App\Services\SendgridMailService::class,
    ])
    ->withMiddleware(...)
    ->withExceptions(...)
    ->create();
```

## Ordre du processus de démarrage de Laravel

Au démarrage d'une application Laravel, les service providers et les hooks de l'Application sont exécutés dans l'ordre suivant :

1. Exécution de `register()` de tous les ServiceProviders
2. `registered()` de l'Application
3. `booting()` de l'Application
4. Exécution de `boot()` de tous les ServiceProviders
5. `booted()` de l'Application

Vous pouvez vérifier cet ordre d'exécution en ajoutant le code suivant à `AppServiceProvider`.

```php theme={null}
class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        info('1. AppServiceProvider register');

        $this->booting(function () {
            info('4. AppServiceProvider booting');
        });

        $this->booted(function () {
            info('5. AppServiceProvider booted');
        });

        $this->app->registered(function () {
            info('2. app registered');
        });

        $this->app->booting(function () {
            info('3. app booting');
        });

        $this->app->booted(function () {
            info('6. AppServiceProvider@register app booted');
        });
    }

    public function boot(): void
    {
        $this->app->booted(function () {
            info('7. AppServiceProvider@boot app booted');
        });
    }
}
```

Les méthodes `registered()`, `booting()` et `booted()` de `ApplicationBuilder` ne font qu'enregistrer des callbacks sur l'Application. Habituellement, il n'est pas nécessaire de les utiliser, mais elles permettent des opérations spéciales comme modifier le Kernel dont la préparation est terminée via `booted()`.

```php theme={null}
// bootstrap/app.php

use Illuminate\Contracts\Http\Kernel;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(...)
    ->withMiddleware(...)
    ->withExceptions(...)
    ->booted(function (Application $app) {
        $kernel = $app->make(Kernel::class);

        //

        $app->instance(Kernel::class, $kernel);
    })
    ->create();
```

## Étapes suivantes

<Card title="Conteneur de services" icon="box" href="/fr/service-container">
  Comprenez le fonctionnement du conteneur de services utilisé en interne par `ApplicationBuilder`.
</Card>

<Card title="FAQ nouvelle structure" icon="circle-question" href="/fr/advanced/app-structure-faq">
  Réponses aux questions fréquentes sur la nouvelle structure d'application.
</Card>


## Related topics

- [FAQ sur la nouvelle structure d'application Laravel 11+](/fr/advanced/app-structure-faq.md)
- [Guide de migration de l'ancienne vers la nouvelle structure](/fr/advanced/app-structure-migration.md)
- [Structure des répertoires](/fr/directory-structure.md)
- [Mise à niveau de Laravel 10 à 11](/fr/blog/upgrade-10-to-11.md)
- [Laravel Maestro — l'orchestrateur de développement des starter kits](/fr/blog/maestro-introduction.md)
