Be Software Architecture Wise
Be Wise Apps

Read in English · Leer en español · Lire en français

Abschnitt 0.8 von 0.8 · Einsteiger · 20 Min. Lesezeit

Anforderungen, Versionskontrolle, Tests, Auslieferung und der Weg einer Anfrage

Videolektion

5 Min. · auf Englisch gesprochen, mit Untertiteln
Nur anhören (Audio)
Transkript lesen

Software zu entwickeln ist Teamsport mit einer sorgfältigen Routine. Bedürfnisse werden aufgeschrieben, jede Änderung wird festgehalten und geprüft, und automatische Tests wirken als Sicherheitsnetz. Am Ende dieser Lektion verfolgen wir ein einziges Tippen auf Bestellen durch jeden Teil eines Essensliefersystems und führen so das ganze Kapitel zusammen.

Software beginnt mit einem Bedürfnis. Manche Anforderungen sagen, was ein System tun muss, etwa: Ein Kunde kann per Karte bezahlen. Andere sagen, wie gut, etwa: Der Bezahlvorgang ist innerhalb von zwei Sekunden abgeschlossen. Viele Teams schreiben Bedürfnisse als User Stories auf. Zum Beispiel: Als hungriger Kunde möchte ich ein Gericht nachbestellen, das ich schon einmal hatte, damit ich schneller essen kann. Akzeptanzkriterien legen dann genau fest, wann die Story fertig ist.

Viele Menschen ändern täglich denselben Code, deshalb nutzen Teams Versionskontrolle, fast immer ein Werkzeug namens Git. Es zeichnet jede Änderung auf, wer sie vorgenommen hat und warum. Jede gespeicherte Änderung ist ein Commit, hier auf der Hauptlinie. Um an etwas Neuem zu arbeiten, legt eine Entwicklerin einen Branch an: einen eigenen Arbeitsstrang. Hier fügen zwei Commits einen Trinkgeld-Button und seine Tests hinzu, ohne andere zu stören. Ist die Arbeit fertig, öffnet die Entwicklerin einen Pull Request. Automatische Prüfungen laufen, und jemand aus dem Team prüft die Änderung. Reviews finden Fehler, verbreiten aber auch Wissen, sodass mindestens zwei Personen jede Änderung verstehen. Nach der Freigabe wird der Branch zurück in main gemergt.

Jede Änderung riskiert, etwas kaputt zu machen, das vorher funktioniert hat. Automatisierte Tests sind Programme, die andere Programme prüfen, und es gibt sie in Schichten. An der Basis stehen Unit-Tests. Jeder prüft ein kleines Stück, etwa eine Funktion, und läuft in Millisekunden, sodass ein Team viele davon haben kann. In der Mitte prüfen Integrationstests, ob Teile zusammenarbeiten, etwa der Bestellcode mit einer echten Datenbank. An der Spitze stehen wenige End-to-End-Tests, die das ganze System so steuern, wie es ein Nutzer täte. Sie sind realistisch, aber langsam und manchmal flaky. Ziel ist es, die meisten Probleme mit schnellen, fokussierten Tests abzufangen.

Die Automatisierung, die eine Änderung zu den Kunden bringt, heißt Pipeline. Bei Continuous Integration wird jede Änderung oft gemergt und automatisch gebaut und getestet. Ein Fehlschlag geht direkt an den Entwickler zurück, binnen Minuten. Als Nächstes kommt Staging: eine Kopie des Systems, so nah an der Produktion wie möglich, auf der das Release geprobt wird. Die Produktion ist das Live-System, auf das echte Kunden angewiesen sind. Bei Continuous Delivery gibt ein Mensch jedes Release per Knopfdruck frei. Bei Continuous Deployment geht jede erfolgreiche Änderung automatisch live.

Teams entscheiden auch, wie sie die Arbeit organisieren. Bei einem sequenziellen Vorgehen, oft Wasserfall genannt, endet jede Phase, bevor die nächste beginnt. Das passt zu festen Anforderungen, aber die Nutzer sehen bis spät nichts. Scrum, das am weitesten verbreitete agile Framework, arbeitet in kurzen Zyklen namens Sprints von höchstens einem Monat, die jeweils mit einem Review enden. Kanban hat keine festen Zyklen. Die Arbeit fließt über ein sichtbares Board, und das Team begrenzt, wie viel gleichzeitig in Arbeit ist. Und DevOps ist eine Kultur, die Entwicklung und Betrieb von Software zusammenbringt: „you build it, you run it“.

