Start / Seminare / Angular Grundlagen & Moderne Patterns
Modul
Monorepo & Micro-Frontends
5 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Angular — Monorepo & Micro-Frontends
0:00 Ab hier verlassen wir das Gebiet, in dem Architektur eine Frage des Codes ist. Die beiden Muster in diesem Modul lösen keine technischen Probleme, sondern organisatorische: Teams, die sich gegenseitig blockieren, Prioritäten, die nicht zusammenpassen, Freigaben, die aufeinander warten. Das ist wichtig zu wissen, bevor man anfängt — denn beide Muster kosten Komplexität, und wer sie ohne den passenden Schmerz einführt, bezahlt den Preis ohne den Gegenwert.
0:26 Wir schauen uns an, welche Kopplung das Monorepo löst, welche es übrig lässt, und was Micro-Frontends daran ändern.
Skalierung über Projektgrenzen
0:33 Fünf Kapitel, die die Eskalationsleiter aus dem vorigen Modul fortsetzen. Zuerst das Monorepo: mehrere eigenständige Projekte in einem Repository, von Angulars Kommandozeile direkt unterstützt. Dann der ehrliche Blick darauf, was damit tatsächlich entkoppelt ist — und was nicht. Aus dieser Lücke ergibt sich das dritte Thema: Micro-Frontends.
0:54 Wir setzen ein solches Projekt auf, schauen uns eine laufende Anwendung an und gehen zuletzt den Schritt zu vollständig getrennten Repositories. Jede Stufe löst genau eine Kopplung, die die vorige übrig gelassen hat.
Monorepo mit mehreren Projekten
1:08 Der Anlass für diese Stufe ist selten die Codemenge. Er ist fast immer sozial: Zwei Teams arbeiten in derselben Anwendung, wollen aber unterschiedliche Dinge — und stehen sich dabei im Weg. Schauen wir uns an, wie sich das entzerren lässt, ohne gleich getrennte Welten zu bauen. Und das ist ein wichtiger Punkt: Nicht die Menge an Code führt zu dieser Stufe, sondern die Menge an Menschen, die daran arbeiten.
1:32 Ein Monorepo hält mehrere eigenständige Projekte in einem Repository. Das Bemerkenswerte daran: Angulars Kommandozeile kann das von Haus aus. Sie legen einen Arbeitsbereich ohne Anwendung an und generieren anschließend so viele Projekte hinein, wie Sie brauchen — jedes mit eigener TypeScript-Konfiguration, eigenem Einstiegspunkt und eigener Anwendungskonfiguration.
1:53 Der Unterschied zum Feature-Shell ist entscheidend: Beim Feature-Shell teilen sich die Bereiche weiterhin den Start der Anwendung. Hier hat jedes Projekt seinen eigenen. Genau das ist es, was die Teams voneinander löst. Der zweite Punkt auf dieser Folie ist der eigentliche Auslöser, und er ist es wert, ihn konkret zu machen.
2:12 Stellen Sie sich vor, ein Team möchte auf Zoneless umstellen, das andere hat gerade andere Prioritäten. Solange sich beide dieselbe Anwendungskonfiguration teilen, kann das erste Team nicht handeln — oder es handelt und verursacht Fehler im Code des zweiten. Mit getrennten Projekten löst sich das auf. Dazu kommen deutlich weniger Merge-Konflikte und die Möglichkeit, jedes Projekt einzeln zu bauen und auszuliefern.
2:36 Das klingt nach viel, und für viele Teams ist es genau die richtige Stufe. Das Bemerkenswerte an diesen Befehlen ist, wie unspektakulär sie sind. Kein zusätzliches Werkzeug, keine fremde Bibliothek — das ist alles Angular. Der erste Befehl legt einen leeren Arbeitsbereich an, und das Wesentliche steht in der Option: keine Anwendung erzeugen. Danach generieren Sie die Projekte einzeln hinein.
3:00 Der letzte Befehl ist der interessanteste: Eine Bibliothek ist kein lauffähiges Projekt, sondern ein Bündel aus Komponenten und Diensten, das die anderen importieren. Dort landet, was alle brauchen — der Kopfbereich der Seite etwa, oder gemeinsame Formularbausteine. Die rechte Spalte ist die gute Nachricht, die linke die eigentliche Botschaft.
3:21 Was jedes Projekt für sich hat, ist die Konfiguration seines Starts — und damit die Freiheit, technische Entscheidungen selbst zu treffen. Was geteilt bleibt, ist die Paketverwaltung. Und das ist der Punkt, an dem diese Stufe an ihre Grenze kommt: Eine gemeinsame Paketdatei heißt gemeinsame Versionen. Wenn ein Team eine Bibliothek aktualisieren will und ein anderes noch nicht kann, warten beide.
3:44 Das ist mal ein Vorteil — eine Stelle für Angular-Updates ist bequem — und mal genau die Blockade, die man loswerden wollte. Der erste Punkt ist ein Missverständnis, das ich oft höre: Das Monorepo ersetzt das Feature-Shell-Muster nicht. Innerhalb eines Projekts im Monorepo gliedern Sie weiterhin nach Fachbereichen — die Muster liegen auf verschiedenen Ebenen und ergänzen sich.
4:06 Der zweite Punkt ist eine Kleinigkeit, die beim ersten Mal irritiert: Befehle brauchen jetzt einen Projektnamen. Der dritte ist der inhaltliche: Trennen Sie nicht, was fachlich zusammengehört. Und der vierte ist die Erwartung, um die es auf der nächsten Folie geht — getrennte Projekte sind noch keine getrennten Freigaben.
Das Monorepo-Muster in der Praxis
4:25 Jetzt der ehrliche Teil. Wir schauen uns an, was im Alltag eines Monorepos tatsächlich funktioniert — und wo die Grenze verläuft. Diese Grenze ist der Grund, warum es das nächste Kapitel überhaupt gibt. Denn getrennte Ordner und getrennte Build-Befehle erzeugen ein Gefühl von Unabhängigkeit, das an einer entscheidenden Stelle täuscht.
4:45 Wo genau, sehen wir gleich — und es ist der Grund für alles, was danach kommt. Im Alltag arbeiten Sie projektweise: Ein Befehl startet genau ein Projekt, ein anderer baut genau eines und legt sein Ergebnis in einen eigenen Ordner. Das funktioniert und fühlt sich richtig getrennt an. Gleichzeitig können Projekte weiterhin voneinander importieren — die Kasse im Shop-Projekt kann eine Adressauswahl aus dem Konto-Projekt verwenden.
5:10 Das ist praktisch und beabsichtigt. Es ist aber auch der Punkt, an dem eine Abhängigkeit entsteht, die man beim Bauen einlöst und nicht erst beim Laufen. Merken Sie sich das Wort Bauzeit — gleich wird es wichtig. Diese fünf Schritte beschreiben den Alltag, und der ist angenehm unaufgeregt. Starten, bauen, ausliefern — jeweils projektweise.
5:31 Interessant sind die Schritte drei und vier: Was mehrere Projekte brauchen, gehört in die Bibliothek. Und projektübergreifende Importe setzt man bewusst, nicht nebenbei. Genau hier lohnt sich Sheriff aus dem vorigen Modul ein zweites Mal — die Etiketten funktionieren im Arbeitsbereich genauso wie innerhalb eines Projekts.
5:50 Ohne diese Absicherung wächst zwischen Projekten dieselbe stille Kopplung, die Sie zwischen Feature-Shells schon einmal verhindert haben. Diese vier Punkte sind der Ertrag, und er ist beachtlich. Jedes Team besitzt sein Projekt. Die Konfigurationen sind getrennt, also ist auch der Streit über technische Grundsatzfragen entschärft.
6:10 Merge-Konflikte werden seltener, weil man sich seltener in denselben Dateien trifft. Und ein Punkt, der leicht untergeht: die Paketkonsistenz. Eine einzige Stelle, an der Angular aktualisiert wird, ist für die Wartung ein echter Vorteil — vor allem, wenn man die Alternative kennt, bei der jedes Team seine eigene Version pflegt.
6:30 Für viele Organisationen ist das Monorepo deshalb der richtige Endpunkt. Der erste Punkt ist der wichtigste des ganzen Moduls, deshalb steht er hervorgehoben. Die Adressauswahl aus dem Konto-Projekt wird in die Kasse des Shop-Projekts hineinkompiliert. Wenn das Konto-Team sie ändert und ausliefert, passiert auf der Kassenseite — nichts. Erst wenn auch das Shop-Team neu baut und ausliefert, wird die Änderung sichtbar.
6:55 Ein Team hängt also von der Auslieferung eines anderen ab. Der dritte Punkt ist der, der in regulierten Umgebungen entscheidet: Alle sehen allen Code. Wer Zahlungen verarbeitet und Auflagen erfüllen muss, kommt damit nicht weit. Diese Aufgabe ist ein Experiment, und ihr Ergebnis ist ein Nicht-Ereignis. Bauen und liefern Sie ein Projekt aus.
7:16 Ändern Sie dann etwas im Nachbarprojekt, das vom ersten benutzt wird, und liefern Sie es ebenfalls aus. Und dann schauen Sie hin: Es passiert nichts. Geschafft haben Sie es, wenn Sie benennen können, welcher Build fehlt, damit die Änderung ankommt. Ich empfehle diese Übung ausdrücklich, weil sie einen abstrakten Satz — Bauzeit-Abhängigkeit — in eine Erfahrung verwandelt.
7:38 Und weil sie die beste Begründung liefert, wenn im Team die Frage nach Micro-Frontends aufkommt.
Ein Micro-Frontend-Projekt aufsetzen
7:44 Damit sind wir an der Stelle, an der Micro-Frontends ihren Sinn bekommen. Eine Vorbemerkung: Das Angular-Team unterstützt dieses Muster nicht mit eigenen Werkzeugen. Es rät aber auch nicht davon ab — der Ansatz, den wir hier zeigen, wurde sogar im Angular-Blog vorgestellt. Sie arbeiten also mit einer Fremdbibliothek, und das sollten Sie wissen.
8:05 Und es lohnt sich, das im Team offen anzusprechen, bevor man anfängt. Die Idee ist überraschend einfach: Mehrere eigenständig gebaute und ausgelieferte Anwendungen erscheinen dem Nutzer als ein einziges Produkt. Ein Host bildet die Schale — Kopfbereich, Navigation, Umschalter zwischen den Anwendungen. Die Remotes liefern Routen und Komponenten zu.
8:25 Der Vergleich, der mir dazu einfällt, ist ein Einkaufszentrum: Von außen ein Gebäude mit einem Namen, innen lauter eigenständige Geschäfte, die selbst entscheiden, wann sie umbauen. Der Kunde merkt davon nichts — er geht durch eine Tür und ist im nächsten Laden. Der erste Punkt ist der, der alles trägt: Jedes Team liefert aus, wann es fertig ist. Nicht wenn das Nachbarteam neu baut, nicht wenn der gemeinsame Freigabetermin kommt.
8:51 Ich habe Umgebungen erlebt, in denen mehrere Teams mehrmals täglich in Produktion gingen — das verändert die Arbeitsweise grundlegend, weil eine fertige Funktion nicht mehr wochenlang wartet. Der dritte Punkt ist der, der in regulierten Branchen den Ausschlag gibt: Ein Bereich mit Auflagen lässt sich vollständig abschotten.
9:09 Und der vierte macht es unterscheidbar vom bloßen Aufteilen: Mehrere Projekte tragen zur selben Seite bei. Der erste Schritt bringt den neuen Baustein: das Host-Projekt, die Schale. Die Schritte zwei bis vier sind derselbe Befehl mit unterschiedlichen Argumenten — einmal als Host, dreimal als Remote, jedes mit eigenem Port.
9:28 Wichtig ist, was dieser Befehl nebenbei tut: Er installiert nicht nur ein Paket, sondern führt auch eine Codegenerierung aus, die die Bau-Konfiguration umstellt. Der fünfte Schritt ist der inhaltliche und der einzige, der Nachdenken verlangt: Was gibt jedes Remote nach außen? Das ist eine Schnittstellenentscheidung, und sie hält länger als der Rest.
9:49 Achten Sie hier vor allem auf die Ports. Jedes Projekt bekommt einen eigenen und läuft eigenständig unter einer eigenen Adresse — das ist keine Konfigurationsdeko, sondern der Kern des Ganzen. Der Host findet seine Remotes nämlich nicht über Importe, sondern über URLs. Und genau daraus folgt später die Freiheit, die Projekte in getrennte Repositories zu legen.
10:11 Ein Hinweis aus der Praxis: Nach dem Einrichten stehen in der Host-Konfiguration Standardports, die selten zu den vergebenen passen. Diese Zeilen von Hand nachzuziehen ist der erste Schritt, der tatsächlich manuell anfällt. Genau der eben erwähnte Punkt steht hier an erster Stelle, weil er jeden einmal erwischt. Der zweite ist der Unterschied zum Monorepo, an den man sich gewöhnen muss: Läuft ein Remote nicht, findet der Host es nicht — Sie brauchen also mehrere laufende Prozesse, und das will im Entwicklungsalltag organisiert sein.
10:41 Der dritte ist die grundsätzliche Warnung, die ich in diesem Modul nicht oft genug wiederholen kann. Und der vierte ist eine Erwartung, die man früh korrigieren sollte: Es gibt hier keine eingebaute Unterstützung, auf die man sich bei Angular-Updates verlassen kann.
Eine laufende Micro-Frontend-Anwendung
10:56 Schauen wir uns an, wie so eine Anwendung im Betrieb funktioniert. Es sind im Grunde nur zwei Mechanismen, und wenn Sie die verstanden haben, verstehen Sie das ganze Muster: exponierte Routen und das Nachladen einzelner Komponenten zur Laufzeit. Und die gute Nachricht dabei: Was für Sie als Entwicklerin oder Entwickler sichtbar bleibt, sind ganz normale Angular-Routen und ganz normale Komponenten.
11:18 Die Federation arbeitet darunter. Jedes Remote legt in einer Konfigurationsdatei fest, was es nach außen gibt. Typischerweise sind das zwei Dinge: seine Routen, damit der Host dorthin navigieren kann, und einzelne Komponenten, die anderswo eingebunden werden sollen. Der Host lädt beides zur Laufzeit nach. Das Wesentliche steckt in diesem letzten Wort.
11:39 Beim Monorepo wurde die fremde Komponente hineinkompiliert — hier wird sie geholt, wenn sie gebraucht wird. Deshalb genügt eine neue Auslieferung des Remotes, damit die Änderung überall ankommt. Genau diese Kopplung war das Problem, das wir loswerden wollten. Diese fünf Bausteine bilden eine Kette, und der Satz in der Fußzeile ist der Schlüssel: Der Host kennt seine Remotes nur über Adressen, nicht über Importe.
12:05 Die Konfigurationsdatei sagt, was ein Remote hergibt. Die Einstiegsadresse sagt dem Konsumenten, wo er das findet. Die Ladefunktion holt es. Und die Routendatei des Hosts hängt es ein. Der Menü-Export ist dabei der Baustein, den man beim ersten Entwurf gerne vergisst — aber irgendwoher muss die Navigation ja wissen, welche Seiten ein Remote überhaupt anbietet.
12:27 Was hier passiert, ist im Kern eine Schleife über eine Liste von Remotes: Für jedes wird ein Pfad angelegt und darunter werden die Routen eingehängt, die dieses Remote anbietet. Der wichtigste Hinweis steht in der Fußzeile und ist eine typische Fehlerquelle: Der Bezeichner, unter dem das Remote seine Routen anbietet, und der Bezeichner, mit dem der Host sie abruft, müssen identisch sein.
12:48 Sie sind Zeichenketten, nicht typgeprüft. Ein Tippfehler fällt deshalb nicht beim Bauen auf, sondern erst, wenn jemand auf den Menüpunkt klickt und nichts passiert. Die ersten beiden Schritte sind das Grundmuster, das wir gerade gesehen haben. Schritt drei ist der, der die Navigation zusammenhält: Jedes Remote sagt selbst, welche Menüpunkte es beisteuert — sonst müsste der Host die Seiten aller Remotes kennen, und die Entkopplung wäre dahin.
13:13 Schritt vier zeigt, wie diese Komponenten im Host landen: dynamisch gerendert, statt fest im Template verdrahtet. Und Schritt fünf ist die praktische Fußangel, an die man sich gewöhnt: Was zur Laufzeit geholt wird, kommt als Versprechen an und braucht im Template den entsprechenden Pipe. Die ersten beiden Punkte sind die, die im Alltag Zeit kosten, und beide haben dieselbe Ursache: Die Verbindung zwischen Host und Remote ist zur Bauzeit nicht prüfbar.
13:39 Was sonst der Compiler findet, finden Sie hier im Browser. Der dritte Punkt ist eine Entwurfsfrage, die früh auftaucht: Nicht jedes Remote gehört in den Anwendungsumschalter — die Kontoverwaltung etwa gehört gefühlt zum Shop, auch wenn sie technisch eigenständig ist. Und der vierte ist die Kehrseite der Laufzeitfreiheit: Ein nicht laufendes Remote merken Sie erst, wenn jemand dorthin klickt.
Micro-Frontends mit getrennten Repositories
14:01 Die letzte Stufe. Und die überraschende Nachricht vorweg: Technisch ist sie fast ein Nicht-Ereignis. Weil der Host seine Remotes ohnehin nur über Adressen kennt, ist es ihm gleichgültig, aus welchem Repository sie kommen. Der Schritt ist also weniger eine technische Änderung als eine organisatorische. Was sich ändert, sind Zuständigkeiten, Zugriffsrechte und die Frage, wer wann welche Paketversion aktualisiert.
14:26 Jedes Projekt bekommt sein eigenes Repository, seine eigene Paketverwaltung, seine eigene Konfiguration. Für die Federation ändert sich nichts — sie interessiert sich nur dafür, dass unter einer bestimmten Adresse etwas erreichbar ist, wenn es gebraucht wird. Das ist eine bemerkenswerte Eigenschaft, wenn man sie mit dem Aufwand vergleicht, den derselbe Schritt im Monorepo bedeuten würde.
14:48 Und es ist zugleich die Antwort auf die Frage, die am Ende des Monorepo-Kapitels offengeblieben ist: Die gemeinsame Paketdatei, die alle Teams aneinandergebunden hat, gibt es hier nicht mehr. Der zweite Punkt ist der, der die letzte offene Kopplung löst: Jedes Team aktualisiert seine Pakete nach eigener Priorität. Kein Warten mehr darauf, dass alle bereit sind für die nächste Angular-Version.
15:10 Der dritte Punkt ist für regulierte Umgebungen der eigentliche Grund, diesen Schritt überhaupt zu gehen — ein Bereich mit Auflagen ist vollständig abgeschottet, inklusive Zugriffsrechten auf den Quellcode. Und der vierte Punkt ist das, was das Ganze zusammenhält: Für den Menschen vor dem Bildschirm bleibt es eine Anwendung. Er merkt nicht, dass er zwischen vier Repositories wandert.
15:32 Diese Schritte sind unspektakulär, und das ist der Punkt: Repository je Projekt, Abhängigkeiten je Repository installieren, jedes auf seinem Port starten, Adressen im Host eintragen. Der einzige Schritt, der wirklich Aufmerksamkeit verlangt, ist der fünfte. Wenn zwei Projekte unterschiedliche Angular-Versionen verwenden, muss der Browser beide herunterladen — und die Anwendung wird für den Nutzer schwerer.
15:56 Die Federation kann mit verschiedenen Versionen umgehen, das ist ausdrücklich vorgesehen. Aber jede Abweichung kostet Bandbreite. Es lohnt sich, das im Blick zu behalten, auch wenn niemand mehr dazu verpflichtet ist. Der erste Punkt ist die notwendige Ernüchterung. „Völlig unabhängig" ist ein Versprechen, das dieses Muster nicht halten kann — alle Teams bauen weiter an einem gemeinsamen Produkt, teilen sich Daten, eine gemeinsame Schale und die Erwartungen der Nutzer an ein einheitliches Erscheinungsbild.
16:25 Der zweite und dritte Punkt sind die technische Seite derselben Medaille: Autonomie bei Paketversionen ist ein Gewinn und wächst dem Nutzer gleichzeitig auf die Ladezeit. Und der vierte fasst es zusammen: Der Koordinationsaufwand verschwindet nicht, er wandert vom Code in Absprachen. Diese Aufgabe schließt beide Architekturmodule ab, und sie ist die schwierigste. Nehmen Sie ein eigenes Projekt und entscheiden Sie, welche Stufe angemessen ist.
16:50 Geschafft haben Sie es, wenn Sie eine Empfehlung haben und dazu einen benannten Schmerzpunkt. Nicht „wäre schöner", sondern: Wo hat es in den letzten Monaten konkret geknirscht? Und der Hinweis ist der wichtigste Satz auf dieser Folie: Wenn Sie keinen Schmerzpunkt benennen können, lautet die richtige Antwort, die aktuelle Stufe zu behalten.
17:10 Das ist kein Ausweichen, das ist das Ergebnis. Vier Sätze, die den Weg zusammenfassen. Das Monorepo entkoppelt Teams beim Entwickeln — beim Ausliefern bleiben sie aneinandergebunden, weil fremder Code hineinkompiliert wird. Genau diese Deploy-Kopplung und die Frage nach abgeschotteten Bereichen sind die beiden Gründe, die zu Micro-Frontends führen.
17:30 Technisch tragen die dann erstaunlich wenig Zeremonie: Der Host kennt seine Remotes über Adressen, und ob die aus einem oder aus vier Repositories kommen, ist ihm gleich. Und der letzte Punkt ist die Ehrlichkeit, die dazugehört — vollständige Autonomie gibt es nicht, sie verlagert sich nur.
Fragen & Weiterarbeiten
17:47 Damit ist der Architekturteil abgeschlossen. Im nächsten Modul geht es wieder näher an den Code: State Management — der Weg von den klassischen RxJS-Stores zu Signal Stores und, wenn es größer wird, zu NgRx. Interessanterweise gilt dort dieselbe Logik wie hier: mit der einfachsten Lösung anfangen und erst wechseln, wenn sie weh tut.
18:06 Wenn Sie sich in Native Federation vertiefen möchten, ist die npm-Seite des Pakets ein brauchbarer Einstieg — sie ist knapper und aktueller als das meiste, was man sonst dazu findet.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →