Wenn Sie Microservices erstellen, müssen Sie verstehen, was ein begrenzter Kontext ist

Mar 16, 2020 · 19 min read

Foto vom National Cancer Institute auf Unsplash

Das Wachstum der Einführung von Microservices hat zu einer Wiederbelebung der Popularität von einigen zuvor übersehenen Software-Designmustern. Viele dieser Muster stammen aus Eric Evans ‚Domain Driven Design, einem Buch, in dem es sowohl um Teamstruktur als auch um Softwarearchitektur geht.

Und von diesen Mustern ist der begrenzte Kontext vielleicht am wichtigsten zu verstehen. Als Ingenieure haben wir den begrenzten Kontext als ein Entwurfsmuster für Softwarearchitekturen betrachtet. Aber das liegt daran, dass wir es ein wenig von seiner ursprünglichen Verwendung übernommen haben. Wie von Evans verwendet, ist der begrenzte Kontext ebenso ein organisatorisches wie ein technisches Muster.

Aus diesem Grund betrachte ich das beschränkte Kontextmuster als Dreh- und Angelpunkt beim Verständnis von Microservices. Nicht nur, wie man sie baut, sondern auch, warum wir sie überhaupt bauen und wie sie unsere Organisationen erfolgreicher machen können. Wenn wir verstehen, was begrenzte Kontexte sind – wenn wir die Denkweise des begrenzten Kontexts sowohl technisch als auch organisatorisch übernehmen —, können wir beim Aufbau unserer Microservices-Architektur wirklich erfolgreich sein.

Warum auf Microservices umsteigen?

Lassen Sie uns zunächst eine kleine Übung durchführen. Stellen Sie sich diese Frage: Warum entwickeln wir Microservices überhaupt?

Nehmen Sie sich einen Moment Zeit, um darüber nachzudenken. Was sind die Vorteile, die zuerst in den Sinn kommen? Was sind die Hauptprobleme, die wir zu lösen hoffen sollten? Notieren Sie sich einige Antworten, nur um ehrlich zu bleiben.

Haben Sie Ihre Antwort? Gut. Lies es dir selbst vor. Haben Sie die technischen Standardvorteile erreicht? Continuous Delivery, Skalierbarkeit, polyglotte Umgebungen, Container und Clouds und all das gute Zeug? Groß.

Aber enthielt Ihre wichtigste Antwort etwas darüber, wie Sie Ihrem Unternehmen ermöglichen können, effizienter zu arbeiten? Es sollte. Denn beim Aufbau von Microservices geht es nicht darum, technische Vorteile zu realisieren. Es geht wirklich darum, organisatorische Vorteile zu erzielen. Alles andere ist ein Implementierungsdetail.

Monoliths = gekoppelter Code und gekoppelte Teams

Wenn unsere Monolithen immer größer werden, nimmt die Produktivität ab. Dafür gibt es mindestens zwei Hauptgründe.

Wir bremsen unsere Geschwindigkeit

Erstens trägt jedes Engineering-Team zu einer riesigen Codebasis bei. Daher ist es für Teams immer wahrscheinlicher, dass ihr Code mit dem Code anderer in Konflikt gerät. Um die potenziellen Probleme, die dies verursachen könnte, zu mildern, führen wir Verfahren ein — Code-Einfrieren, QA-Testperioden, Release-Züge usw. – die buchstäblich dazu bestimmt sind, unsere Produktivität zu verlangsamen.

Natürlich verhindern diese Verfahren, dass Funktionen und Verbesserungen rechtzeitig bereitgestellt werden. Sie zerstören auch die Fähigkeit der Ingenieure, sich auf die Prioritäten ihrer Teams zu konzentrieren. Wenn während eines Testzeitraums ein Fehler gefunden wird, muss sich das verantwortliche Team auf die Behebung dieses Fehlers konzentrieren. Wenn ein schwerwiegender Fehler in der Produktion gefunden wird, muss das Team den Fehler nicht nur beheben, sondern auch durch die Reifen springen, um ihn im nächsten Release-Zug bereitzustellen.

Bereitschaftsdienst wird zum Segen. Wenn mit unserem Monolithen etwas schief geht, muss jemand Tag und Nacht verfügbar sein, um das Problem zu beheben. Aber wer? Große Organisationen mit großen Monolithen stehen im Allgemeinen vor zwei Möglichkeiten:

  • Ein Incident-Management-Team, dessen einzige, traurige Aufgabe innerhalb der Organisation darin besteht, auf Probleme zu reagieren, die durch den Code anderer Ingenieure verursacht werden, und herauszufinden, wie sie gelöst werden können.
  • Ein rotierender Bereitschaftsplan, bei dem jede Woche ein beliebiger Ingenieur mit der traurigen Aufgabe betraut wird, für die Lösung von Problemen verantwortlich zu sein, die höchstwahrscheinlich durch Code verursacht werden, der von einem anderen Ingenieur in einem anderen Ingenieurteam geschrieben wurde.