Jetzt das ganze Kapitel in einem einzigen Moment. Ein Kunde tippt in einer Essensliefer-App auf Bestellen. Die App macht aus dem Warenkorb JSON. Zuerst übersetzt DNS den Namen des Unternehmens in eine IP-Adresse. Die App sendet eine HTTPS-Anfrage. Ein Load Balancer in der Cloud-Region des Unternehmens reicht sie an eine von mehreren Kopien des Bestellservice weiter, die in Containern laufen. Der Bestellservice ruft die API des Zahlungsanbieters auf und wartet. Der Kunde darf „bestätigt“ erst sehen, wenn die Zahlung genehmigt ist, deshalb ist dieser Aufruf synchron. Dann werden in einer Transaktion die Bestellung und ihre Artikel in der Datenbank gespeichert. Entweder wird alles gespeichert oder nichts. Zum Schluss kommt eine Nachricht „Bestellung aufgegeben“ in eine Queue, und die App zeigt „bestätigt“. Der Benachrichtigungsservice holt sie ab, sobald er bereit ist, und eine Push-Benachrichtigung erreicht das Telefon. Ist dieser Service kurz ausgefallen, geht sichtbar nichts kaputt.

Fragen Sie bei jedem Schritt „Was wäre, wenn?“, und die Architekturfragen zeigen sich. Was, wenn beim Zahlungsaufruf ein Timeout eintritt? Wurde die Karte belastet? Ein eindeutiger Schlüssel, der mit der Bestellung gesendet wird, erlaubt dem Service, gefahrlos erneut zu fragen, ohne doppelt zu belasten. Was, wenn das Speichern der Bestellung fehlschlägt, nachdem die Zahlung geklappt hat? Zahlung und Datenbank sind getrennte Systeme, daher braucht der Entwurf einen Weg zur Erstattung oder Wiederholung. Und was, wenn sich der Verkehr zur Abendessenszeit verdreifacht? Es kommen mehr Kopien des Bestellservice hinzu, und die Datenbank wird zum wahrscheinlichen Engpass. Keine dieser Fragen betrifft das Schreiben einer Funktion. Es geht darum, wie die Teile angeordnet sind.

Jemand musste all das entscheiden: was synchron ist, wo die Daten liegen, was wo läuft. Zu entscheiden, wie die Teile angeordnet sind, ist Softwarearchitektur. Damit ist Kapitel null abgeschlossen. Machen Sie den optionalen Kapiteltest und gehen Sie dann weiter zu Kapitel eins: Was Softwarearchitektur ist.

Was Sie wissen müssen

Von der Idee zur Anforderung

Software beginnt mit einem Bedürfnis: Ein Restaurant möchte Bestellungen online annehmen, ein Krankenhaus möchte, dass Patienten ihre Termine selbst buchen. Dieses Bedürfnis in etwas zu übersetzen, das ein Team bauen kann, ist die Arbeit mit Anforderungen. Manche Anforderungen beschreiben, was das System tun muss („ein Kunde kann per Karte bezahlen“); andere beschreiben, wie gut es das tun muss („der Bezahlvorgang ist für 95 % der Kunden innerhalb von zwei Sekunden abgeschlossen“). Kapitel 2 zeigt, dass die zweite Art, die Qualitätsmerkmale, die meisten Architekturentscheidungen treibt.

Viele Teams halten funktionale Bedürfnisse als User Stories fest: kurze Beschreibungen aus Sicht der Nutzer. Kent Beck führte sie Ende der 1990er-Jahre im Extreme Programming ein, und ein Team beim Londoner Unternehmen Connextra entwarf 2001 die Vorlage, die heute die meisten verwenden:

As a hungry customer,
I want to reorder a meal I have ordered before,
so that I can eat sooner without searching the menu again.

Acceptance criteria
- My last 10 orders appear on the home screen.
- Tapping "Reorder" fills the basket with the same items.
- Items that are no longer available are flagged, not silently dropped.
Eine User Story mit Akzeptanzkriterien. Die Kriterien sind das, was Tester und automatisierte Tests prüfen werden.

