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.

Was Architektur leistet

Sie setzt Grenzen, hält Entscheidungen fest, senkt Änderungskosten und erklärt dem Team das Verhalten im Fehlerfall.

Was Architektur nicht leistet

Sie ersetzt weder Denken noch Tests, Verantwortung oder fachliche Gespräche. Dokumentation allein hält Produktion nicht am Laufen.

Wann sie dringend wird

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.

Mindmap Gute Architekturentscheidung
ProblemWas belastet das Unternehmen wirklich?
DatenWas ist die maßgebliche Datenquelle?
GrenzeWo endet die Verantwortung des Moduls?
AusfallWas tun wir, wenn der Anbieter schweigt?
NachweisWie prüfen wir die Funktion?
RückkehrWie gelangen wir wieder zu einem stabilen Zustand?

Was benötigt wird

Fünf Dinge, ohne die Architektur nur eine Zeichnung bleibt.

Geschäftsziel Prozessgrenzen Entscheidungsverantwortung Funktionsnachweis dokumentierter Entscheidungsgrund

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.

3–4 Personenkleine Anwendung, eine Domäne, schnelle Entscheidungen
5–6 PersonenKundenbereich, Zahlungen, Integrationen, mehrere Rollen
6–8 Personensensible Daten, viele Systeme, Audit und formale Abnahme

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.

Rätsel 01

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.

0/6

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.

Anwendung lokal starten Anmeldung, Zahlungen, Formulare und Integrationen finden Datenkarte zeichnen Charakterisierungstest ergänzen Abnahmekriterien vor der Änderung festlegen

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));
}
Rätsel 02

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.

01Welches Geschäftsereignis startet den Prozess?
02Welche Daten sind die maßgebliche Quelle?
03Wer darf eine kostspielige Entscheidung freigeben?
04Was geschieht bei einer doppelten Anfrage?
05Wie sieht der Kunde den Arbeitsstatus?
06Welcher Test schließt die Abnahme ab?

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.

Entscheidungsergebnis dieses Workshops 0/2

Beginne mit einer Entscheidung. Gute Architektur muss sich in einem echten Szenario begründen lassen, nicht nur in einem Diagramm.

Workshop zurücksetzen