(Mis)Organisation unserer Teams

Monolithen stören unsere Organisationen auf eine andere Weise. Unsere gesamte Organisation arbeitet an demselben großen Produkt. Aber wir müssen die Organisation noch in überschaubare Teams aufteilen. Daher neigen wir dazu, funktionale Rollen zu suchen, um Teamgrenzen zu finden:

ollaborative Arbeit. Anstatt zusammenzuarbeiten, um das eigentliche Problem zu lösen (z. B. Wie entwerfen, erstellen und warten wir Feature X?) Mitglieder der verschiedenen Funktionsbereiche konzentrieren sich einfach auf ihren eigenen Teil und werfen ihre Arbeit metaphorisch über den Zaun, wenn sie fertig sind. Das Potenzial für Zusammenarbeit und Synergie — wo die kombinierte Qualität der Teamarbeit viel mehr ist als die Summe der einzelnen Teammitglieder — geht verloren.

Es ist auch voller Engpässe. Wenn wir unsere Teams nach Funktionsbereichen organisieren, haben wir natürlich eine Fehlausrichtung der Prioritäten. Nehmen wir an, das Produktmanagementteam hat entschieden, dass der Checkout-Prozess unseres Monolithen überarbeitet werden muss. Sie planen Zeit mit dem Designteam, um einige Mocks zusammenzustellen. Irgendwann werden die Mocks fertig sein und dem Frontend-Team zur Implementierung übergeben. Natürlich benötigt das Frontend-Team APIs, die vom Backend-Team implementiert werden müssen, sodass sie blockiert werden, bis dies abgeschlossen ist. Sobald das Backend-Team seine Arbeit an den neuen Checkout-Diensten priorisiert hat, stellt es fest, dass es Hilfe vom Datenbankadministrationsteam (DBA) benötigt. Was natürlich seine eigenen Prioritäten hat. Das Backend-Team wird also blockiert, bis ein DBA freigegeben ist.

eise scheint diese Organisationsstruktur ein bisschen wie eine schlecht gestaltete, übermäßig gekoppelte Softwarearchitektur … nicht wahr?

Microservices = entkoppelter Code, entkoppelte Teams

Im Gegensatz dazu ermöglicht eine Microservices-Architektur Teamautonomie. Es wird viel einfacher, Teams zu bilden, die in sich geschlossen sind, effizient zusammenarbeiten und nicht ständig durch Abhängigkeiten von anderen Teams blockiert werden.

Teams können die volle Verantwortung für ihre Arbeit übernehmen, vom Design über die Entwicklung bis hin zur Bereitstellung. Jedes Mitglied trägt die Verantwortung für das Erreichen des Ziels seines Teams, sodass es Anreize erhält, an mehr als nur „seinem Teil“ teilzunehmen. Ich habe mit Teams zusammengearbeitet, in denen Produktmanager, Designer, Front-End-, Back-End- und Mobile-Ingenieure zusammengekommen sind, um Produktfunktionen zu entwerfen, die weitaus bessere Ergebnisse liefern, als dies von einer Person hätte erreicht werden können.

Das Team übernimmt die Verantwortung für seine eigenen Artefakte, sobald sie in der Produktion bereitgestellt werden. Dies führt im Allgemeinen zu Code höherer Qualität, der einfacher zu beheben ist. Warum ist das so? Anders als bei einem Monolithen neigen Teams dazu, eine ganzheitliche Sicht auf die Microservices zu haben, die sie besitzen. So ist es für das Team viel einfacher, Probleme zu antizipieren, eine gute Protokollierung und Metriken hinzuzufügen, um Probleme zu beheben, wenn sie auftreten, und Belastbarkeitsmuster (z. B. Wiederholungsversuche, Leistungsschalter und Fallbacks usw.) ordnungsgemäß zu verwenden, um Probleme zu vermeiden an erster Stelle.

Da Teams ein volles Gefühl der Eigenverantwortung für ihre Arbeit haben, geht es weniger um einen albtraumhaften Veröffentlichungsplan als vielmehr um die Pflege ihrer Kreation, wenn sie ihre Dienste in der Produktion gesund und am Laufen halten.

Schließlich arbeiten die Teams auf das gleiche Ziel hin, auf der gleichen Zeitachse. Das bedeutet, dass eine Person nicht mehr blockiert wird, während sie darauf wartet, dass jemand in einem anderen Funktionsbereich frei wird.

Wir müssen uns bewusst sein, was Autonomie angeht