Eine Story ist bewusst klein und unvollständig. Ron Jeffries beschrieb sie als Karte, Gespräch und Bestätigung (Card, Conversation, Confirmation): Die Karte ist eine Erinnerung, die Details werden im Gespräch zwischen denen geklärt, die das Feature wollen, und denen, die es bauen, und die Akzeptanzkriterien bestätigen, wann es fertig ist. Fachleute sind uneins darüber, wie viel vorab aufgeschrieben werden sollte. Teams in regulierten Branchen wie Medizinprodukten und Luftfahrt müssen formale, nachverfolgbare Anforderungen führen; Produktteams bevorzugen oft schlankere Stories, die kurz vor Arbeitsbeginn verfeinert werden. Beides ist in seinem Kontext legitim.

Versionskontrolle mit Git

Quellcode ist das wertvollste gemeinsame Dokument des Teams, und viele Menschen ändern ihn täglich. Versionskontrolle zeichnet jede Änderung auf, wer sie vorgenommen hat und warum, lässt Menschen parallel arbeiten, ohne sich gegenseitig zu überschreiben, und erlaubt es, jeden früheren Stand wiederherzustellen. Fast jedes Team nutzt heute Git, das Linus Torvalds 2005 für die Entwicklung des Linux-Kernels geschaffen hat, meist zusammen mit einem Hosting-Dienst wie GitHub, GitLab oder Bitbucket.

Wenige Begriffe decken den größten Teil des Alltags ab. Ein Commit ist ein gespeicherter Schnappschuss einer Reihe von Änderungen mit einer erklärenden Nachricht („Add tip button to checkout“). Ein Branch ist ein eigener Arbeitsstrang: Eine Entwicklerin legt einen Branch von main an, macht dort Commits, ohne jemanden zu stören, und mergt den Branch nach Abschluss zurück, wobei die beiden Historien zusammengeführt werden. Haben zwei Personen dieselben Zeilen geändert, meldet Git einen Merge-Konflikt, den ein Mensch auflösen muss.

Auf Hosting-Diensten läuft das Mergen meist über einen Pull Request (bei GitLab Merge Request genannt): eine Webseite mit der vorgeschlagenen Änderung, auf der automatische Prüfungen laufen und Kolleginnen und Kollegen ein Code-Review durchführen, also die Änderung lesen, Fragen stellen und Verbesserungen vorschlagen, bevor sie sie freigeben. Reviews finden Fehler, aber ihr größerer Nutzen ist, Wissen zu verbreiten (mindestens zwei Personen verstehen jede Änderung) und das Design konsistent zu halten. Architekten beteiligen sich oft an Reviews von Änderungen, die Struktur oder Schnittstellen betreffen.

Ein Feature-Branch wird von main angelegt, erhält zwei Commits, wird in einem Pull Request geprüft und zurück in main gemergtmainCommitCommitFix eines KollegenMerge-Commitfeature/tip-buttonButton neuTests neuPull RequestReview + CIDer Branch hält laufende Arbeit von main fern, bis sie geprüft und getestet ist.
Branch, Commit, Review, Merge: der alltägliche Zyklus der Versionskontrolle.
git switch -c feature/tip-button     # create a branch and move onto it
# ... edit files ...
git add checkout.py test_checkout.py
git commit -m "Add tip button to checkout"
git push -u origin feature/tip-button  # upload; then open a pull request
# after review and green checks, the branch is merged into main
Der alltägliche Git-Zyklus auf der Kommandozeile.

Beim Branching gehen Teams unterschiedlich vor. Manche führen langlebige Branches pro Feature oder Release; andere praktizieren Trunk-Based Development, mergen kleine Änderungen mindestens täglich in main und verbergen unfertige Features hinter Schaltern, sogenannten Feature-Flags. Die Forschung des DORA-Programms bringt kurzlebige Branches mit besserer Delivery-Performance in Verbindung, aber die richtige Wahl hängt auch davon ab, wie ein Team Releases ausliefert.

Testen: die Pyramide

