Micro Frontends mit Vue: Strategien für große Teams
AI generated
<v/>
{ }
Vue.js · Architektur · Skalierung · Teams
Micro Frontends mit Vue
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.

19 Min. Lesezeit Runtime-Integration · Shared State · Deployment Vue 3 · Vite · Nuxt

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.

11. FAQ: Micro Frontends mit Vue

1Was ist ein Micro Frontend bei Vue?
Eine unabhängig deploybare Vue-Anwendung, die zusammen mit anderen eine Gesamtoberfläche bildet. Löst vor allem organisatorische Probleme großer Teams.
2Ab welcher Teamgröße lohnt es sich?
Meist ab drei oder mehr eigenständigen Teams am selben Frontend. Bei kleineren Teams überwiegt der Integrationsoverhead.
3Custom Elements oder Module Federation?
Custom Elements für kleinere, lose gekoppelte Module. Module Federation für größere Bereiche mit geteilten Abhängigkeiten.
4Wie teilt man Zustand zwischen Modulen?
Über einen minimalen Event-Bus mit CustomEvents. Eine gemeinsame Pinia-Instanz verletzt die Deployment-Unabhängigkeit.
5Wie bleibt das Design konsistent?
Zentrales, eigenständig versioniertes Design-System-Paket, kombiniert mit CSS-Isolation über Scoped Styles oder Shadow DOM.
6Wie geht man mit Routing um?
Entweder ein orchestrierender Shell-Router oder echte Seitenwechsel für Domänengrenzen mit voller Reaktivität nur innerhalb einer Domäne.
7Größtes Risiko bei geteilter Vue-Laufzeit?
Versionsinkompatibilitäten zwischen Host und Remote ohne definierten Kompatibilitätsvertrag.
8Verschlechtert das die Performance?
Bei unbedachter Umsetzung ja. Mit geteilten Dependencies oder Server-Side-Composition bleibt der Overhead gering.
9Braucht jedes Modul eine eigene Pipeline?
Ja, das ist der Kern des Nutzens. Nur so kann ein Team unabhängig von anderen releasen.
10Wann darauf verzichten?
Bei nur einem Team oder zu stark verzahnten fachlichen Domänen, bei denen der Integrationsoverhead den Nutzen übersteigt.