Aber wir erhalten diese Vorteile nicht kostenlos, indem wir unseren Monolith einfach in Microservices aufteilen. Werfen wir einen Blick auf unsere erste, naive Ansicht einer Microservices-Architektur:

Wenn wir wie die meisten Ingenieure sind, besteht unsere ursprüngliche Idee einer Microservice-Architektur aus einer Reihe von Microservices. Jeder macht eine Art API (ReST, vielleicht) verfügbar, damit jeder andere Dienst daraus lesen und schreiben kann.

Wenn wir Erfahrungen sammeln, lernen wir, dass nicht alle Microservices den gleichen Zweck erfüllen — oder zumindest nicht. Und so wie unser Monolith in Schichten angeordnet wurde, ordnen wir unsere Microservices an:

An dieser Stelle haben wir die verschiedenen Arten von Microservices und Anwendungen definiert, die wir erstellen möchten. Groß. Aber wir haben immer noch keine großen Fortschritte in Bezug auf die Autonomie des Teams gemacht. Jeder Microservice muss einem Team gehören. Und so stellt sich die Frage: Welchen Teams gehören welche Microservices?

Funktionsübergreifende Teams

Unser erster, naiver Ansatz könnte darin bestehen, unsere Teams zu organisieren, indem wir unsere Monolith-Organisationsstruktur nachahmen:

Hier sehen wir Teams (in lila), die nach Funktionen organisiert sind: UX Design, Frontend Engineering, Backend Engineering, Data Engineers, DBAs, QA usw.

Das könnte sich zumindest anfangs richtig anfühlen. Aber lassen Sie uns einen Schritt zurücktreten und uns den Wert ansehen, den wir unseren Kunden bieten wollen. Ist es unser Ziel, Dinge wie die folgenden für unsere Kunden zu bauen?

  • Eine Reihe von Datenbankschemata
  • Eine Reihe von Benutzeroberflächenmodellen
  • Eine Reihe von Microservices, die mit einer MySQL-Datenbank kommunizieren können?

Nicht wirklich. Das sind nur die Werkzeuge, mit denen wir Mehrwert für unsere Kunden schaffen. Der tatsächliche Wert, den wir unseren Kunden / Benutzern bieten, besteht aus Funktionen und Funktionen wie:

  • Ein Produktkatalog zum Suchen
  • Ein Mechanismus, um Artikel in einen Warenkorb zu legen und anschließend zu kaufen
  • Ein Benachrichtigungssystem, um Kunden über den Status ihrer Einkäufe zu informieren

Ebenso möchten wir unser Team nicht nach Funktionsbereichen organisieren. Vielmehr sollten wir unsere Teams durch den Wert definieren, den sie für Kunden schaffen; das heißt, funktionsübergreifend, in (den treffend benannten) funktionsübergreifenden Teams.

In funktionsübergreifenden Teams arbeiten alle zusammen, um ein bestimmtes Produkt oder eine bestimmte Funktion von Anfang bis Ende zu entwickeln. Jeder im Team hat die gleichen Ziele und Prioritäten, sodass kein Funktionsbereich von einem anderen blockiert wird. Erfordert der neue Backend-API-Dienst einige Datenbankdesignarbeiten? Gut; Der Backend-Ingenieur und der DBA des Teams können beide ihre Arbeit priorisieren.Im besten Fall ermutigen funktionsübergreifende Teams die Mitglieder, in jeder Phase des Projekts zusammenzuarbeiten. Jedes Teammitglied trägt zum Gesamtdesign des Features bei. Frontend-, Backend- und Mobile-Engineers definieren gemeinsam API-Verträge. Jeder testet. Und jeder beginnt sich in seinem jeweiligen Bereich gut zu auskennen.

Und so könnten unsere Teamstrukturen ungefähr so aussehen:

Das ist besser. Aber irgendetwas fühlt sich immer noch nicht richtig an.

Sicher, wir haben Teams gebildet, die wahrscheinlich effektiver darin sein werden, Produkte zu besitzen. Wir haben jedoch immer noch einen Top-Down-Ansatz gewählt, um die Topologie der Microservices zu identifizieren, die unsere Organisation erstellen möchte. Wir haben eine große Sammlung von voneinander abhängigen Microservices, von denen die meisten miteinander gekoppelt sind. Wir haben sie einfach verschiedenen Teams zugewiesen, um sie aufzubauen.

