Systemarchitektur ohne Chaos: Entscheidungen, Team und belastbare Nachweise
Ein praktischer Architekturworkshop: Wann Architektur nötig ist, wer sie führt, wie Entscheidungen und Grenzen dokumentiert werden und wie du fremde Systeme ohne Rätselraten übernimmst. Mit Mindmap, Code und Entscheidungsspiel.
Der vollständige Artikel steht unten. Aktivieren Sie JavaScript für Quiz und Simulationen.
Architekturworkshop ohne Theater
Gute Architektur beginnt, wenn wir aufhören zu raten.
Nicht beim Diagramm, Frameworknamen oder Schlagwort „Microservices“. Zuerst kommt die einfache, schwierige Frage: Was wird sich häufig ändern, was muss stabil bleiben, und was darf beim zweiten Deployment nicht kaputtgehen?
Architektur ist gut, wenn das Unternehmen sein Produkt ohne Panik verändern kann. Wenn das Team die Verantwortlichkeiten kennt. Wenn Zahlung, Formular, Kundenbereich, E-Mail und Bericht nicht zu einem Klumpen verklebt sind, den vor dem Wochenende niemand anfassen möchte.
Sie setzt Grenzen, hält Entscheidungen fest, senkt Änderungskosten und erklärt dem Team das Verhalten im Fehlerfall.
Sie ersetzt weder Denken noch Tests, Verantwortung oder fachliche Gespräche. Dokumentation allein hält Produktion nicht am Laufen.
Wenn eine Korrektur viele Module berührt und niemand ihre Nebenwirkungen vorhersehen kann.
Eine Definition für die Praxis
Architektur umfasst Entscheidungen, die sich später schwer rückgängig machen lassen.
Ein Framework lässt sich wechseln, eine Buttonfarbe ändern, ein Endpoint umbenennen. Schwieriger sind Datenmodell, Verantwortungsgrenzen, Zahlungsintegration, Berechtigungspolitik, Deployment-Pipeline oder Beobachtbarkeitsregeln.
Ist eine Entscheidung teuer rückgängig zu machen, gehört sie in die Architekturarbeit. Ist sie es nicht, braucht es keine große Beratung. Diese einfache Regel schützt vor zwei Extremen: regellosem Chaos und wertloser Bürokratie.
Was benötigt wird
Fünf Dinge, ohne die Architektur nur eine Zeichnung bleibt.
Meist fehlt nicht Technologie, sondern Verantwortung. Das Team weiß, dass „eine Integration nötig ist“, aber niemand hält fest, wer Retries verantwortet, was bei Duplikaten passiert, wohin Fehler gelangen, wer das Ergebnis abnimmt und wann die Aufgabe abgeschlossen ist.
Architektur sollte deshalb kurz, lesbar und mit einem echten Ablauf verbunden sein. Bezahlt ein Kunde ein Audit, muss das System wissen, was die Zahlung bedeutet, wann die Messung startet, wie der Bericht gespeichert und die Nachricht versendet wird und was bei einem gescheiterten Schritt angezeigt wird.
Wann genügt leichte Ordnung, wann braucht es formale Architektur?
| Situation | Ausreichend | Nicht auslassen |
|---|---|---|
| Marketingwebsite | Komponentenstandard, SEO, Formulare, Analytics. | Inhaltsbackups, Formularsicherheit, Fehlermonitoring. |
| Kundenbereich | Klare Rollen, Sitzungen, Logs, Änderungsprotokoll. | Rechte, Datenaufbewahrung, Passwortreset, E2E-Tests. |
| Checkout und Zahlungen | Getrennter Bestell- und Abwicklungsprozess. | Webhook-Idempotenz, Signaturen, Rückerstattung, Zahlungsstatus. |
| ERP-/API-Integrationen | Verträge, Queues, Retries, Datenabbildung. | Duplikatbehandlung, DLQ, Datenkonsistenz, Versionierung. |
| Reguliertes System | ADRs, Security-Review, Zugriffskontrolle, Auditspur. | DSGVO, Auftragsverarbeitung, Aufbewahrung, Incident-Verfahren. |
Menschen und Verantwortung
Wer sollte Architektur einführen, und wie viele Personen gehören dazu?
Das schlechteste Modell ist eine Person, die „die Architektur erfindet“ und sie dem Team als unumstößliche Wahrheit übergibt. Ebenso schlecht: zwölf Personen mit Meinungen, aber niemand mit Verantwortung.
Für die meisten Projekte reicht eine kleine Runde: fachliche Zielverantwortung, technische Verantwortung für Backend und Daten, jemand für Frontend oder Nutzererlebnis, Betrieb und Sicherheit sowie bei Bedarf Domänenwissen aus Vertrieb, Buchhaltung, Logistik oder Support.
Mehr Menschen bedeuten nicht bessere Architektur. Oft bedeuten sie langsamere Entscheidungen und verteilte Verantwortung. Besser ist ein kleines Team, das entscheiden, begründen und Ergebnisse nachweisen kann.
Ein Kunde bezahlt ein Audit, doch die Website-Analyse dauert einige Minuten. Was tust du?
Ich starte das gesamte Audit im Stripe-Webhook.
Ich speichere die Zahlung und das Ereignis in einer Outbox.
Ich zeige „Bericht folgt“ ohne Queue.
Wähle eine Entscheidung und prüfe ihre Konsequenz.
Beispiel aus dem Produktivbetrieb
Eine kleine Funktion wird teuer, wenn ihre Grenzen falsch liegen.
Stellen wir uns vor: Ein Kunde gibt eine URL ein, bezahlt ein Audit, das System analysiert, speichert das Ergebnis, verschickt eine E-Mail und zeigt den Bericht im Panel. Auf einer Folie klingt das einfach. In Produktion folgen sofort Fragen: Was bei zwei Stripe-Webhooks, fehlender E-Mail, längerer Analyse, Neuladen nach der Zahlung oder einem fertigen Bericht, dessen Datenbankspeicherung scheitert?
Genau hier beginnt Architektur. Nicht bei der Bibliothekswahl, sondern beim Verhalten in der realen Welt.
Code als Warnsignal
Auch kurzer Code kann zu viel erledigen.
Dieses harmlose Beispiel vermischt Zahlung, Audit, Bericht, E-Mail und externen Anbieter. Nach einem Ausfall ist schwer festzustellen, was bereits passiert ist.
public async Task CompleteOrder(Guid orderId)
{
var order = await db.Orders.FindAsync(orderId);
order.Status = "Paid";
var audit = await crawler.Run(order.TargetUrl);
order.ReportJson = JsonSerializer.Serialize(audit);
await email.Send(order.Email, "Raport gotowy", audit.Summary);
await stripe.MarkProcessed(order.PaymentIntentId);
await db.SaveChangesAsync();
}
Eine bessere Grenze
Erst die Entscheidung, dann die Nebenwirkungen.
Eine Transaktion sollte Fakt und Absicht weiterer Arbeit speichern. Audit, E-Mail und Integrationen können getrennt laufen – mit Retries, Monitoring und verständlichem Kundenstatus.
public sealed record ConfirmAuditPayment(
Guid OrderId,
string PaymentIntentId,
DateTimeOffset ConfirmedAt);
public sealed class ConfirmAuditPaymentHandler
{
public async Task Handle(ConfirmAuditPayment command)
{
var order = await orders.Get(command.OrderId);
order.MarkAsPaid(command.PaymentIntentId, command.ConfirmedAt);
outbox.Add(new AuditRequested(order.Id, order.TargetUrl));
outbox.Add(new CustomerNotificationRequested(order.Id));
await unitOfWork.SaveChanges();
}
}
SOLID ohne Glaubenskrieg
Prinzipien sollen helfen, nicht fehlende Entscheidungen verdecken.
Single Responsibility sagt: Wenn eine E-Mail-Änderung die Zahlung beschädigt, sind Verantwortungen vermischt. Open/Closed sagt: Wenn jeder neue Anbieter einen halben Systemumbau verlangt, fehlt ein Vertrag. Dependency Inversion sagt: Hängt die Domäne von technischen Details ab, bestimmen diese bald das Produkt.
Praktisch reichen zunächst einfache Verträge, die keine falschen Versprechen machen:
public interface AuditReportStore
{
Task Save(AuditReport report, CancellationToken cancellationToken);
}
public interface AuditNotifier
{
Task NotifyReportReady(Guid orderId, CancellationToken cancellationToken);
}
public sealed class CompleteAuditHandler
{
public async Task Handle(AuditCompleted completed, CancellationToken ct)
{
await reports.Save(completed.Report, ct);
await notifier.NotifyReportReady(completed.OrderId, ct);
}
}
Mini-Spiel
Ordne den Ablauf einer Architekturentscheidung.
Ausfall- und Retry-Plan
Geschäftsziel
Test, Monitoring und Rollback
Datenmodell und maßgebliche Quelle
Prozessgrenzen
Integrationsvertrag
Ordne die Schritte. Beginne beim Ziel, nicht bei der Technologie.
Antworten löschen
Ein fremdes System übernehmen
Beginne nicht mit Refactoring. Verstehe zuerst, was nicht kaputtgehen darf.
Bestehenden Code zu übernehmen ist weder Schuldigensuche noch Jagd auf hässliche Klassen, sondern Detektivarbeit. Finde kritische Abläufe, Quelldaten, Integrationen, Entscheidungsverantwortliche und Stellen, an denen ein fehlender Test Kunden Geld kosten kann.
Ein Charakterisierungstest erklärt alten Code nicht für schön. Er sagt: „So arbeitet das System heute, und das dürfen wir nicht versehentlich ändern.“
[Fact]
public async Task Paid_order_requests_audit_only_once_for_duplicate_webhook()
{
var paymentIntentId = "pi_123";
await app.HandleStripeWebhook(paymentIntentId);
await app.HandleStripeWebhook(paymentIntentId);
var order = await app.FindOrderByPayment(paymentIntentId);
Assert.Equal(OrderStatus.Paid, order.Status);
Assert.Single(await app.Outbox.EventsOfType<AuditRequested>(order.Id));
}
Du übernimmst ein Modul ohne Tests. Was tust du am ersten Tag?
Ich refaktoriere sofort die hässlichste Klasse.
Ich schreibe einen Charakterisierungstest für den kritischen Ablauf.
Ich lese die README und nehme an, sie sei aktuell.
Wähle eine Antwort und erfahre, was sie für die Wartung bedeutet.
ADR in der Praxis
Eine Entscheidung braucht Grund, Konsequenzen und Nachweis.
Wir dokumentieren nicht um der Dokumentation willen, sondern damit wir in drei Monaten nicht raten müssen, warum das System so arbeitet.
ADR-004: Das Audit startet nach der Zahlung asynchron
Kontext:
Ein Website-Audit kann einige zehn Sekunden bis mehrere Minuten dauern.
Der Checkout muss schnell abgeschlossen sein.
Entscheidung:
Nach dem Stripe-Webhook speichern wir die Bestellung als bezahlt
und schreiben das Ereignis AuditRequested in die Outbox.
Folgen:
Das Panel zeigt den Status "in der Warteschlange".
Der Worker kann das Audit nach einem Fehler wiederholen.
Wir brauchen Monitoring für die Queue und gescheiterte Jobs.
Nachweis:
Test eines doppelten Webhooks, Test eines Worker-Neustarts,
Korrelationslog mit orderId.
Entscheidungs-Preflight starten
Bereit zur Prüfung. Klicke und prüfe, ob die Entscheidung vollständig nachvollziehbar ist.
Kontrollfragen
Kannst du sie nicht beantworten, ist die Architektur noch nicht fertig.
Erkenntnis
Gute Architektur zeigt sich besonders, wenn etwas schiefgeht.
Kann ein System seinen Status klar anzeigen, Vorgänge sicher wiederholen, Daten erhalten, Alarme senden, Änderungen zurücknehmen und Menschen an der richtigen Stelle entscheiden lassen, beginnt Architektur sich auszuzahlen.
Weil sie Chaoskosten reduziert und dem Team angstfreie Änderungen ermöglicht.
Beginne mit einer Entscheidung. Gute Architektur muss sich in einem echten Szenario begründen lassen, nicht nur in einem Diagramm.
Workshop zurücksetzen