Jede Änderung riskiert, etwas kaputt zu machen, das vorher funktioniert hat; das nennt man eine Regression. Automatisierte Tests sind Programme, die andere Programme prüfen, daher lassen sie sich bei jeder Änderung in Minuten ausführen statt in Tagen durch Menschen. Es gibt sie in Schichten. Ein Unit-Test prüft ein kleines Stück, etwa eine Funktion, isoliert und läuft in Millisekunden. Ein Integrationstest prüft, ob Teile zusammenarbeiten: der Bestellcode mit einer echten Datenbank oder der Shop mit der Testumgebung eines Zahlungsanbieters. Ein End-to-End-Test steuert das ganze System so, wie es ein Nutzer täte, zum Beispiel indem er sich in der App von der Speisekarte bis zur Bestellbestätigung durchtippt.

Mike Cohns Testpyramide, veröffentlicht 2009 in seinem Buch Succeeding with Agile, schlägt die Proportionen vor: viele Unit-Tests, weniger Integrationstests und wenige End-to-End-Tests. Die höheren Schichten sind realistischer, aber langsamer, teurer in der Wartung und anfälliger für Flaky-Fehlschläge, die aus Gründen kommen und gehen, die nichts mit der Änderung zu tun haben. Manche Fachleute plädieren für eine andere Form, etwa mehr Integrationstests bei Systemen, die vor allem Bindeglied zwischen Services sind; einig sind sich alle im Grundsatz, die meisten Probleme mit schnellen, zuverlässigen, fokussierten Tests abzufangen.

Die Testpyramide: viele schnelle Unit-Tests an der Basis, weniger Integrationstests und wenige langsame End-to-End-Tests an der SpitzeEnd-to-EndIntegrationstestsUnit-Testswenige: Gesamtsystemlangsam, teils flakyeinige: Teile gemeinsam,Datenbank, APIsviele: eine Funktionoder Klasse, Millisek.langsamer, teurer,realistischerschnellDie Form ist ein Richtwert, kein Gesetz: Die meisten Prüfungen sollten schnell und fokussiert sein.
Viele schnelle Unit-Tests, weniger Integrationstests und wenige End-to-End-Tests geben schnelles Feedback, ohne an Realismus zu verlieren.
def delivery_fee(basket_total, distance_km):
    if basket_total >= 25:
        return 0.0                      # free delivery on larger orders
    return round(1.50 + 0.40 * distance_km, 2)

def test_free_delivery_over_threshold():
    assert delivery_fee(30.00, 5) == 0.0

def test_fee_grows_with_distance():
    assert delivery_fee(10.00, 5) == 3.50
Zwei Unit-Tests, geschrieben mit pytest, für eine Funktion zur Liefergebühr. Sie laufen in Millisekunden und legen die Regeln fest.

Umgebungen: von der Entwicklung bis zur Produktion

Code durchläuft auf seinem Weg zu den Kunden mehrere Umgebungen, jede eine eigene Kopie des Systems mit eigenen Servern, Daten und Einstellungen. Ziel ist es, Probleme dort zu finden, wo sie billig und harmlos sind, bevor sie die Produktion erreichen, die Live-Umgebung, auf die echte Nutzer angewiesen sind.

UmgebungWer sie nutztDatenZweck
EntwicklungEine Entwicklerin oder ein Entwickler, auf dem Laptop oder in einem persönlichen Cloud-BereichKlein, erfundenCode schnell schreiben und ausprobieren.
Test (oder Integration)Das Team und die automatisierte PipelineErfunden oder anonymisiertAutomatisierte und explorative Tests auf den zusammengeführten Änderungen ausführen.
Staging (oder Pre-Production)Tester, Product Owner, manchmal ausgewählte KundenIn Form und Umfang produktionsähnlich, nie rohe personenbezogene Daten ohne SchutzmaßnahmenDas Release auf einer Kopie proben, die der Produktion so nah wie möglich kommt.
ProduktionEchte KundenEchtDas Geschäft betreiben. Änderungen kommen nur über die Pipeline.

Je stärker Umgebungen von der Produktion abweichen, desto mehr Probleme rutschen durch: Eine Testdatenbank mit hundert Zeilen verrät nicht den fehlenden Index, durch den eine Abfrage bei zehn Millionen Zeilen vierzig Sekunden dauert. Umgebungen ähnlich zu halten ist leichter, wenn sie aus denselben Container-Images und derselben per Skript erzeugten Konfiguration gebaut werden, ein Thema von Infrastructure as Code.