Dies führt zu Bedenken wie:

  • Wie können wir APIs erstellen, die alle aktuellen und zukünftigen Anforderungen eines Kunden erfüllen? Können wir unsere Daten kapseln, wenn einer unserer Dienste von den Diensten eines anderen Teams aufgerufen wird?
  • Wie viel Zeit werden wir damit verschwenden, darauf zu warten, dass andere Teams unsere Abhängigkeiten implementieren?
  • Welche Ausfälle unserer Systeme können durch Ausfälle in anderen Systemen (kaskadierende Ausfälle) verursacht werden?
  • Können wir die Anzahl der Anrufe kontrollieren, an denen unsere Dienste beteiligt sein könnten? Können wir sicherstellen, dass unsere Organisation keine grenzenlosen synchronen Anrufe zwischen Diensten erstellt, was zu astronomischen Antwortzeiten führt, oder schlimmer noch (und ja, ich habe dies gesehen) unendlich rekursive Anrufe über Dienste hinweg?
  • Was ist, wenn das spezifische Feature oder der Problembereich unseres Teams nicht für die vorgeplante Microservice-Topologie geeignet ist?

Wir brauchen noch eine andere Denkweise. Vielleicht gibt es bereits ein Muster, dem wir folgen können?

Geben Sie den begrenzten Kontext ein

Der begrenzte Kontext ist ein Schlüsseldesignmuster aus Domain Driven Design oder DDD. Das Verständnis des begrenzten Kontexts hilft uns, autonome Teams und damit autonome Microservice-Architekturen zu bilden.DDD selbst beschreibt eine Methodik der Softwareentwicklung, bei der Einzelpersonen innerhalb einer Organisation zusammenarbeiten, um eine gemeinsame Sprache zu definieren. In seinem Buch Domain Driven Design stellt Eric Evans häufig Ingenieure dar, die mit Produktbesitzern zusammenarbeiten, um ein vereinbartes Vokabular zu erstellen, um Dinge wie Produkte, Komponenten der Produkte, Aktionen, die ein Produkt ausführen kann (oder an dem Produkt ausgeführt werden kann), Teile von Workflows usw. zu beschreiben. Dieses Vokabular umfasst die Domäne der Organisation.

In vielen großen Organisationen wird es jedoch unmöglich, ein einziges, konsistentes Vokabular zu definieren. In diesen Fällen teilen wir unsere Domain in Subdomains auf. Beispiele für Subdomains können sein:

  • Inventory management
  • Product discovery
  • Order management
  • Warenkorb und Kasse

Wenn Designer, Ingenieure, Produktmanager usw. zusammenkommen, um eine Subdomain aufzubauen, bilden sie ihre eigene Art zu denken und über die Subdomain und ihre Komponenten zu sprechen.

Hier trifft DDD auf funktionsübergreifende Teamstruktur. Obwohl die Teammitglieder aus verschiedenen Funktionsbereichen stammen, sind sie für ihre eigene Subdomain verantwortlich und werden schließlich zu ansässigen Experten. Darüber hinaus ist das Team dafür verantwortlich zu bestimmen, welche Artefakte — Microservices, Webanwendungen, mobile Apps, Datenbanken und die zugehörige Infrastruktur — benötigt werden, um die Subdomain zum Leben zu erwecken und den Kunden des Unternehmens zur Verfügung zu stellen.

Wir können uns das Team und seine Artefakte als einen begrenzten Kontext vorstellen.

Den begrenzten Kontext definieren

Während Evans in seinem Buch häufig über begrenzte Kontexte spricht, definiert er das Muster nicht wirklich explizit. Also werde ich versuchen, es hier zu tun:

Begrenzter Kontext:Ein intern konsistentes System mit sorgfältig entworfenen Grenzen, die vermitteln, was in das System eintreten und was es verlassen kann.

Mit anderen Worten, ein begrenzter Kontext repräsentiert einen Kontext — im Wesentlichen ein System, das kooperative Komponenten kapselt — mit klar definierten Grenzen, die bestimmen, was in das System eintreten und was es verlassen kann.

ebewesen) bieten eine schöne Analogie. Innerhalb einer Zelle befinden sich alle möglichen Komponenten (Zellkern, Ribosomen, Zytoplasma, Zytoskelett usw.), die alle in der Zelle selbst eingekapselt sind. Um jede Zelle herum befindet sich jedoch eine Membran, die als Barriere zwischen den Zellinneren und dem Rest des Organismus fungiert. Die Membran schützt die Zelle vor ihrer Umgebung, lässt bestimmte Nährstoffe in sie eindringen und lässt verschiedene Nebenprodukte austreten.

In gleicher Weise besteht ein begrenzter Kontext aus einer Vielzahl von Komponenten (Microservices, Webanwendungen, mobile Apps, Datenbanken, Nachrichtenwarteschlangen usw.). Es dient auch als logische Barriere, die diese Komponenten kapselt. Intern können die Komponenten gekoppelt werden und können Daten frei aneinander weitergeben. Der begrenzte Kontext hilft jedoch dabei, eine lose Kopplung extern durchzusetzen, indem explizite Punkte definiert werden, an denen:

  • Externe Daten können eingegeben werden (möglicherweise über einen Verbraucher, der ein Kafka-Thema abonniert hat)
  • Interne Daten können beendet werden (möglicherweise über ein anderes Kafka-Thema oder über eine gut gestaltete GET-API, die sorgfältig erstellt wurde, um alle internen Systemdetails zu verbergen)

