
Die Microservices-Architektur strukturiert eine Anwendung als eine Sammlung lose gekoppelter, unabhängig voneinander bereitstellbarer Dienste. Jeder Dienst verfügt über eine bestimmte Geschäftsfunktion, verfügt über einen eigenen Datenspeicher und kommuniziert über ein Netzwerk. Obwohl Microservices eine erhebliche betriebliche Komplexität mit sich bringen, ermöglichen sie Unternehmen die Skalierung von Entwicklungsteams, die unabhängige Bereitstellung und den Aufbau widerstandsfähigerer Systeme.
Die Microservices-Architektur strukturiert eine Anwendung als eine Sammlung lose gekoppelter, unabhängig voneinander bereitstellbarer Dienste. Jeder Dienst verfügt über eine bestimmte Geschäftsfunktion, verfügt über einen eigenen Datenspeicher und kommuniziert über ein Netzwerk. Obwohl Microservices eine erhebliche betriebliche Komplexität mit sich bringen, ermöglichen sie Unternehmen die Skalierung von Entwicklungsteams, die unabhängige Bereitstellung und den Aufbau widerstandsfähigerer Systeme.
Eine monolithische Anwendung verfügt über alle Funktionen in einer einzigen Codebasis und Bereitstellungseinheit:
┌─────────────────────────────────────────────┐
│ Monolith Application │
├──────────────┬──────────────┬───────────────┤
│ User Module │ Order Module│ Payment │
│ (controllers, services, repos, DB tables) │
└──────────────┴──────────────┴───────────────┘
Monolith-Vorteile:
Nachteile des Monolithen:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ User Service │ │ Order Service │ │ Payment Service │
│ (own DB, API) │ │ (own DB, API) │ │ (own DB, API) │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌───────────▼───────────┐
│ API Gateway │
│ (auth, routing) │
└───────────────────────┘
Jeder Dienst besitzt genau eine Geschäftsfähigkeit. Wenn Sie den Zweck eines Dienstes beschreiben können, ohne „und“ zu verwenden, ist er wahrscheinlich gut abgegrenzt.
Gut:
Schlecht:
Jeder Dienst besitzt seine Datenbank. Dienste teilen niemals Datenbanken – sie kommunizieren über APIs.
-- Service A has its own database
CREATE TABLE users (
id UUID PRIMARY KEY,
email VARCHAR(255) UNIQUE,
name VARCHAR(255)
);
-- Service B has its own database, stores only the user ID
CREATE TABLE orders (
id UUID PRIMARY KEY,
user_id UUID NOT NULL,
total DECIMAL(10,2),
status VARCHAR(50)
);
Warum nicht Datenbanken teilen?
Ein Ausfall in einem Dienst sollte nicht auf andere übergreifen.
Leistungsschaltermuster:
// Using a circuit breaker to handle service failures gracefully
const breaker = new CircuitBreaker({
failureThreshold: 5,
resetTimeout: 30000, // 30 seconds
});
async function getPaymentStatus(orderId: string) {
try {
return await breaker.call(() => paymentService.getStatus(orderId));
} catch (error) {
// Fallback: return cached status or default
return { status: 'pending', note: 'Payment service unavailable' };
}
}
Schottmuster: Weisen Sie verschiedenen Diensten separate Thread-Pools zu, damit ein langsamer Dienst nicht alle Ressourcen erschöpft:
// Separate thread pools per service
const paymentPool = new ThreadPool({ maxThreads: 10 });
const inventoryPool = new ThreadPool({ maxThreads: 20 });
const notificationPool = new ThreadPool({ maxThreads: 5 });
Jeder Dienst kann unabhängig bereitgestellt werden. Dies ist der Hauptvorteil von Microservices.
Bereitstellungsautomatisierung:
# GitHub Actions for a single microservice
name: Deploy Payment Service
on:
push:
paths:
- 'services/payment/**'
- '.github/workflows/payment.yml'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t payment-service .
- run: docker push registry.example.com/payment-service:${{ github.sha }}
- run: kubectl set image deployment/payment-service payment-service=registry.example.com/payment-service:${{ github.sha }}
Vorteile: Einfach zu implementieren und zu überdenken. Die Semantik von Anfragen und Antworten ist bekannt.
Nachteile: Erstellt Laufzeitkopplung – wenn der Downstream-Dienst ausfällt, ist der Aufrufer betroffen.
// REST call using fetch
const order = await fetch(`http://order-service:8080/api/orders/${orderId}`);
// gRPC definition
service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
}
message GetOrderRequest {
string order_id = 1;
}
Vorteile: Lose Kopplung, bessere Ausfallsicherheit, unterstützt ereignisgesteuerte Architekturen.
Nachteile: Eventualkonsistenz, schwieriger zu debuggen, erfordert Nachrichteninfrastruktur.
// Kafka producer
await producer.send({
topic: 'order.created',
messages: [{ value: JSON.stringify({ orderId, userId, total }) }]
});
// Kafka consumer in notification service
await consumer.run({
eachMessage: async ({ message }) => {
const { orderId, userId } = JSON.parse(message.value.toString());
await emailService.sendOrderConfirmation(userId, orderId);
}
});
| Kriterien | Synchron | Asynchron |
|---|---|---|
| Benötigt der Anrufer eine sofortige Antwort? | Ja | NEIN |
| Kann die Operation verschoben werden? | NEIN | Ja |
| Ist der Downstream-Dienst immer verfügbar? | Ja | Nicht erforderlich |
| Benötigen Sie eine starke Konsistenz? | Ja | Letztendlich ist es in Ordnung |
// Strangler Fig pattern
const gateway = express();
// Route to new service if available, fall back to monolith
gateway.use('/api/users', async (req, res, next) => {
try {
const response = await fetch('http://user-service/api/users' + req.path);
return res.json(await response.json());
} catch {
next(); // Fall through to monolith handler
}
});
| Servicegröße | Teamgröße | Bereitstellungshäufigkeit | Beispiel |
|---|---|---|---|
| ⚡ Klein | 1-3 Entwickler | Mehrmals/Tag | Benutzerüberprüfung |
| 📐 Mittel | 3-6 Entwickler | Täglich/wöchentlich | Auftragsabwicklung |
| 🏗️ Groß | 6-10 Entwickler | Wöchentlich | Zahlungsplattform |
Faustregel: Ein Service sollte klein genug sein, dass ein einzelnes Team ihn vollständig besitzen kann, und groß genug, um einen sinnvollen Geschäftswert zu bieten.
Microservices erzeugen riesige Mengen an Telemetrie. Drei Säulen:
1. Protokollierung:
// Structured logging (JSON)
{
"timestamp": "2026-05-24T10:30:00.123Z",
"level": "error",
"service": "payment-service",
"trace_id": "abc123",
"message": "Payment processing failed",
"error": "stripe: insufficient funds",
"metadata": { "order_id": "ord_456", "amount": 2999 }
}
2. Metriken (Prometheus-Format):
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST",path="/api/payments",status="500"} 42
3. Verteiltes Tracing (OpenTelemetry):
# OpenTelemetry auto-instrumentation
OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4318"
OTEL_SERVICE_NAME: "payment-service"
OTEL_TRACES_SAMPLER: "parentbased_traceidratio"
OTEL_TRACES_SAMPLER_ARG: "0.1"
In dynamischen Umgebungen (Kubernetes) ändern sich IP-Adressen. Dienste finden sich über DNS:
Ein einziger Einstiegspunkt für alle Kundenanfragen:
| Funktion | Beispiel |
|---|---|
| Routenführung | /api/users → Benutzerdienst, /api/orders → Bestellservice |
| Authentifizierung | Validieren Sie JWT-Tokens vor dem Routing |
| Ratenbegrenzung | 1000 Anforderungen/Minute pro Kunde |
| Fordern Sie eine Transformation an | Konvertieren Sie JSON in Protobuf |
| Antwort-Caching | GET-Antworten zwischenspeichern |
| Stromkreisunterbrechung | Stoppen Sie die Weiterleitung an fehlerhafte Dienste |
Alle Konfigurationen externalisieren:
# ConfigMap in Kubernetes
apiVersion: v1
kind: ConfigMap
metadata:
name: payment-service-config
data:
DATABASE_URL: "postgresql://..."
STRIPE_API_KEY: "${STRIPE_API_KEY}" # from Secret
MAX_RETRIES: "3"
FEATURE_FLAG_NEW_CHECKOUT: "true"
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
spec:
containers:
- name: payment-service
image: registry.example.com/payment-service:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
Conways Gesetz: Organisationen entwerfen Systeme, die ihre Kommunikationsstruktur widerspiegeln. Wenn Ihr Team nicht entlang der Servicegrenzen organisiert ist, werden Microservices zu Reibungsverlusten führen.
| Kategorie | Optionen | Notizen |
|---|---|---|
| Rahmen | Spring Boot, NestJS, FastAPI, Go-Kit | Wählen Sie basierend auf der Teamkompetenz |
| API | REST, gRPC, GraphQL | gRPC für intern, REST für extern |
| Nachrichten | Kafka, RabbitMQ, NATS | Kafka für hohen Durchsatz |
| Datenbank | PostgreSQL, DynamoDB, MongoDB | Passen Sie die Datenbank an die Serviceanforderungen an |
| Container | Docker | Universal |
| Orchestrierung | Kubernetes | EKS, AKS, GKE oder selbstverwaltet |
| API-Gateway | Kong, Envoy, AWS API Gateway | Gesandter für Service-Mesh-Integration |
| Beobachtbarkeit | OpenTelemetry, Prometheus, Jaeger | OpenTelemetry ist der Standard |
| CI/CD | GitHub-Aktionen, GitLab CI, ArgoCD | ArgoCD für GitOps |
| Service-Mesh | Istio, Linkerd, Cilium | Der Einfachheit halber verlinkt |
| Geheimes Management | HashiCorp Vault, AWS Secrets Manager | Tresor für Multi-Cloud |
Microservices sind ein leistungsstarkes Architekturmuster, das unabhängige Bereitstellung, Teamautonomie und skalierbare Systeme ermöglicht. Allerdings sind sie mit einer erheblichen betrieblichen Komplexität verbunden. Die erfolgreichsten Microservices-Einführungen:
Microservices sind nicht die Standardarchitektur – sie sind eine Lösung für spezifische Skalierungs- und Organisationsprobleme. Wählen Sie sie bewusst und nicht standardmäßig aus.
Noch keine freigegebenen Kommentare sichtbar. Neue Antworten können moderiert werden.