Continuous Integration, Delivery und Deployment

Continuous Integration (CI) ist die Praxis, die Arbeit aller häufig, mindestens täglich, in die gemeinsame Hauptlinie zu mergen, wobei ein automatischer Build- und Testlauf jede Änderung prüft. Sie ging in den 1990er-Jahren aus dem Extreme Programming hervor. Ihr Sinn ist, dass Integrationsprobleme innerhalb von Minuten gefunden werden, solange die Änderung der Autorin oder dem Autor noch frisch im Kopf ist, statt Wochen später in einer mühsamen „Integrationsphase“. Ein Team, das CI praktiziert, behandelt einen fehlgeschlagenen Build als Notfall: Ihn zu reparieren geht jeder neuen Arbeit vor.

Continuous Delivery (CD) erweitert CI so, dass die Software jederzeit releasefähig ist: Jede Änderung, die die Pipeline durchläuft, könnte auf Knopfdruck in Produktion gehen, und ein Release wird zu einer routinemäßigen Geschäftsentscheidung statt zu einem riskanten Ereignis. Den Ansatz beschrieben Jez Humble und David Farley 2010 in ihrem Buch Continuous Delivery. Continuous Deployment geht einen Schritt weiter und gibt jede erfolgreiche Änderung automatisch frei, ohne menschliche Freigabe; Teams, die das tun, deployen unter Umständen viele Male am Tag.

Die Automatisierung, die eine Änderung vom Commit bis in die Produktion trägt, ist eine Pipeline. Weil jeder Schritt automatisiert und jedes Mal identisch ist, hängen Releases nicht mehr davon ab, dass sich eine Person um Mitternacht an eine Checkliste erinnert.

Eine CI/CD-Pipeline: Commit, Build, automatisierte Tests, Staging und Produktion, wobei Continuous Integration die ersten drei Stufen abdecktContinuous Integration: bei jeder ÄnderungDelivery / DeploymentCommitPush oder PRBuildübersetzen, packenAutomat.TestsStagingwie ProduktionProduktionechte Nutzerein Fehler stoppt das Band und geht binnen Minuten ans Team zurückDelivery: Mensch gibt freiDeployment: vollautomatisch
Continuous Integration prüft jede Änderung automatisch; Delivery und Deployment bringen den erfolgreichen Build in die Produktion.

Checkliste: bevor eine Änderung die Produktion erreicht

Was der Prozess eines erfahrenen Teams bei jeder Änderung prüft, größtenteils automatisch.

  • Die Änderung ist mit einer Story oder einem Issue verknüpftJeder kann später herausfinden, warum sie vorgenommen wurde.
  • Sie ist kleinKleine Änderungen sind leichter zu prüfen, zu testen, zu deployen und notfalls rückgängig zu machen.
  • Jemand aus dem Team hat sie geprüftÜber einen Pull Request, mit beantworteten Fragen und erledigten Kommentaren.
  • Automatisierte Tests bestehen auf jeder EbeneUnit- und Integrationstests in der CI und die End-to-End-Prüfungen, die den betroffenen Ablauf abdecken.
  • Sie lief in StagingDerselbe Build, derselbe Konfigurationsmechanismus und dieselben Deployment-Schritte wie in der Produktion.
  • Es gibt einen Weg zurückEin Rollback oder ein Feature-Flag kann sie schnell abschalten, wenn etwas schiefgeht.
  • Jemand bemerkt es, wenn sie etwas kaputt machtMonitoring und Alerts decken ab, was die Änderung berührt (Kapitel 9, Observability).

Arbeitsweisen: Wasserfall, Scrum und Kanban