Ein begrenzter Kontext repräsentiert auch sein funktionsübergreifendes Team. Das Team besteht aus verschiedenen Teammitgliedern (Designer, Frontend/Backend/Mobile Engineers, Produktmanager, Data Engineers und QA Engineers, etc.). Intern arbeiten diese Mitglieder kooperativ auf die gleichen konsistenten Ziele hin. Darüber hinaus sind (oder sollten) diese Teammitglieder gekapselt sein, damit sie nur minimale Abhängigkeiten von anderen Teams haben.Anstatt also auf organisatorischer Ebene zu beginnen und alle Anwendungen und Microservices zu definieren, die wir erstellen möchten, bauen wir Teams rund um unsere Subdomains auf, damit diese Teams ihre Subdomains erweitern und definieren können, was erstellt werden muss. Richtig gemacht, neigen wir dazu, verschiedene begrenzte Kontexte in der Organisation als organisch wachsend und nicht als starre, vordefinierte Strukturen zu sehen.

Auswirkungen auf das Brechen des Monolithen

Conways Gesetz sagt uns, dass Organisationen Softwaresysteme entwerfen, die die Kommunikationsstruktur ihrer Organisation nachahmen. Das erweist sich oft als wahr, daher sollten wir darüber nachdenken, wie wir unsere Organisation strukturieren, wenn wir mit dem Aufbau von Microservices beginnen.

In der Tat sollte jetzt ein Bild in Ihrem Kopf auftauchen. Wenn wir von Monolith zu Microservices übergehen, sollten wir anfangen, vertikal zu denken (den Monolith durch seine Subdomains zu teilen), anstatt horizontal (den Monolith durch seine Funktionsschichten zu teilen).

das Recht

Mit anderen Worten, wir sollten nicht damit beginnen, die Datenzugriffsschicht des Monolithen durch Daten-Microservices zu ersetzen. Vielmehr sollten wir zunächst eine ganze Funktion aufteilen (z. B. den Checkout-Prozess oder die Produktsuche). Jedes Feature repräsentiert einen begrenzten Kontext. Und jeder wird von einem engagierten funktionsübergreifenden Team aufgeteilt.

Darüber hinaus sollte sich dieses Team auf seine Aufgabe konzentrieren, nämlich entweder:

  • die vorhandene Funktionalität originalgetreu nachzubilden,
  • oder (besser) eine völlig neue, verbesserte Erfahrung für seine Kunden zu schaffen.

Als Teil des Prozesses sollte das Team das System entwerfen, das für das Unterfangen am besten geeignet ist.

Zum Beispiel könnten wir uns entscheiden, unsere Produktsuchfunktion aus unserem Monolith zu entfernen. Das Produktsuchteam könnte letztendlich ein System entwerfen, das Folgendes umfasst:

  • Kafka-Konsumenten, die eine Reihe externer Kafka-Themen anhören, um ihr eigenes internes System of Record (SoR) für Produkte zu aktualisieren.
  • ein Kafka-Publisher, der Änderungen an seinem SoR auf ein internes Kafka-Thema überträgt
  • Ein anderer Kafka-Consumer, der dieses interne Thema abhört und einen elastischen Suchindex aktualisiert
  • ein GraphQL-Endpunkt für Freiformsuchen, der die elastische Suche abfragt
  • ein ReST-Endpunkt, der einzelne Produkte nach ID abruft
  • eine neu gestaltete Webanwendung, die diese Endpunkte verwendet, um Kunden die Suche nach Produkten und die Erkundung von Produktdetails zu ermöglichen
  • ein ähnlicher Satz von Bildschirmen in apps, die diese Endpunkte verwenden
  • Ein Kafka-Publisher, der Nachrichten verschiebt, darstellung verschiedener Abfragen, die von Kunden an ein externes Kafka-Thema durchgeführt werden, zur Verwendung durch einen anderen begrenzten Kontext (z. B. analytics), der interessiert sein könnte

Wie das Design unseres rot gekapselten Product-Search Bounded Context aussehen könnte

Während wir immer mehr vertikale Teile unseres Monolithen abziehen, bauen andere Teams ihre eigenen begrenzten Kontexte auf. Diese begrenzten Kontexte könnten am Ende ganz anders aussehen.

Jedes Team bestimmt, wie es seine Aufgabe am besten löst

