Microservices avec Claude Code : limites, API, Compose et tests
Microservices avec Claude Code : frontières, contrats API, Compose, tests, observabilité et rollout.
Les microservices consistent à découper une grande application en petits services indépendants qui collaborent via des API ou des événements. Claude Code devient utile quand il ne se limite pas à générer des dossiers, mais vérifie aussi les frontières de services, les contrats API, la propriété des données, Docker Compose, la passerelle, l’observabilité, les tests et le plan de déploiement.
Il faut rester pragmatique : les microservices n’améliorent pas automatiquement un produit. Ils ajoutent des appels réseau, de la compatibilité d’API, des transactions distribuées, de la corrélation de logs et des rollbacks plus complexes. Si le domaine change encore tous les jours, commencez par un monolithe modulaire. Pour les bases, voir développement API avec Claude Code, Docker Compose, logging et monitoring et architecture événementielle.
Gardez des références officielles comme points d’ancrage : Anthropic Claude Code overview, Docker Compose documentation et OpenAPI Specification 3.1. Elles aident à distinguer les suggestions de Claude Code du contrat réellement exploité.
Commencer Par Les Frontières
Le prompt initial doit demander une justification, pas seulement une liste de services.
Tu es architecte reviewer pour découper une application e-commerce en microservices.
Contexte:
- Le flux de commande change souvent.
- Le stock doit évoluer séparément à cause de l'intégration entrepôt.
- Paiement et notifications ne doivent pas bloquer le catalogue.
À produire:
1. Services candidats et responsabilités.
2. Données possédées par chaque service.
3. Interactions synchrones par API.
4. Interactions asynchrones par événement.
5. Ce qui doit rester dans le monolithe pour l'instant.
Contraintes:
- Pas de tables partagées.
- Pas de noms de tables internes dans les API.
- Pas de logique métier dans la gateway.
- Démarrage local avec Docker Compose.
Les documents Microsoft Microservices architecture guide, AKS microservices reference architecture et API Management gateway overview donnent un cadre officiel pour l’autonomie, le routage et l’exploitation.
Contrat API Et Données
Écrivez le contrat avant le code.
openapi: 3.1.0
info:
title: Order Service API
version: 1.0.0
paths:
/orders:
post:
summary: Create an order after reserving inventory
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [customerId, items]
properties:
customerId:
type: string
items:
type: array
minItems: 1
items:
type: object
required: [sku, quantity]
properties:
sku:
type: string
quantity:
type: integer
minimum: 1
responses:
"201":
description: Order accepted
"409":
description: Inventory could not be reserved
| Service | Possède | Peut appeler | Ne doit pas faire |
|---|---|---|---|
| gateway | aucune donnée métier | order, inventory | calculer stock ou remises |
| order-service | orders, order_items | inventory API, order-events | lire les tables inventory |
| inventory-service | stock, reservations | rien au départ | lire les tables orders |
| notification-service | delivery logs | order-events | changer l’état d’une commande |
Pour afficher commande et stock sur le même écran, évitez le JOIN entre bases de services. Préférez composition d’API, modèle de lecture, index de recherche ou cache alimenté par événements.
Pour les reviews, gardez un petit service-inventory.json dans le dépôt. Les humains décident les frontières; Claude Code vérifie si un changement les viole.
{
"services": [
{
"name": "gateway",
"owns": [],
"mayCall": ["order-service", "inventory-service"],
"mustNot": ["store business data", "calculate discounts"]
},
{
"name": "order-service",
"owns": ["orders", "order_items"],
"mayCall": ["inventory-service"],
"mustNot": ["read inventory tables directly"]
},
{
"name": "inventory-service",
"owns": ["stock", "reservations"],
"mayCall": [],
"mustNot": ["change order status"]
}
],
"releaseRules": [
"no shared database tables",
"public APIs hide internal table names",
"every service has healthcheck, logs, tests, and rollback notes"
]
}
Exemple Exécutable
mkdir microservices-demo
cd microservices-demo
mkdir services
npm init -y
npm pkg set type=module
npm install express zod pino redis undici
compose.yaml:
services:
gateway:
image: node:22-alpine
working_dir: /workspace
command: node services/service.mjs
environment:
SERVICE: gateway
PORT: 3000
ORDER_URL: http://order-service:3000
INVENTORY_URL: http://inventory-service:3000
ports:
- "8080:3000"
volumes:
- .:/workspace
depends_on:
- order-service
- inventory-service
order-service:
image: node:22-alpine
working_dir: /workspace
command: node services/service.mjs
environment:
SERVICE: order
PORT: 3000
INVENTORY_URL: http://inventory-service:3000
REDIS_URL: redis://redis:6379
volumes:
- .:/workspace
depends_on:
redis:
condition: service_healthy
inventory-service:
condition: service_started
inventory-service:
image: node:22-alpine
working_dir: /workspace
command: node services/service.mjs
environment:
SERVICE: inventory
PORT: 3000
volumes:
- .:/workspace
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
services/service.mjs:
import express from "express";
import pino from "pino";
import { createClient } from "redis";
import { request } from "undici";
import { z } from "zod";
import { randomUUID } from "node:crypto";
const service = process.env.SERVICE ?? "inventory";
const port = Number(process.env.PORT ?? 3000);
const log = pino({ name: service });
function base(name) {
const app = express();
app.use(express.json());
app.use((req, res, next) => {
req.requestId = req.header("x-request-id") ?? randomUUID();
res.setHeader("x-request-id", req.requestId);
next();
});
app.get("/health", (_req, res) => res.json({ ok: true, service: name }));
return app;
}
function inventory() {
const app = base("inventory");
const stock = new Map([["sku-1", 5], ["sku-2", 2]]);
const Reserve = z.object({ sku: z.string().min(1), quantity: z.number().int().positive() });
app.get("/inventory/:sku", (req, res) => res.json({ sku: req.params.sku, quantity: stock.get(req.params.sku) ?? 0 }));
app.post("/inventory/reservations", (req, res) => {
const parsed = Reserve.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
const available = stock.get(parsed.data.sku) ?? 0;
if (available < parsed.data.quantity) return res.status(409).json({ error: "insufficient_stock", available });
stock.set(parsed.data.sku, available - parsed.data.quantity);
log.info({ requestId: req.requestId, sku: parsed.data.sku }, "reserved");
res.status(201).json({ sku: parsed.data.sku, remaining: stock.get(parsed.data.sku) });
});
app.listen(port, () => log.info({ port }, "inventory started"));
}
async function order() {
const app = base("order");
const redis = createClient({ url: process.env.REDIS_URL ?? "redis://localhost:6379" });
await redis.connect();
const Order = z.object({
customerId: z.string().min(1),
items: z.array(z.object({ sku: z.string().min(1), quantity: z.number().int().positive() })).min(1),
});
app.post("/orders", async (req, res) => {
const parsed = Order.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
for (const item of parsed.data.items) {
const response = await request(`${process.env.INVENTORY_URL}/inventory/reservations`, {
method: "POST",
headers: { "content-type": "application/json", "x-request-id": req.requestId },
body: JSON.stringify(item),
});
if (response.statusCode >= 400) return res.status(response.statusCode).json(await response.body.json());
}
const order = { id: randomUUID(), ...parsed.data, status: "accepted" };
await redis.xAdd("order-events", "*", { type: "OrderAccepted", payload: JSON.stringify(order) });
res.status(201).json(order);
});
app.listen(port, () => log.info({ port }, "order started"));
}
function gateway() {
const app = base("gateway");
async function forward(req, res, url) {
const response = await request(url, {
method: req.method,
headers: { "content-type": "application/json", "x-request-id": req.requestId },
body: req.method === "GET" ? undefined : JSON.stringify(req.body),
});
res.status(response.statusCode).send(await response.body.text());
}
app.post("/orders", (req, res) => forward(req, res, `${process.env.ORDER_URL}/orders`));
app.get("/inventory/:sku", (req, res) => forward(req, res, `${process.env.INVENTORY_URL}/inventory/${encodeURIComponent(req.params.sku)}`));
app.listen(port, () => log.info({ port }, "gateway started"));
}
if (service === "inventory") inventory();
else if (service === "order") await order();
else if (service === "gateway") gateway();
docker compose up
curl http://localhost:8080/inventory/sku-1
curl -X POST http://localhost:8080/orders -H "content-type: application/json" -d '{"customerId":"cust-1","items":[{"sku":"sku-1","quantity":2}]}'
docker compose down
Critères Avant Production
Avant de publier, ne comptez pas seulement le nombre de services. Vérifiez si chaque service peut être modifié, testé, déployé et restauré sans bloquer tout le produit. Demandez à Claude Code de transformer un flux métier en tableau avec propriétaire des données, contrat API, test de contrat, métrique clé et rollback.
Observabilité, Tests Et Rollout
Le minimum viable comprend x-request-id, logs structurés, nom de service, IDs métier, /health, taux d’erreur et latence. Demandez à Claude Code de relire la compatibilité API, les migrations par service, l’absence de logique métier dans la gateway, les branches 400/409/500, la panne Redis, les Feature Flags, le canary et le rollback.
Les bons cas d’usage sont l’e-commerce avec commande, stock, paiement et notification, le SaaS B2B avec facturation, permissions et audit, et les plateformes média avec ingestion, transformation, recherche et diffusion. Les pièges fréquents sont le découpage par tables, la base partagée, une bibliothèque domaine commune trop grosse, la logique métier dans la gateway et un déploiement sans observabilité.
Le vrai use case est une capacité métier qui change, monte en charge ou échoue différemment du reste du produit. Le pitfall classique est de créer des services sans propriétaire clair. Demandez à Claude Code une fiche courte par service candidat: owner, contrat API, tables possédées, événements produits, événements consommés, dashboard, SLO, commande de déploiement, commande de rollback et comportement quand une dépendance tombe.
Ajoutez aussi l’impact business. Si le service touche le checkout, l’inscription d’essai, les emails, les publicités, les rapports ou les formulaires de consultation, la checklist doit vérifier ce chemin. Une séparation propre sur le papier mais qui casse l’attribution ou les notifications reste une mauvaise décision. Pour un premier PR, un inventaire de services et un test de contrat valent souvent mieux que dix dossiers générés.
Si vous voulez réutiliser des extraits CLAUDE.md, des checklists de review et des modèles de contrats API, commencez par les produits pratiques ClaudeCodeLab.
Pour faire relire les frontières, contrats API, Compose et monitoring d’un vrai projet, l’équipe peut commencer par Claude Code training and consultation sur un seul flux métier.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Permission receipt Claude Code : portée, preuves et rollback
Modèle de permission receipt pour Claude Code : actions autorisées, limites d'approbation, commandes de preuve, rollback et CTAs revenus.
Agent Harness securise pour Claude Code et Codex : permissions, verification et rollback
Construisez un Agent Harness pratique pour Claude Code et Codex avec politiques, plan, verification et recuperation.
Sous-agents Claude Code : guide pratique pour déléguer sans perdre le contrôle
Guide pratique des sous-agents Claude Code pour répartir articles et code : règles, prompts, pièges et checklist.