Teams entscheiden auch, wie sie die Arbeit selbst organisieren. Bei einem sequenziellen Vorgehen, oft Wasserfall genannt, durchläuft das Projekt Phasen der Reihe nach: Anforderungen, Entwurf, Implementierung, Test, Deployment. Jede Phase endet, bevor die nächste beginnt. Der Name wird meist auf Winston Royces Aufsatz von 1970 zurückgeführt, obwohl Royce das Wort nicht verwendete und sogar davor warnte, dass ein einziger Durchlauf durch die Phasen riskant sei. Sequenzielle Pläne passen nach wie vor zu Arbeit, bei der die Anforderungen feststehen und Änderungen sehr teuer sind, etwa bei manchen regulierten oder hardwareabhängigen Projekten. Ihre Schwäche für die meiste Software ist, dass Nutzer bis spät nichts sehen, sodass Missverständnisse dann auftauchen, wenn sie am teuersten zu beheben sind.

Agile Ansätze, benannt im Agilen Manifest von 2001, liefern funktionierende Software in kurzen Zyklen und passen Pläne an, während sie dazulernen. Scrum ist das am weitesten verbreitete Framework: Ein kleines Team arbeitet in festen Zyklen namens Sprints von höchstens einem Monat, die jeweils mit einer Planung beginnen und mit einem Review der Arbeit und einer Retrospektive zur Zusammenarbeit des Teams enden. Der Scrum Guide 2020 definiert drei Verantwortlichkeiten: den Product Owner (entscheidet, was am wertvollsten ist), den Scrum Master (hilft dem Team, Scrum wirksam einzusetzen) und die Developers (erledigen die Arbeit). Kanban, für Software aus dem Produktionssystem von Toyota übernommen, hat keine festen Zyklen: Arbeitspakete fließen über ein sichtbares Board (zu erledigen, in Arbeit, Review, erledigt), und das Team begrenzt, wie viele davon gleichzeitig in Arbeit sein dürfen, damit Arbeit fertig wird, bevor neue begonnen wird.

Keiner dieser Ansätze passt für jedes Team, und viele Organisationen mischen sie: Scrum für Produktteams, Kanban für Support und Betrieb, ein eher sequenzieller Plan rund um eine feste regulatorische Frist. Einen Prozess „agil“ zu nennen macht ihn nicht agil; entscheidend ist, ob funktionierende Software oft genug echte Nutzer erreicht, um von ihnen zu lernen.

DevOps: die Verantwortung eines Teams vom Code bis zum Kunden

Traditionell schrieben Entwickler Software, und ein separates Betriebsteam betrieb sie, mit gegensätzlichen Anreizen: Entwickler wurden für Veränderung belohnt, der Betrieb für Stabilität. DevOps ist die Bewegung, benannt auf der ersten DevOpsDays-Konferenz 2009 in Gent, die beide zusammenbringt. Sie ist vor allem eine Kultur der gemeinsamen Verantwortung („you build it, you run it“), gestützt durch Praktiken wie CI/CD, Infrastructure as Code, Monitoring und schuldzuweisungsfreie Auswertung von Vorfällen. Sie ist weder eine Berufsbezeichnung noch ein Werkzeug, auch wenn beides unter dem Namen verkauft wird. Das DORA-Forschungsprogramm ist vor allem dafür bekannt, Delivery-Performance mit vier Schlüsselmetriken zu messen: wie oft ein Team deployt, wie lange eine Änderung bis in die Produktion braucht, wie oft Änderungen fehlschlagen und wie schnell der Dienst nach einem Ausfall wiederhergestellt ist.

Ausgearbeitetes Beispiel: der Weg eines Tippens

Alles in diesem Kapitel kommt in einem einzigen Moment zusammen. Eine Kundin hat in einer Essensliefer-App einen Warenkorb gefüllt und tippt auf Bestellen. Binnen ein oder zwei Sekunden zeigt der Bildschirm „Bestellung bestätigt“, und zwanzig Minuten später vibriert das Telefon: „Ihr Fahrer ist unterwegs“. Hier ist, was Schritt für Schritt geschieht, mit dem Konzept hinter jedem Schritt und dem Ort, an dem es eingeführt wurde.

Der Weg eines Tippens auf Bestellen: vom Telefon über DNS, HTTPS und einen Load Balancer zum Bestellservice, zur Datenbank, zum Zahlungsanbieter, zur Message Queue und zur Push-BenachrichtigungEssenslieferfirma, Cloud-RegionHandy-AppBestellenDNSName zu AdresseZahlungsanbieterDrittanbieter-APILoad BalancerHTTPS-EndeBestellserviceAPI + LogikBestell-DBTransaktionMessage QueueOrderPlacedBenachr.-servicePush-DienstApple / Google123456789
Ein Tippen durchläuft das Telefon, das Internet, die Cloud-Region des Unternehmens und zwei externe Anbieter; die Nummern entsprechen den Schritten unten.