Beachten Sie, dass Komponenten innerhalb eines bestimmten begrenzten Kontexts eng miteinander gekoppelt sein können. In unserem Beispiel erfolgt jede Kommunikation zwischen begrenzten Kontexten durch Übergeben von Nachrichten über eine Kafka-Nachrichtenwarteschlange. Wichtig ist, dass wir synchrone Anforderungs- / Antwortaufrufe zwischen begrenzten Kontexten vermeiden.

Dies gilt auch für die Überreste des Monolithen. Wir wollen sicherlich keine enge Kopplung zwischen unseren neuen Microservices und unserem Legacy-Monolith. Wenn wir also Teile des Monolithen entfernen, nutzen wir die Nachrichtenübermittlung, damit die verbleibenden Teile mit unseren neuen begrenzten Kontexten kommunizieren können.

Realitätscheck über all diese Entkopplung

An dieser Stelle können wir uns fragen, ob es wirklich möglich ist, unsere begrenzten Kontexte entkoppelt zu halten.

Können wir unsere Teams in der realen Welt wirklich vor externen Abhängigkeiten schützen? Wird es nie Fälle geben, in denen ein Team von einem anderen Team blockiert werden muss, um seine Arbeit zu erledigen?

Und können wir tatsächlich Servicearchitekturen für unsere Subdomains erstellen, die vollständig von anderen Subdomains entkoppelt sind? Ist es wirklich nicht erforderlich, dass eine Anwendung in einem begrenzten Kontext jemals synchron einen Dienst in einem anderen aufruft?

In Wirklichkeit ist es möglicherweise unmöglich, unsere begrenzten Kontexte zu 100% entkoppelt zu halten. Aber wir können uns nähern, viel näher als die meisten von uns vielleicht denken.

Reale Architekturen

Schauen wir uns zunächst entkoppelte Architekturen an. Oft kaufen wir in den Trugschluss, dass jede Art von Daten in genau einem Ort leben sollte, und dass jedes andere System muss direkt in diesem einen Ort aufrufen, um auf die Daten zuzugreifen.

Wir bezeichnen dies als Zuweisung einer Single Source of Truth (SSoT) zu unseren Daten. Aber wie in diesem Artikel beschrieben, der die Idee von SSoTs seziert, ist dieser Begriff im Großen und Ganzen ein Anti-Muster. Stattdessen sollten die meisten begrenzten Kontexte ihre eigene lokale Kopie aller Daten speichern, die sie verwenden müssen.

Dies wird durch unseren produktsuchbegrenzten Kontext aus dem vorherigen Abschnitt veranschaulicht. Dieser begrenzte Kontext hängt natürlich stark von den Produktkatalogdaten unserer Organisation ab. Aber die Chancen stehen gut, dass Daten in einem anderen begrenzten Kontext generiert werden (wir nennen es den produkteintragsgebundenen Kontext).

Unser erster (naiver) Ansatz könnte darin bestehen, eine ReST-API aus dem produkteintragsgebundenen Kontext verfügbar zu machen und die Dienste innerhalb des produktsuchgebundenen Kontexts zu zwingen, diese API aufzurufen. Aber wir können es besser machen. Wir können stattdessen die Systeme entkoppelt halten, indem wir die von den Produkteintrittsdiensten vorgenommenen Änderungen in Kafka veröffentlichen. Unsere Kafka-Konsumenten für die Produktsuche nehmen diese Nachrichten dann auf und aktualisieren die Produktsuchdatenbanken.

Beachten Sie, dass diese beiden begrenzten Kontexte sind schließlich konsistent. Dies bedeutet, dass es kurze Zeiträume gibt, in denen ein bestimmtes Datenelement zwischen Produkteingabe und Produktsuche inkonsistent sein kann. Zum Beispiel, wenn der Preis von White Wombat Widgets von $ 1,99 auf $ 2 erhöht wird.49, wird es eine kurze Zeit (oft eine Frage von Sekunden, wenn nicht Millisekunden), wo es eine 50 ¢ Unterschied in White Wombat Widget Preis über die beiden begrenzten Kontexte.

Dies führt zu den realen Fällen, in denen wir keine Alternative haben, als begrenzte Kontexte zu koppeln. In einigen Fällen ist eine eventuelle Konsistenz nicht akzeptabel. Bevor ein Kunde beispielsweise seinen Online-Kauf abschließen kann, müssen wir möglicherweise sicherstellen, dass jeder Artikel in seinem Warenkorb zu diesem Zeitpunkt tatsächlich verfügbar ist. Selbst dann können wir oft die Kopplung zwischen den beiden begrenzten Kontexten minimieren.

