Le Starter Pack Microservices Laravel 2026 : Architecture, Docker & RabbitMQ
Tutoriel radical pour monter une architecture microservices Laravel propre : Monorepo, Docker Compose, RabbitMQ, Gateway JWT et Events asynchrones. Code copy/paste 'production-ready'.
Sommaire
Tu es développeur Laravel et tu entends parler de microservices. Tu veux voir du concret, pas de la théorie fumeuse. Tu veux savoir comment on connecte vraiment 4 applications Laravel entre elles sans que ça devienne un plat de spaghettis.
Ce guide est un Starter Pack. Il te donne une base saine, testée et reproductible pour démarrer un projet distribué. Nous allons construire une application "E-commerce" minimaliste (MVP) mais architecturée correctement.
1. Pourquoi (et quand pas) Microservices ?
Pourquoi ? Pour l'indépendance de déploiement et l'organisation des équipes. Si tu as 30 développeurs qui se marchent dessus sur le même repo, découper le monolithe permet à la "Team Checkout" de déployer sans attendre la "Team Catalogue".
Quand NE PAS le faire ? Si tu es seul ou une petite équipe (< 5). La complexité opérationnelle (logging distribué, cohérence éventuelle, latence réseau) va te tuer. Si ton problème est la performance, fais un Monolithe Modulaire bien cachée. Si ton problème est l'organisation, lis la suite.
2. Architecture Cible
On part sur un pattern classique : API Gateway + Backend for Frontend (BFF).
┌──────────────┐
│ Client Web │
└──────┬───────┘
│ HTTPS (JSON + JWT)
▼
┌──────────────────┐
│ API GATEWAY │ (Port 8000)
│ Auth & Routing │
└────┬────┬────┬───┘
│ │ │
┌───────────────┘ │ └────────────────┐
│ HTTP │ HTTP │ HTTP
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ SERVICE │ │ SERVICE │ │ SERVICE │
│ USERS │ │ CATALOG │ │ ORDERS │
│ (Port 8001) │ │ (Port 8002) │ │ (Port 8003) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ DB │ DB │ DB + Publie
▼ ▼ ▼
(db_users) (db_catalog) (db_orders)
│
│ "OrderCreated" (Async)
▼
┌──────────────┐
│ RABBITMQ │
└──────┬───────┘
│ Consomme
▼
┌──────────────┐
│ SERVICE │
│ NOTIFICATION │
└──────────────┘
Les Flux :
- Synchrone (Bleu) : Le client parle uniquement à la Gateway. La Gateway vérifie le JWT, puis proxyfie la requête vers le service adéquat via HTTP interne.
- Asynchrone (Rouge) : Quand une commande est créée,
orders-servicene parle PAS ànotification-service. Il publie un message sur RabbitMQ.notification-servicese réveille, consomme le message et envoie un email.
3. Arborescence & Conventions (Monorepo)
Un monorepo simplifie la gestion de l'infra locale et le partage de code (DTOs, Enums).
my-microservices/
├── apps/ # Le code de tes services
│ ├── gateway/ # Laravel
│ ├── users/ # Laravel
│ ├── catalog/ # Laravel
│ ├── orders/ # Laravel
│ └── notifications/ # Laravel
├── docker/ # Config Docker spécifique (Nginx, PHP...)
├── docker-compose.yml # L'orchestration locale
├── Makefile # Tes raccourcis vitaux
└── README.md
Convention de nommage réseau :
Dans Docker Compose, les hostnames seront les noms des services : http://users, http__catalog, etc.
4. Docker Compose : L'Infra complète
C'est la pièce maîtresse. Copie-colle ça dans docker-compose.yml à la racine.
On utilise une image Postgres unique pour l'économie de ressources, mais avec 3 bases de données distinctes créées au démarrage.
version: '3.8'
services:
# --- INFRASTRUCTURE ---
postgres:
image: postgres:15-alpine
container_name: ms_postgres
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: root
# Script d'init pour créer les multiples DBs
POSTGRES_MULTIPLE_DATABASES: "users_db,catalog_db,orders_db,notifications_db"
volumes:
- pg_data:/var/lib/postgresql/data
- ./docker/postgres/init:/docker-entrypoint-initdb.d
networks:
- backend
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: ms_rabbitmq
ports:
- "5672:5672" # AMQP protocol
- "15672:15672" # Dashboard Management (User: guest/guest)
networks:
- backend
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 10s
timeout: 10s
retries: 5
# --- SERVICES MÉTIERS ---
gateway:
build:
context: .
dockerfile: docker/php/Dockerfile
args:
PATH_TO_APP: apps/gateway
container_name: ms_gateway
ports:
- "8000:8000"
volumes:
- ./apps/gateway:/var/www/html
environment:
APP_PORT: 8000
DB_HOST: postgres
RABBITMQ_HOST: rabbitmq
depends_on:
- postgres
- rabbitmq
command: php artisan serve --host=0.0.0.0 --port=8000
networks:
- backend
users:
build:
context: .
dockerfile: docker/php/Dockerfile
args:
PATH_TO_APP: apps/users
container_name: ms_users
volumes:
- ./apps/users:/var/www/html
environment:
APP_PORT: 8001
DB_HOST: postgres
DB_DATABASE: users_db
command: php artisan serve --host=0.0.0.0 --port=8001
networks:
- backend
catalog:
build:
context: .
dockerfile: docker/php/Dockerfile
args:
PATH_TO_APP: apps/catalog
container_name: ms_catalog
volumes:
- ./apps/catalog:/var/www/html
environment:
APP_PORT: 8002
DB_HOST: postgres
DB_DATABASE: catalog_db
command: php artisan serve --host=0.0.0.0 --port=8002
networks:
- backend
orders:
build:
context: .
dockerfile: docker/php/Dockerfile
args:
PATH_TO_APP: apps/orders
container_name: ms_orders
volumes:
- ./apps/orders:/var/www/html
environment:
APP_PORT: 8003
DB_HOST: postgres
DB_DATABASE: orders_db
RABBITMQ_HOST: rabbitmq
depends_on:
- rabbitmq
command: php artisan serve --host=0.0.0.0 --port=8003
networks:
- backend
notifications:
build:
context: .
dockerfile: docker/php/Dockerfile
args:
PATH_TO_APP: apps/notifications
container_name: ms_notifications
volumes:
- ./apps/notifications:/var/www/html
environment:
DB_HOST: postgres
DB_DATABASE: notifications_db
RABBITMQ_HOST: rabbitmq
depends_on:
- rabbitmq
# Ce service ne sert pas de HTTP, il consomme des queues
command: php artisan rabbitmq:consume orders.created --tries=3
networks:
- backend
steps:
backend:
driver: bridge
volumes:
pg_data:
Tip Infra : Pour que PostgreSQL crée automatiquement les bases, ajoutez ce script bash
docker/postgres/init/create-multiple-postgresql-databases.sh(Google it: "Postgres docker multiple databases") dans votre repo. Sans ça, seule la DB par défaut est créée.
5. Création des Apps Laravel
Pas de magie, on utilise Composer.
mkdir apps
cd apps
# Création des 5 projets
for service in gateway users catalog orders notifications; do
composer create-project laravel/laravel $service
# Tip: Installez tout de suite le driver RabbitMQ
cd $service
composer require vladimir-yuldashev/laravel-queue-rabbitmq
cd ..
done
Configuration des `.env` (Exemple pour `orders`)
Dans apps/orders/.env :
APP_NAME=OrdersService
APP_URL=http://ms_orders:8003
DB_CONNECTION=pgsql
DB_HOST=ms_postgres # Nom du container ou 'postgres' selon ton network
DB_PORT=5432
DB_DATABASE=orders_db
DB_USERNAME=postgres
DB_PASSWORD=root
QUEUE_CONNECTION=rabbitmq
RABBITMQ_HOST=ms_rabbitmq
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_QUEUE=orders.created
6. Implémenter la Gateway (HTTP Proxy)
La Gateway est "bête". Elle ne contient pas de logique métier. Elle route.
Dans apps/gateway, on utilise le client HTTP de Laravel.
`routes/api.php` de la Gateway
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Http;
Route::middleware('api')->group(function () {
// Auth Proxy (vers Users Service qui gère l'auth pour de vrai, ou géré ici)
// Pour ce tuto, on proxyfie tout.
// USERS Service
Route::get('/users/{id}', function ($id) {
return Http::get("http://ms_users:8001/api/users/{$id}")->json();
});
// CATALOG Service
Route::get('/products', function () {
return Http::get("http://ms_catalog:8002/api/products")->json();
});
// ORDERS Service
Route::post('/orders', function () {
// Validation basique d'entrée ici
$data = request()->all();
// Appel au service
$response = Http::post("http://ms_orders:8003/api/orders", $data);
return response()->json($response->json(), $response->status());
});
});
Bonus Sécurité : Ajoute un middleware
CheckJwtglobal sur la Gateway. C'est le seul point d'entrée public. Les services internes (Users, Catalog...) ne doivent PAS être exposés sur des ports publics en prod (en Docker Compose, ils ne le sont pas, seul8000est mappé).
7. L'Événement Asynchrone : OrderCreated
Quand une commande est créée dans orders, on veut notifier notifications SANS bloquer la réponse HTTP.
Dans `apps/orders`
- Créer la commande via Controller.
- Publier un event sur la queue.
// apps/orders/app/Http/Controllers/OrderController.php
public function store(Request $request)
{
$order = Order::create($request->all());
// On publie le message brut ou via une classe Job/Event
// Ici version simple avec le package RabbitMQ
Queue::pushRaw(json_encode([
'event' => 'order.created',
'data' => $order->toArray()
]), 'orders.created');
return response()->json($order, 201);
}
Dans `apps/notifications` (Le Consommateur)
Ce service n'a pas besoin de controllers complexes. Il a besoin d'un Job qui traite le message.
- Créer un Job
ProcessOrderCreated. - Configurer le worker pour écouter la queue
orders.created.
// apps/notifications/app/Jobs/ProcessOrderCreated.php
public function handle()
{
// $this->job->getRawBody() contient le JSON
$payload = json_decode($this->job->getRawBody(), true);
Log::info("📧 Email envoyé pour la commande #" . $payload['data']['id']);
// Ici: Mail::to($payload['data']['email'])...
}
Commande de lancement : C'est la commande définie dans le
docker-compose.yml->php artisan rabbitmq:consume orders.created.
8. Le Makefile : Ton meilleur ami
Ne tape jamais docker-compose exec gateway php artisan.... Utilise un Makefile.
# Makefile
setup:
@echo "Installation des dépendances..."
@docker run --rm -v $(PWD)/apps/gateway:/app composer install
@docker run --rm -v $(PWD)/apps/users:/app composer install
@docker run --rm -v $(PWD)/apps/catalog:/app composer install
@docker run --rm -v $(PWD)/apps/orders:/app composer install
@docker run --rm -v $(PWD)/apps/notifications:/app composer install
@echo "Démarrage..."
@make up
up:
docker-compose up -d
down:
docker-compose down
logs:
docker-compose logs -f
test:
docker-compose exec gateway php artisan test
docker-compose exec users php artisan test
9. Pièges Classiques & Checklist de Prod
- Latence réseau : Chaque appel HTTP interne ajoute 20-50ms. N'enchaîne pas 10 appels (N+1 problem distribué). Utilise RabbitMQ pour tout ce qui n'est pas nécessaire à l'affichage immédiat.
- Transaction distribuée : Si
Orderest créé mais que le paiement plante dansPaymentService, tu as une commande fantôme. Solution : Saga Pattern (ou simplement, ne sépare pas Commandes et Paiements au début). - Logs Illisibles : Utilise un
Correlation-ID.- La Gateway génère un UUID
X-Correlation-ID. - Elle le passe dans les headers à
Users,Orders. - Chaque service loggue cet ID.
- Poussez les logs vers une stack ELK ou Loki.
- La Gateway génère un UUID
10. Bonus : Observabilité (OpenTelemetry)
Pour savoir où ça plante, installe OpenTelemetry. En PHP, ça s'installe via PECL ou des packages composer.
Ajoute à tes Services :
composer require open-telemetry/opentelemetry
Et configure tes traces pour être envoyées vers Jaeger (ajoute un container Jaeger à ton docker-compose).
Ce starter pack est minimaliste mais fonctionnel. Il respecte la séparation des concerns et introduit l'asynchronisme dès le début, indispensable pour scaler.
À toi de jouer : clone, make setup, et code tes features !