Schritt für Schritt

  1. 0. Die App baut die AnfrageDie App ist Software auf dem Telefon (Abschnitt 0.1). Sie holt den Warenkorb aus ihren Datenstrukturen im Arbeitsspeicher (Abschnitt 0.2) und macht daraus einen JSON-Body: Restaurant, Artikel, Lieferadresse und einen eindeutigen Bestellschlüssel, der auf dem Telefon erzeugt wird.
  2. 1. DNSDie App braucht die Adresse von api.fooddelivery.example. Das Telefon fragt einen DNS-Resolver, der eine IP-Adresse zurückgibt, oft aus einem Cache (Abschnitt 0.4).
  3. 2. HTTPSDie App öffnet eine TCP-Verbindung und eine TLS-Sitzung, prüft dabei das Zertifikat des Servers und sendet dann POST /v1/orders mit einem Access Token, das belegt, welcher Kunde angemeldet ist (Abschnitte 0.4 und 0.6).
  4. 3. Load BalancerDie Anfrage erreicht einen Load Balancer in der Cloud-Region des Unternehmens. Er entschlüsselt HTTPS und leitet die Anfrage an eine von mehreren funktionsfähigen Kopien des Bestellservice weiter, die in Containern über mehrere Availability Zones verteilt laufen (Abschnitt 0.7).
  5. 4. API und ZahlungDer Bestellservice ist über seine REST-API erreichbar. Sein in Module gegliederter Code (Abschnitt 0.3) validiert den Warenkorb und ruft dann die Web-API des Zahlungsanbieters auf, um die Karte zu autorisieren; er wartet synchron mit einem Timeout, weil der Kunde erst „bestätigt“ sehen darf, wenn die Zahlung genehmigt ist (Abschnitt 0.6). Er sendet den Bestellschlüssel als Idempotenzschlüssel mit, damit die Karte nur einmal belastet wird, falls der Aufruf wiederholt wird.
  6. 5. DatenbanktransaktionIn einer ACID-Transaktion fügt der Service die Bestellung und ihre Artikel ein und speichert die Zahlungsreferenz. Entweder wird alles gespeichert oder nichts; nach dem Commit übersteht es einen Absturz (Abschnitt 0.5). Ein Index auf der Kunden-ID sorgt dafür, dass die Bestellhistorie schnell lädt.
  7. 6. QueueDer Service legt eine OrderPlaced-Nachricht in eine Message Queue und antwortet sofort mit 201 Created. Die App zeigt „Bestellung bestätigt“. Alles Weitere geschieht asynchron (Abschnitt 0.6).
  8. 7. Andere Services reagierenDer Tablet-Service für das Restaurant, der Service für die Kurierdisposition und der Benachrichtigungsservice lesen die Nachricht jeweils, sobald sie bereit sind. Ist einer kurz ausgefallen, warten seine Nachrichten in der Queue.
  9. 8. Push-BenachrichtigungDer Benachrichtigungsservice reagiert auf OrderPlaced und später auf eine ähnliche Nachricht, wenn ein Fahrer zugewiesen ist, indem er den Push-Dienst von Apple oder Google aufruft, eine weitere Drittanbieter-API, die die Benachrichtigung auf das Telefon des Kunden zustellt.
  10. 9. Und hinter allemJedes Stück Code auf diesem Weg wurde nach einer User Story geschrieben, in einem Pull Request geprüft, auf mehreren Ebenen getestet und über eine CI/CD-Pipeline via Staging in die Produktion ausgeliefert (dieser Abschnitt).

Ausgearbeitetes Beispiel

Ausgearbeitetes Beispiel: was der Weg lehrt

Fragen Sie bei jedem Schritt „Was wäre, wenn?“, und die Architekturfragen zeigen sich.

Was, wenn beim Zahlungsaufruf ein Timeout eintritt?Wurde die Karte belastet oder nicht? Der Idempotenzschlüssel erlaubt dem Bestellservice, gefahrlos erneut zu fragen, ohne doppelt zu belasten.
Was, wenn der Datenbank-Commit nach der Zahlung fehlschlägt?Zahlung und Datenbank sind getrennte Systeme, daher deckt keine einzelne ACID-Transaktion beide ab. Der Entwurf braucht einen Weg zur Erstattung oder Wiederholung, ein Problem, das Kapitel 6 mit Sagas löst.
Was, wenn der Benachrichtigungsservice ausgefallen ist?Sichtbar geht nichts kaputt: Bestellungen werden weiter angenommen, und Nachrichten warten in der Queue. Genau deshalb ist dieser Schritt asynchron.
Was, wenn sich der Verkehr zur Abendessenszeit verdreifacht?Der Load Balancer verteilt die Anfragen auf mehr Kopien des Bestellservice, die Autoscaling hinzufügt; die Datenbank ist nun der wahrscheinliche Engpass.
Was, wenn ein Release den Checkout kaputt macht?Tests in der Pipeline sollten es abfangen; falls nicht, löst das Monitoring einen Alert aus, und das Team macht einen Rollback auf die vorige Version.

Keine dieser Fragen betrifft, wie man eine Funktion schreibt. Es geht darum, wie die Teile angeordnet sind und was jeder Teil den anderen verspricht.

Warum das für die Architektur wichtig ist

Der Weg eines Tippens nutzt ein Dutzend Komponenten, drei Organisationen sowie synchrone und asynchrone Kommunikation, und er musste trotzdem entworfen werden: Jemand hat entschieden, dass die Zahlung synchron ist und Benachrichtigungen nicht, dass Bestellungen in einer relationalen Datenbank liegen, dass der Service in Containern über zwei Zonen läuft, dass der Zahlungsanbieter hinter einem Modul abgeschottet ist. Zu entscheiden, wie diese Teile angeordnet sind, welche Qualitäten jede Anordnung erreicht und was sie kostet, und festzuhalten, warum, ist Softwarearchitektur. Auch die Art, wie Software entsteht, ist für die Architektur wichtig: Eine Architektur, die sich nicht in Teilen testen oder nicht ohne die Abstimmung von zehn Teams ausliefern lässt, bremst jede Änderung. Kapitel 1 beginnt mit Was Softwarearchitektur ist.

Prüfen Sie Ihr Verständnis

Was ist der Unterschied zwischen Continuous Delivery und Continuous Deployment?

Bei Continuous Delivery kann jede erfolgreiche Änderung auf Knopfdruck freigegeben werden, aber ein Mensch entscheidet, wann. Bei Continuous Deployment wird jede erfolgreiche Änderung automatisch freigegeben.

Warum stellt die Testpyramide Unit-Tests an die Basis?

Sie sind schnell, billig und zuverlässig, sodass ein Team bei jeder Änderung Tausende ausführen und in Minuten Feedback bekommen kann. Langsamere, anfälligere End-to-End-Tests bleiben den wenigen Abläufen vorbehalten, die sie brauchen.

Warum ist beim Weg eines Tippens der Zahlungsaufruf synchron, die Benachrichtigung aber asynchron?

Die App darf erst „bestätigt“ anzeigen, wenn die Zahlung genehmigt ist, daher wartet sie auf diese Antwort. Die Benachrichtigung kann etwas später gesendet werden, daher läuft sie über eine Queue, und ihr Ausfall kann das Bestellen nicht blockieren.

Welche zwei Hauptvorteile hat ein Pull Request über das Finden von Bugs hinaus?

Er verbreitet Wissen, weil mindestens eine weitere Person jede Änderung versteht, und er hält das Design des Codes konsistent, weil Reviewer strukturelle Entscheidungen hinterfragen können.

Ende von Kapitel 0Den Kapiteltest ablegenOptional: 20 Multiple-Choice-Fragen und 2 schriftliche Antworten, in der App.Zum Test →

Quellen und weiterführende Literatur

Haben Sie eine Frage zu diesem Abschnitt? ArchitectBot, der KI-Lernassistent des Kurses, antwortet zuerst aus dem Kurs.

Fragen: „Wie beeinflusst die Art, wie mein Team Code testet und deployt, die Architektur, die wir wählen sollten?“