Unsere Interaktionen könnten folgendermaßen aussehen:

  • Da der Kunde die Benutzeroberfläche für die Produktsuche verwendet, um Produkte zu finden, werden die Produktsuchdatenbanken verwendet, um Informationen abzurufen (z. B. Stile, Kundenbewertungen, Preise usw.) über die Produkte
  • Auch wenn der Kunde mit dem Bestellvorgang beginnt, verwenden wir die Produktsuchdatenbanken, um die Informationen abzurufen, die angezeigt werden müssen.
  • Wenn der Kunde schließlich auf die Schaltfläche „Kauf abschließen“ klickt, rufen wir den produkteintragsbegrenzten Kontext synchron auf, um die Verfügbarkeit der Artikel vor Abschluss des Kaufs zu überprüfen.

Ein weiteres häufiges Beispiel, das sofortige Konsistenz in Bezug auf die Autorisierung erfordert. In vielen Systemen müssen Sicherheitstoken bei jeder Anforderung abgerufen oder validiert werden. In diesen Fällen müssen wir wahrscheinlich zulassen, dass unsere begrenzten Kontexte einen anderen, sicherheitsorientierten begrenzten Kontext aufrufen.

Reale Organisationsstrukturen

Wie wäre es mit eigenständigen, funktionsübergreifenden Teams? Wie möglich sind sie in der realen Welt?

In Wirklichkeit ist es ein Prozess der kontinuierlichen Bewegung hin zu völlig eigenständigen Teams. Selten werden wir jemals 100% Autonomie mit unseren Teams erreichen. Aber wenn wir damit beginnen, unsere Teams intelligent zu organisieren und auftretende Engpässe zu erkennen und darauf zu reagieren, können wir uns nähern.Für den Anfang sollten wir unsere vertikalen, funktionsübergreifenden Teams maximieren und die Anzahl der horizontalen, einzelfunktionalen Teams minimieren. Das bedeutet, sich dem Drang zu widersetzen, sogenannte „Kernteams“ zu bilden – deren Aufgabe es ist, gemeinsame Datendienste aufzubauen, die von anderen produktorientierten Teams genutzt werden — und stattdessen unsere Teams um den Geschäftswert zu bilden, den sie bieten werden.

Viele Organisationen gehen auf Zehenspitzen auf dieses Ziel zu und bilden zunächst domänenorientierte Teams von Produktmanagern sowie Front-End- und Back-End-Ingenieuren. Das ist ein Anfang. Aber wen sollten diese Teams noch einschließen? Die genaue Mitgliedschaft kann sich zwischen verschiedenen Teams mit unterschiedlichen Anforderungen unterscheiden. Aber wir sollten Dinge wie:

  • Wenn unser Team Front-End-Ingenieure hat, sollten sie wahrscheinlich eng mit einem Grafikdesigner zusammenarbeiten, der sich der Domain widmet.
  • Mobile Engineers – oft in ihren eigenen Bereich der Organisation gebunden – sollten für Domänen mit einer mobilen Komponente einbezogen werden.
  • In ihrem aufschlussreichen Artikel über Data Meshes beklagt Zhamak Dehghani, dass Dateningenieure oft von funktionsübergreifenden Teams ausgeschlossen werden – zum Nachteil der Dateningenieure und der funktionsübergreifenden Teams selbst.

Sobald wir die Zusammensetzung unserer Teams festgelegt haben, sollten wir auf Engpässe achten. Gibt es andere Teams, die die Produktivität unserer funktionsübergreifenden Teams gewöhnlich blockieren?

Zum Beispiel haben viele Organisationen ein dediziertes Sicherheitsteam. Dies ist natürlich eine gute Praxis; Organisationen brauchen eine kohärente Sicherheitsstrategie und eine Möglichkeit, die Governance über diese Strategie sicherzustellen. Es ist jedoch auch üblich, dass Teams ihre Arbeit in verschiedenen Phasen unterbrechen, um Sicherheitsüberprüfungen ihrer Arbeit zu ermöglichen. Selbst in den besten Situationen, Dies schafft Hindernisse für unsere Teams als Routine eine Geschäftspraxis. Darüber hinaus führt dies häufig dazu, dass Teams ihre gesamte Arbeit oder einen Teil davon verschrotten und von vorne beginnen müssen, da sie Sicherheitsanforderungen aufdecken, die nicht erfüllt wurden.

Dies ist eindeutig ein schlechter Geruch. Aber wie können wir die Sicherheitsstandards unserer Organisation durchsetzen und gleichzeitig den Teams ermöglichen, autonom und produktiv zu bleiben?

Wir können dies tun, indem wir unseren funktionsübergreifenden Teams Sicherheitsingenieure hinzufügen. Es gibt drei Ansätze, die wir nehmen können:

  • Wenn wir das Glück haben, ein relativ großes Sicherheitsteam zu haben, können wir jedem funktionsübergreifenden Team einen Vollzeit-Sicherheitsingenieur (SE) zuweisen.
  • Bei kleineren Sicherheitsteams kann jede SE in Teilzeit mehreren funktionsübergreifenden Teams zugeordnet werden. Dies würde es dem SEs weiterhin ermöglichen, die Ziele und Designs der Teams zu verstehen und mit dem Team zusammenzuarbeiten, um die Sicherheitsstandards der Organisation während des gesamten Prozesses einzuhalten.
  • Wenn wir nicht über genügend Sicherheitsressourcen verfügen, können wir uns in die entgegengesetzte Richtung bewegen. Anstatt Mitglieder des Sicherheitsteams in unsere funktionsübergreifenden Teams zu bringen, können wir Mitglieder der funktionsübergreifenden Teams in das Sicherheitsteam bringen. Jedes funktionsübergreifende Team würde einen oder zwei Sicherheitsbeauftragte benennen. Die Vertreter würden sich regelmäßig mit der Sicherheit treffen und über die Sicherheitsanforderungen und -standards der Organisation auf dem Laufenden gehalten werden. Sie dürfen selbst keine Sicherheitsexperten sein. Sie können jedoch die Rolle eines Sicherheitsingenieurs übernehmen und sicherstellen, dass ihre Teams die Sicherheitspraktiken des Unternehmens einhalten.

Gilden

Dies fügt sich in ein anderes Organisationsmuster ein, das an Zugkraft gewonnen hat: Gilden. Das Gildenmodell entstand aus dem funktionsübergreifenden Teammodell. Diese Teams sind von Natur aus mit Mitgliedern besetzt, die sich auf verschiedene Funktionen spezialisiert haben. Noch, Es macht oft Sinn, dass sich Leute, die sich auf eine bestimmte Funktion spezialisiert haben, auch treffen; zum Beispiel, zu:

  • Verbessern Sie ihre Fähigkeiten und lernen Sie voneinander
  • Entdecken und etablieren Sie Best Practices für ihre jeweilige Funktion
  • Erstellen Sie je nach Funktion Unternehmensstandards und -anforderungen

Unsere letzte Sicherheitslösung bildete effektiv eine „Sicherheitsgilde“. Die Teammitglieder arbeiteten hauptsächlich mit ihren vertikalen Teams; Aber in regelmäßigen Abständen trafen sich einige von ihnen mit der Sicherheits- „Gilde“, um die Sicherheitspraktiken und -standards der Organisation zu besprechen.

Das Gildenmodell funktioniert auch bei der Softwarearchitektur besonders gut. Insbesondere bei einer Microservices-Architektur ist ein gewisses Maß an organisationsweiter technischer Governance erforderlich. Eine Gruppe von Architekten, die in einem metaphorischen Elfenbeinturm sitzen und Regeln an Teams verteilen, ist jedoch im Allgemeinen kontraproduktiv. Stattdessen können sich leitende Ingenieure aus unseren funktionsübergreifenden Teams regelmäßig in einer Architekturgilde treffen. Dort können sie Probleme von ihren Teams ansprechen, Lösungen erarbeiten und Muster und Standards festlegen.

Beispiele für vertikale funktionsübergreifende Teams, ergänzt durch horizontale Gilden

Gilden können auch auf fast alle anderen Funktionen erweitert werden. Schließlich möchten wir, dass unsere Designer einen gemeinsamen UI-Styleguide entwickeln und daraus arbeiten. Wir möchten, dass unsere Frontend-Ingenieure die gleichen UI-Elemente verwenden. QA-Ingenieure sollten sich daran orientieren, wie Tests in unseren Organisationen durchgeführt werden. Und Produktmanager sollten mit der gesamten Produkt-Roadmap des Unternehmens synchronisiert sein.

Bringen Sie alles zusammen

Der Wechsel zu Microservices kann die Produktivität unserer Teams dramatisch verbessern. Aber wir müssen verstehen, wie wir die Vorteile einer Microservices-Architektur nutzen können, um dorthin zu gelangen. Von allen Entwurfsmustern und Konzepten, die sich auf Microservices beziehen, ist der begrenzte Kontext wohl der wichtigste, um uns dieses Verständnis zu vermitteln.

Mit einem soliden Verständnis des begrenzten Kontextes verstehen wir, dass:

  • Unsere Organisationsstruktur und unsere technische Architektur gehen Hand in Hand
  • Unsere produktorientierten Teams sollten minimale Abhängigkeiten von anderen Teams haben, genauso wie die Systeme, die sie bauen, von anderen Systemen entkoppelt sein sollten

Im Allgemeinen bringt die Einbeziehung des begrenzten Kontexts die Denkweise mit sich, dass wir mit unserer Microservices-Architektur erfolgreich sein müssen. Stellen Sie sicher, dass Sie dieses wichtige Muster verstehen, bevor Sie sich auf Ihre Microservices-Reise begeben!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.