Advanced (Aktualisiert: 3.6.2026)

Microservices mit Claude Code: Grenzen, APIs, Compose und Tests

Microservices mit Claude Code: Grenzen, API-Verträge, Compose, Tests, Observability und Rollout.

Microservices mit Claude Code: Grenzen, APIs, Compose und Tests

Microservices sind ein Architekturansatz, bei dem eine große Anwendung in kleine unabhängige Services aufgeteilt wird, die über APIs oder Events zusammenarbeiten. Claude Code hilft dabei nur dann wirklich, wenn es nicht bloß Ordner erzeugt, sondern Servicegrenzen, API-Verträge, Datenhoheit, lokale Compose-Umgebung, Gateway, Observability, Tests und Rollout gemeinsam prüft.

Der wichtigste Punkt: Microservices sind kein automatischer Performancegewinn. Nach der Aufteilung entstehen neue Probleme wie Netzwerkfehler, API-Kompatibilität, verteilte Transaktionen, Log-Korrelation und Rollback-Reihenfolgen. Wenn das Produkt noch täglich seine Domäne ändert, ist ein modularer Monolith oft die bessere erste Stufe. Für Grundlagen passen auch API-Entwicklung mit Claude Code, Docker Compose, Logging und Monitoring und Event-driven Architecture.

Nutze offizielle Referenzen als Review-Anker: Anthropic Claude Code overview, Docker Compose documentation und OpenAPI Specification 3.1. So bleibt klar, was Claude Code vorschlägt und was dein Team wirklich betreiben will.

Erst Grenzen, Dann Code

Ein guter Prompt zwingt Claude Code, über fachliche Fähigkeiten statt über Tabellen nachzudenken.

Du bist Architektur-Reviewer für die Aufteilung einer E-Commerce-Anwendung in Microservices.

Kontext:
- Der Bestellprozess ändert sich häufig.
- Lagerbestand muss wegen Warehouse-Integration separat deployt werden.
- Payment und Notifications dürfen den Katalog nicht blockieren.

Liefere:
1. Servicekandidaten und Verantwortlichkeiten.
2. Daten, die jeder Service besitzt.
3. Synchrone APIs und asynchrone Events.
4. Funktionen, die vorerst im Monolith bleiben sollten.
5. Minimaler erster Sprint mit Docker Compose.

Regeln:
- Keine gemeinsam genutzten Datenbanktabellen.
- Keine internen Tabellennamen in APIs.
- Keine Geschäftslogik im Gateway.

Als Referenz eignen sich Microsofts Microservices architecture guide, die AKS microservices reference architecture und der Überblick zu API Management gateways.

Vertrag Und Datenhoheit

Der API-Vertrag kommt vor dem Handler-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
ServiceBesitztDarf aufrufenDarf nicht
gatewaykeine Fachdatenorder, inventoryBestand oder Rabatte berechnen
order-serviceorders, order_itemsinventory API, order-eventsinventory-Tabellen lesen
inventory-servicestock, reservationszunächst nichtsorders-Tabellen lesen
notification-servicedelivery logsorder-eventsBestellstatus ändern

Wenn eine UI Bestell- und Bestandsdaten zusammen braucht, ist ein Cross-Database-Join trotzdem die falsche Abkürzung. Nutze API-Komposition, Read Models, Suchindex oder Event-getriebene Caches.

Für Reviews lohnt sich ein kleines service-inventory.json im Repository. Menschen entscheiden die Grenze; Claude Code prüft, ob eine Änderung gegen diese Grenze verstößt.

{
  "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"
  ]
}

Minimal Laufendes Beispiel

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

Kriterien Vor Dem Go Live

Zaehle vor dem Start nicht die Anzahl der Services, sondern die nachweisbaren Verantwortlichkeiten. Jeder Service braucht Datenhoheit, einen stabilen API-Vertrag, Contract-Tests, Healthcheck, Logs und einen Rollback-Pfad. Lass Claude Code einen konkreten Business-Flow in diese Pruefliste zerlegen.

Observability, Tests Und Rollout

Mindestens erforderlich sind x-request-id, strukturierte Logs, Servicename, fachliche IDs, /health, Fehlerrate und Latenz. Ergänze einen kleinen Contract-Test für die Order-Anfrage, bevor du Integrationstests ausbaust. Claude Code sollte vor dem Rollout API-Kompatibilität, Migration Ownership, Gateway-Logik, 400/409/500-Pfade, Redis-Ausfall, Feature Flags, Canary und Rollback prüfen.

Gute Anwendungsfälle sind E-Commerce mit Order, Inventory, Payment und Notification, B2B-SaaS mit Billing, Berechtigungen und Audit Logs sowie Content-Plattformen mit Ingestion, Transformation, Suche und Auslieferung. Typische Fallen sind Servicegrenzen nach Tabellen, Shared Database, zu große Shared Libraries, Geschäftslogik im Gateway und ein Release ohne Observability.

Ein guter use case ist eine fachliche Fähigkeit, die sich anders ändert, skaliert oder ausfällt als der Rest des Produkts. Der typische pitfall ist dagegen, viele Service-Ordner zu erzeugen, ohne Ownership, Datenhoheit und Rollback zu klären. Lass Claude Code pro Kandidat eine kurze Service-Karte schreiben: Owner, API-Vertrag, besessene Tabellen, erzeugte Events, konsumierte Events, Dashboard, SLO, Deployment-Befehl, Rollback-Befehl und Verhalten bei Ausfall einer Abhängigkeit.

Wenn du wiederverwendbare CLAUDE.md-Bausteine, Review-Checklisten und API-Vertragsvorlagen brauchst, kannst du mit den ClaudeCodeLab Praxis-Templates starten.

Für eine konkrete Review von Servicegrenzen, API-Verträgen, Compose-Setup und Monitoring kann dein Team über Claude Code training and consultation mit einem einzelnen Workflow starten.

#Claude Code #Microservices #API Design #Docker Compose #Observability #Testing
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.