Strategien für große Teams und unabhängige Deployments
Sobald mehrere Teams an derselben Vue-Anwendung arbeiten, wird ein monolithisches Frontend zum Flaschenhals. Micro Frontends teilen eine Vue-Anwendung entlang von Domänengrenzen auf, sodass jedes Team unabhängig entwickeln, testen und deployen kann, ohne auf einen gemeinsamen Release-Zug warten zu müssen.
Inhaltsverzeichnis
- 1. Warum Micro Frontends bei Vue-Projekten entstehen
- 2. Domänen schneiden statt technische Schichten
- 3. Integrationsstrategien im Vergleich
- 4. Runtime-Integration mit Custom Elements
- 5. Shared State zwischen Micro Frontends
- 6. Routing und Navigation über Grenzen hinweg
- 7. Design-Konsistenz ohne enge Kopplung
- 8. Deployment-Pipelines pro Micro Frontend
- 9. Strategien im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Micro Frontends bei Vue-Projekten entstehen
Ein Micro Frontend ist keine Vue-spezifische Technik, sondern ein Architekturmuster, das ein großes Frontend in unabhängig deploybare Einheiten zerlegt. Bei Vue-Projekten entsteht der Druck dafür meist erst ab einer bestimmten Teamgröße: Sobald drei oder mehr Teams am selben Single-Page-Application-Repository arbeiten, wachsen Merge-Konflikte, gegenseitige Blockaden bei Releases und die Kopplung zwischen Modulen, die eigentlich nichts miteinander zu tun haben. Ein Micro Frontend mit Vue löst genau dieses organisatorische Problem, nicht in erster Linie ein technisches.
Der entscheidende Denkfehler bei der Einführung von Micro Frontends mit Vue ist, die Aufteilung rein technisch zu betreiben, etwa nach Seiten oder Komponenten-Typen. Erfolgreiche Aufteilungen orientieren sich an Team- und Domänengrenzen: Das Team, das den Checkout betreut, besitzt das Checkout-Micro-Frontend vollständig, inklusive Deployment, Monitoring und Fehlerbehebung. Diese Eigentümerschaft ist der eigentliche Gewinn, den Micro Frontends mit Vue liefern, unabhängig davon, welche konkrete Integrationstechnik am Ende gewählt wird.
Wichtig ist auch die Abgrenzung zum reinen Monorepo mit mehreren Vue-Apps. Ein Monorepo kann Code teilen und trotzdem als ein einziges Deployment ausgeliefert werden. Ein echtes Micro Frontend Setup erlaubt es, dass ein Team am Montag deployt und ein anderes Team erst am Donnerstag, ohne dass sich die beiden Releases gegenseitig beeinflussen. Diese Entkopplung der Deployment-Zyklen ist das Kernversprechen, an dem sich jede Micro-Frontend-Entscheidung mit Vue messen lassen muss.
2. Domänen schneiden statt technische Schichten
Der erste praktische Schritt bei jedem Micro Frontend mit Vue ist der Schnitt entlang von Geschäftsdomänen, nicht entlang technischer Schichten. Statt ein Micro Frontend für "alle Formulare" und eines für "alle Listen" zu bauen, schneidet man nach fachlichen Bereichen: Produktkatalog, Warenkorb, Checkout, Kundenkonto. Jede Domäne bekommt eine eigene Vue-Anwendung mit eigenem Router, eigenem Store und eigenem Deployment-Zyklus.
Die Domänengrenzen bei einem Micro Frontend orientieren sich idealerweise an denselben Grenzen, die auch im Backend als Bounded Context existieren, sofern eine Domain-Driven-Design-Struktur vorliegt. Das reduziert die Übersetzungsarbeit zwischen Frontend-Team und Backend-Team erheblich, weil beide Seiten über dieselben fachlichen Begriffe sprechen. Ein Vue-Micro-Frontend für den Checkout kommuniziert dann typischerweise mit genau den Backend-Services, die auch fachlich zum Checkout gehören, statt querbeet über mehrere Bounded Contexts hinweg Daten zu aggregieren.
Ein häufiger Fehler beim Domänenschnitt für Vue Micro Frontends ist es, zu fein zu schneiden. Jedes zusätzliche Micro Frontend bringt Integrationsoverhead: ein eigenes Build, ein eigenes Deployment, eigene Monitoring-Dashboards. Als Faustregel gilt, dass die Anzahl der Micro Frontends grob der Anzahl der eigenständigen Teams entsprechen sollte, nicht der Anzahl der Seiten oder Features. Ein Team von drei Entwicklern profitiert selten davon, fünf separate Micro Frontends zu pflegen.
3. Integrationsstrategien im Vergleich
Für die technische Zusammenführung mehrerer Vue-Anwendungen zu einer Gesamtoberfläche gibt es im Kern drei etablierte Strategien: Build-Time-Integration über npm-Pakete, Server-Side-Composition über Edge-Includes und Runtime-Integration im Browser. Jede dieser Strategien hat unterschiedliche Auswirkungen auf Deployment-Unabhängigkeit, Performance und Komplexität, und die Wahl bestimmt maßgeblich, wie gut das Micro Frontend Setup langfristig skaliert.
Build-Time-Integration bedeutet, dass ein Vue-Modul als versioniertes npm-Paket veröffentlicht und von einer Host-Anwendung zur Build-Zeit eingebunden wird. Das ist einfach umzusetzen, verletzt aber das Prinzip der unabhängigen Deployments: Jede Änderung erfordert einen neuen Build der Host-Anwendung. Runtime-Integration hingegen lädt Vue-Micro-Frontends erst im Browser nach, typischerweise über Module Federation oder Custom Elements, wodurch jedes Team seinen eigenen Bundle unabhängig deployen kann, ohne dass die Host-Anwendung neu gebaut werden muss.
// vite.config.js — Runtime integration via dynamic import of a remote entry
// This host app loads independently deployed Vue micro frontends at runtime
export default defineConfig({
build: {
rollupOptions: {
// Host does NOT bundle the remote — only references it
external: [],
},
},
});
// host/src/loadRemote.js
export async function loadRemoteMicroFrontend(remoteUrl, exposedModule) {
// Each remote publishes a manifest with its own version and entry point
const manifestResponse = await fetch(`${remoteUrl}/manifest.json`);
const manifest = await manifestResponse.json();
const script = document.createElement('script');
script.type = 'module';
script.src = manifest.entries[exposedModule];
document.head.appendChild(script);
return new Promise((resolve, reject) => {
script.onload = () => resolve(window.__microFrontends[exposedModule]);
script.onerror = reject;
});
}
Server-Side-Composition ist ein dritter Weg, der besonders bei Nuxt-Projekten praktikabel ist: Ein Edge-Layer, etwa über Nuxt Server Routes oder ein Reverse Proxy, setzt HTML-Fragmente mehrerer Vue-Anwendungen zu einer Seite zusammen, bevor sie den Browser erreicht. Das verbessert die initiale Ladezeit gegenüber reiner Client-Side-Runtime-Integration, erhöht aber die Komplexität auf Infrastrukturebene, weil jedes Fragment serverseitig erreichbar sein muss.
4. Runtime-Integration mit Custom Elements
Eine der robustesten Techniken für Micro Frontends mit Vue ist die Kompilierung einzelner Vue-Komponenten zu nativen Custom Elements über defineCustomElement. Das Ergebnis ist ein Standard-Web-Component, das framework-agnostisch in jede Host-Anwendung eingebunden werden kann, unabhängig davon, ob diese selbst mit Vue, React oder reinem HTML arbeitet. Für Vue-Micro-Frontends bedeutet das: Jedes Team liefert sein Modul als Custom Element aus, und die Host-Anwendung muss nichts über die interne Implementierung wissen.
Der Vorteil gegenüber Module Federation liegt in der geringeren Kopplung an ein spezifisches Bundler-Ökosystem und der nativen Browser-Unterstützung ohne zusätzliches Runtime-Framework. Der Nachteil: Styles müssen bewusst über Shadow DOM oder CSS-Variablen isoliert werden, und die Kommunikation zwischen Custom Elements läuft über Attribute, Properties und Custom Events statt über direkte Vue-Reaktivität.
// checkout-widget.js — Compile a Vue component into a native Custom Element
import { defineCustomElement } from 'vue';
import CheckoutSummary from './CheckoutSummary.vue';
const CheckoutSummaryElement = defineCustomElement(CheckoutSummary, {
shadowRoot: true, // isolate styles from the host page
});
customElements.define('checkout-summary', CheckoutSummaryElement);
// Host application — framework agnostic, no Vue dependency needed
// <checkout-summary order-id="4711" currency="EUR"></checkout-summary>
// Inside CheckoutSummary.vue — communicating outward via native CustomEvent
export default {
props: ['orderId', 'currency'],
emits: ['checkout-completed'],
methods: {
completeCheckout() {
// defineCustomElement maps Vue emits to native CustomEvents automatically
this.$emit('checkout-completed', { orderId: this.orderId });
},
},
};
5. Shared State zwischen Micro Frontends
Geteilter Zustand ist die schwierigste Herausforderung bei Micro Frontends mit Vue, weil jedes Modul im Idealfall unabhängig deploybar bleiben soll, gleichzeitig aber Informationen wie den eingeloggten Nutzer oder den Warenkorb-Inhalt über Modulgrenzen hinweg benötigt. Eine globale Pinia-Instanz über alle Micro Frontends hinweg zu teilen, widerspricht dem Grundprinzip der Unabhängigkeit, weil dann alle Module gegen dieselbe Store-Version kompiliert werden müssten.
Die robustere Lösung sind Custom Events auf dem window-Objekt oder ein minimaler, versionsstabiler Event-Bus, der als eigenes, sehr klein gehaltenes Paket verteilt wird. Jedes Vue-Micro-Frontend abonniert die Events, die es interessieren, und veröffentlicht eigene Events, ohne die interne Store-Struktur der anderen Module zu kennen. Für serverseitig gehaltenen Zustand, etwa Nutzer-Session oder Feature-Flags, empfiehlt sich ein zentraler Endpunkt, den jedes Modul unabhängig abfragt, statt den Zustand clientseitig zwischen Modulen zu synchronisieren.
// shared/eventBus.js — Minimal, framework-agnostic contract between micro frontends
// Published as its own tiny versioned package — never the full Pinia store
export const MICRO_FRONTEND_EVENTS = {
CART_UPDATED: 'mf:cart-updated',
USER_LOGGED_IN: 'mf:user-logged-in',
};
export function publishEvent(eventName, detail) {
window.dispatchEvent(new CustomEvent(eventName, { detail }));
}
export function subscribeEvent(eventName, handler) {
window.addEventListener(eventName, handler);
return () => window.removeEventListener(eventName, handler);
}
// Inside the cart micro frontend, after a successful add-to-cart mutation
publishEvent(MICRO_FRONTEND_EVENTS.CART_UPDATED, { itemCount: 3, total: 89.90 });
// Inside the header micro frontend, listening without knowing cart internals
import { onMounted, onUnmounted, ref } from 'vue';
const itemCount = ref(0);
let unsubscribe;
onMounted(() => {
unsubscribe = subscribeEvent(MICRO_FRONTEND_EVENTS.CART_UPDATED, (e) => {
itemCount.value = e.detail.itemCount;
});
});
onUnmounted(() => unsubscribe());
6. Routing und Navigation über Grenzen hinweg
Navigation zwischen mehreren unabhängig deployten Vue-Anwendungen erfordert eine klare Entscheidung: entweder einen Shell-Router, der die einzelnen Micro Frontends orchestriert, oder vollständige Browser-Navigation zwischen separaten Seiten. Der Shell-Router-Ansatz hält die Single-Page-Application-Erfahrung aufrecht, verlangt aber, dass die Shell weiß, welches Micro Frontend für welche Route zuständig ist, und erhöht damit wieder die Kopplung zwischen Shell und Modulen.
Die pragmatischere Variante für viele Vue-Micro-Frontend-Projekte ist es, echte Seitenwechsel für Domänengrenzen zu akzeptieren, etwa vom Produktkatalog zum Checkout, und nur innerhalb einer Domäne die volle Vue-Router-Reaktivität zu nutzen. Der gefühlte Performance-Unterschied ist bei gutem Preloading und geteiltem Design-System gering, während die Entkopplung der Router-Konfigurationen erheblich zur Team-Autonomie beiträgt.
// shell/src/router.js — Shell router delegates unknown prefixes to remote micro frontends
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/catalog/:pathMatch(.*)*', component: () => loadRemoteView('catalog') },
{ path: '/checkout/:pathMatch(.*)*', component: () => loadRemoteView('checkout') },
{ path: '/account/:pathMatch(.*)*', component: () => loadRemoteView('account') },
],
});
// Each remote view is loaded lazily and mounted with its own internal router
async function loadRemoteView(domain) {
const { default: RemoteApp } = await import(/* @vite-ignore */ `${domain}/RemoteEntry.js`);
return RemoteApp; // owns its own nested vue-router instance internally
}
export default router;
7. Design-Konsistenz ohne enge Kopplung
Ohne Gegenmaßnahmen driften unabhängig entwickelte Vue-Micro-Frontends optisch auseinander, weil jedes Team eigene Buttons, Abstände und Farbtöne implementiert. Die Lösung ist ein gemeinsames Design-System, ausgeliefert als eigenständiges npm-Paket mit Vue-Komponenten und Design-Tokens, das von allen Micro Frontends als Abhängigkeit eingebunden wird. Wichtig ist, dass dieses Paket eine eigene, unabhängige Versionierung besitzt, damit Micro Frontends nicht gezwungen sind, bei jedem Design-System-Update sofort mitzuziehen.
CSS-Isolation ist der zweite Baustein: Scoped Styles innerhalb jeder Vue-Komponente, kombiniert mit CSS-Custom-Properties für Theming-Werte wie Primärfarbe oder Schriftgröße, verhindern, dass ein Micro Frontend versehentlich Styles eines anderen überschreibt. Bei Custom-Element-basierter Integration übernimmt Shadow DOM diese Isolation automatisch, bei reiner Runtime-Integration ohne Shadow DOM ist eine CSS-Namenskonvention mit eindeutigen Präfixen pro Micro Frontend Pflicht.
8. Deployment-Pipelines pro Micro Frontend
Der eigentliche Business-Nutzen von Micro Frontends mit Vue zeigt sich erst in der Deployment-Pipeline: Jedes Team betreibt eine eigene CI/CD-Pipeline, die unabhängig von den Pipelines anderer Teams läuft. Ein typischer Aufbau baut das Vue-Modul, führt Unit- und Integrationstests aus, veröffentlicht das Bundle in einen versionierten Storage-Bucket oder eine CDN-Adresse und aktualisiert anschließend einen Import-Map- oder Manifest-Eintrag, den die Host-Anwendung zur Laufzeit ausliest.
Ein häufig unterschätztes Risiko ist die Versionsverträglichkeit zwischen Micro Frontends und der geteilten Vue-Laufzeit. Wird Vue selbst als geteilte Abhängigkeit über Module Federation eingebunden, muss ein Kompatibilitätsvertrag definiert werden, welche Major-Versionen von Vue zwischen Host und Remote kompatibel sind. Fehlt dieser Vertrag, kann ein isoliert getestetes Micro Frontend in Produktion an einer inkompatiblen geteilten Vue-Version scheitern, ein klassisches Integrationsrisiko, das nur durch Contract-Tests zwischen den Pipelines zuverlässig abgefangen wird.
#!/usr/bin/env bash
# ci/deploy-micro-frontend.sh — Independent pipeline per Vue micro frontend
set -euo pipefail
readonly MODULE_NAME="checkout"
readonly BUILD_DIR="dist"
readonly BUCKET="s3://mf-modules/${MODULE_NAME}"
readonly VERSION="$(git rev-parse --short HEAD)"
npm run build
npm run test:unit
npm run test:contract -- --against=shared-vue-runtime@3
# Upload the versioned bundle, never overwrite an existing version
aws s3 cp "$BUILD_DIR" "${BUCKET}/${VERSION}" --recursive
# Update the manifest that the shell reads at runtime — atomic, single file
cat > manifest.json <<JSON
{ "module": "${MODULE_NAME}", "version": "${VERSION}", "entry": "${BUCKET}/${VERSION}/remoteEntry.js" }
JSON
aws s3 cp manifest.json "${BUCKET}/manifest.json"
echo "[OK] Deployed ${MODULE_NAME}@${VERSION} independently"
9. Strategien im direkten Vergleich
Die Wahl der richtigen Integrationsstrategie für Micro Frontends mit Vue hängt stark vom Reifegrad der Organisation und der Anzahl beteiligter Teams ab. Die folgende Übersicht vergleicht die gängigsten Ansätze entlang der Kriterien Deployment-Unabhängigkeit, Performance und operativer Komplexität.
| Strategie | Deployment-Unabhängigkeit | Performance | Komplexität |
|---|---|---|---|
| npm-Paket (Build-Time) | Gering | Sehr gut | Niedrig |
| Custom Elements | Hoch | Gut | Mittel |
| Module Federation | Hoch | Gut, geteilte Deps | Hoch |
| Server-Side-Composition | Hoch | Sehr gut (SSR) | Hoch (Infra) |
| iframe-Isolation | Sehr hoch | Schwach | Niedrig |
Für die meisten Vue-Organisationen mit drei bis acht Teams ist eine Kombination aus Custom Elements für kleinere, eingebettete Module und Module Federation für größere, eigenständige Bereiche wie den Checkout ein praxistauglicher Mittelweg. iframe-Isolation bleibt Spezialfällen mit maximalen Sicherheitsanforderungen vorbehalten, etwa bei der Einbindung von Drittanbieter-Widgets, bei denen vollständige Isolation wichtiger ist als nahtlose Integration.
Mironsoft
Vue-Architektur, Micro Frontends und skalierbare Frontend-Teams
Mehrere Teams, eine Vue-Anwendung, unabhängige Releases?
Wir analysieren eure bestehende Vue-Codebasis, schneiden Domänengrenzen entlang eurer Teamstruktur und implementieren eine Micro-Frontend-Architektur, mit der jedes Team eigenständig deployen kann.
Architektur-Review
Analyse eurer Vue-Codebasis und Vorschlag für Domänenschnitt
Integration
Custom Elements oder Module Federation produktionsreif einführen
CI/CD-Aufbau
Unabhängige Deployment-Pipelines pro Micro Frontend einrichten
10. Zusammenfassung
Micro Frontends mit Vue lösen primär ein organisatorisches Problem: unabhängige Teams sollen unabhängig deployen können, ohne sich gegenseitig zu blockieren. Der Domänenschnitt sollte an Teamstruktur und Bounded Contexts ausgerichtet werden, nicht an technischen Kategorien. Custom Elements über defineCustomElement bieten eine framework-agnostische, gering gekoppelte Integrationsoption, während Module Federation für größere, eigenständige Bereiche mit geteilten Abhängigkeiten geeignet ist.
Geteilter Zustand läuft am robustesten über einen minimalen, versionsstabilen Event-Bus statt über eine gemeinsame Store-Instanz. Ein zentrales Design-System-Paket hält die optische Konsistenz zwischen unabhängig entwickelten Modulen. Der entscheidende Erfolgsfaktor bleibt am Ende jedoch nicht die Technik, sondern die konsequente Trennung der Deployment-Pipelines, denn nur diese schafft die tatsächliche Team-Autonomie, die Micro Frontends versprechen.
Micro Frontends mit Vue — Das Wichtigste auf einen Blick
Domänenschnitt
Aufteilung nach Geschäftsdomänen und Teamstruktur, nicht nach technischen Schichten oder Seitenanzahl.
Integration
Custom Elements für lose Kopplung, Module Federation für größere Bereiche mit geteilten Abhängigkeiten.
Shared State
Minimaler Event-Bus statt geteilter Pinia-Instanz, damit Module unabhängig versioniert bleiben.
Deployment
Eigene CI/CD-Pipeline pro Micro Frontend, Contract-Tests gegen die geteilte Vue-Laufzeit.