Vom Monorepo zum Multi-Repo (oder zurück): Die richtige Entscheidung für Enterprise Teams
Ein Entscheidungsframework für die Wahl zwischen Monorepo- und Multi-Repo-Strategien in Enterprise-Umgebungen, einschließlich Tooling-Vergleich, CI/CD-Auswirkungen, Hybrid-Patterns und praktischer Migrationsleitfaden.
Die Monorepo-Debatte erzeugt mehr Meinungen als Evidenz. Befürworter verweisen auf Google, Meta und Microsoft als Beweis, dass Monorepos skalieren. Kritiker verweisen auf dieselben Unternehmen und merken an, dass sie Custom Tooling im Millionenwert gebaut haben, um es zum Laufen zu bringen. Die Wahrheit ist, wie immer, dass die richtige Antwort von Ihrer Organisation abhängt.
Dieser Beitrag liefert ein Entscheidungsframework basierend auf realen Enterprise-Migrationsprojekten. Wir haben Teams beim Wechsel von Multi-Repo zu Monorepo, von Monorepo zu Multi-Repo und — am häufigsten — zu einem Hybrid-Pattern geholfen, das das Beste aus beiden Welten vereint.
Monorepo-Vorteile: Was Sie tatsächlich bekommen
Atomare Änderungen über Services hinweg
Dies ist der primäre Vorteil und derjenige, der die gesamte Komplexität rechtfertigt. Wenn sich eine Shared Library ändert, aktualisieren Sie jeden Consumer im selben Pull Request. Keine koordinierten Multi-Repo-Releases. Keine "Library v2.3.1 deployen, dann Service A aktualisieren, dann Service B aktualisieren"-Choreografie.
# Monorepo atomare Änderung — ein einzelner PR
packages/shared-auth/src/token.ts # Library-Änderung
services/api-gateway/src/middleware.ts # Consumer-Update
services/user-service/src/auth.ts # Consumer-Update
services/order-service/src/auth.ts # Consumer-UpdateIn einem Multi-Repo-Setup erfordert dieselbe Änderung vier Pull Requests, vier CI-Läufe und sorgfältige Reihenfolge.
Geteiltes Tooling und Standards
Ein Monorepo erzwingt Konsistenz. ESLint Config, TypeScript-Einstellungen, Docker Base Images, CI/CD-Templates — alles liegt an einem Ort. Wenn Sie eine Linting-Regel aktualisieren, übernimmt jedes Projekt sie.
monorepo/
├── .eslintrc.js # Eine Konfiguration für alle
├── tsconfig.base.json # Geteilte TypeScript-Einstellungen
├── Dockerfile.base # Geteiltes Base Image
├── nx.json # Build-Orchestrierung
├── packages/
│ ├── shared-auth/
│ ├── shared-logging/
│ └── shared-models/
├── services/
│ ├── api-gateway/
│ ├── user-service/
│ └── order-service/
└── infrastructure/
├── terraform/
└── helm/Projektübergreifende Sichtbarkeit
Jeder Entwickler kann jeden Service sehen. Sie können den Code lesen, die Architektur verstehen und Beispiele finden, wie eine Shared Library verwendet wird. Das klingt trivial, aber in einer Organisation mit 200 Entwicklern und 50 Repositories ist es tatsächlich schwierig herauszufinden, wie andere Teams ein Problem gelöst haben.
Multi-Repo-Vorteile: Was Sie tatsächlich bekommen
Team-Autonomie
Jedes Team besitzt sein Repository. Sie wählen ihre Branching-Strategie, ihre CI/CD-Pipeline-Struktur, ihren Release-Rhythmus. Keine Koordination mit anderen Teams für Builds, Merges oder Deployments.
Zugriffskontrolle
In regulierten Umgebungen muss bestimmter Code nur für bestimmte Teams zugänglich sein. Multi-Repo macht dies einfach: Repository-Berechtigungen setzen. In einem Monorepo benötigen Sie CODEOWNERS-Dateien und sorgfältige pfadbasierte Zugriffskontrolle, die die meisten Git-Hosting-Plattformen nur unvollkommen unterstützen.
Build-Isolation
Ein fehlgeschlagener Test in einem Repository blockiert nicht das Deployment eines anderen Teams. In einem Monorepo kann ein fehlgeschlagener Test in einem Shared Package jedes Team blockieren, bis er behoben ist. Monorepo-Tooling mildert dies durch Affected-Project-Erkennung ab, aber es ist nie so sauber wie echte Isolation.
Einfacheres CI/CD
Jedes Repository hat seine eigene Pipeline. Die Pipeline weiß genau, was zu bauen, zu testen und zu deployen ist. Keine Build-Graph-Analyse, keine Affected-Project-Erkennung, kein Cache Management. Diese Einfachheit ist wichtig für Teams ohne dedizierte Platform Engineers.
Das Hybrid-Pattern
Die meisten Unternehmen landen hier. Das Pattern:
Monorepo für eng gekoppelte Services. Ein Bounded Context mit 3-5 Services, die Modelle teilen, zusammen deployt werden und von einem einzelnen Team oder eng zusammenarbeitenden Teams betreut werden.
Separate Repositories für unabhängige Produkte. Eine Zahlungsplattform, ein Kundenportal und ein internes Tool bekommen jeweils ihr eigenes Repository (oder ihr eigenes Monorepo).
Shared Libraries in einem dedizierten Repository. Veröffentlicht als Packages in einer privaten Registry (Azure Artifacts, GitHub Packages, npm private). Versioniert mit Semantic Versioning.
Organisationsstruktur:
├── payment-platform/ # Monorepo (4 Services, 1 Team)
│ ├── services/
│ │ ├── payment-api/
│ │ ├── payment-processor/
│ │ ├── payment-reconciler/
│ │ └── payment-gateway/
│ └── packages/
│ ├── payment-models/
│ └── payment-utils/
├── customer-portal/ # Monorepo (3 Services, 2 Teams)
│ ├── services/
│ │ ├── portal-bff/
│ │ ├── portal-web/
│ │ └── portal-api/
│ └── packages/
│ └── portal-components/
├── shared-libraries/ # Multi-Repo (veröffentlichte Packages)
│ ├── auth-library/
│ ├── logging-library/
│ └── http-client/
└── infrastructure/ # Separates Repo (Platform Team)
├── terraform-modules/
├── helm-charts/
└── policy-definitions/Tooling-Vergleich
Nx
Am besten für: TypeScript/JavaScript Monorepos mit 5-50 Projekten. Starke Ökosystem-Integration (React, Angular, Next.js, Node.js).
// nx.json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"test": {
"cache": true
},
"lint": {
"cache": true
}
},
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": ["default", "!{projectRoot}/**/*.spec.ts"]
}
}Stärken:
- Computation Caching (lokal und remote über Nx Cloud)
- Affected-Befehl — nur bauen/testen, was sich geändert hat
- Code-Generatoren für das Scaffolding neuer Projekte
- Dependency Graph-Visualisierung
- Plugin-Ökosystem für gängige Frameworks
Schwächen:
- Aufwendige Konfiguration für Nicht-JavaScript-Projekte
- Nx Cloud erforderlich für Remote Caching (kostenpflichtig für große Teams)
- Lernkurve für das Plugin-System
Turborepo
Am besten für: Einfachere TypeScript/JavaScript Monorepos, bei denen Sie Caching ohne aufwendige Konfiguration wünschen.
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
},
"lint": {
"outputs": []
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"outputs": []
}
}
}Stärken:
- Minimale Konfiguration
- Schnelles Remote Caching über Vercel oder Self-Hosted
- Inkrementelle Builds mit Content-Aware Hashing
- Einfaches Konzeptmodell
Schwächen:
- Weniger ausgereift als Nx für komplexe Dependency Graphs
- Weniger Code-Generierungsfunktionen
- Primär auf JavaScript/TypeScript fokussiert
Bazel
Am besten für: Große polyglotte Monorepos (500+ Projekte, mehrere Sprachen). Enterprise Teams mit dedizierter Build-Infrastruktur.
# BUILD.bazel
load("@rules_dotnet//dotnet:defs.bzl", "csharp_library", "csharp_test")
csharp_library(
name = "payment-models",
srcs = glob(["src/**/*.cs"]),
deps = [
"//packages/shared-models",
"@nuget//Newtonsoft.Json",
],
visibility = ["//services/payment:__subpackages__"],
)
csharp_test(
name = "payment-models-tests",
srcs = glob(["tests/**/*.cs"]),
deps = [
":payment-models",
"@nuget//xunit",
"@nuget//xunit.runner.visualstudio",
],
)Stärken:
- Hermetische Builds (garantierte Reproduzierbarkeit)
- Sprachunabhängig (Java, C#, Go, Python, TypeScript, C++)
- Remote Execution (Builds auf einen Cluster verteilen)
- Feingranulares Dependency Tracking auf Dateiebene
Schwächen:
- Steile Lernkurve (Starlark Build-Sprache)
- Erhebliche Infrastruktur-Investition (Remote Execution Service)
- Community Rulesets variieren in der Qualität
- Überdimensioniert für Teams unter 100 Entwicklern
CI/CD-Auswirkungen
Die Repository-Strategie formt grundlegend Ihre CI/CD-Architektur.
Monorepo CI/CD
Die Herausforderung besteht darin, "alles bei jedem Commit bauen" zu vermeiden. Sie brauchen Affected-Project-Erkennung:
# GitHub Actions — Nx Affected Builds
name: CI
on:
pull_request:
branches: [main]
jobs:
affected:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
- run: npm ci
- id: set-matrix
run: |
AFFECTED=$(npx nx show projects --affected --base=origin/main --head=HEAD --json)
echo "matrix=$AFFECTED" >> $GITHUB_OUTPUT
build:
needs: affected
runs-on: ubuntu-latest
strategy:
matrix:
project: ${{ fromJson(needs.affected.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- run: npx nx build ${{ matrix.project }}
- run: npx nx test ${{ matrix.project }}# Azure DevOps — pfadbasierte Triggers
trigger:
branches:
include: [main]
paths:
include:
- services/payment-api/**
- packages/shared-models/**
# Diese Pipeline läuft nur, wenn payment-api oder seine Dependencies sich ändernMulti-Repo CI/CD
Jedes Repository hat eine einfache, unabhängige Pipeline:
# azure-pipelines.yml — pro Repository
trigger:
branches:
include: [main]
stages:
- stage: Build
jobs:
- job: BuildAndTest
steps:
- script: dotnet build
- script: dotnet test
- script: docker build -t payment-api .
- stage: Deploy
jobs:
- deployment: DeployToAKS
environment: productionDie Einfachheit ist überzeugend. Aber die Koordination von Änderungen über mehrere Repos erfordert eine Shared-Library-Versionierungsstrategie:
# Dependency-Update-Workflow
# 1. Änderung in shared-auth Library mergen
# 2. Library CI veröffentlicht shared-auth@2.3.1 in Azure Artifacts
# 3. Dependabot/Renovate erkennt neue Version in Consumer-Repos
# 4. Automatisierte PRs aktualisieren Package-Referenzen
# 5. Consumer CI validiert Kompatibilität
# 6. Teams mergen und deployen unabhängigDas funktioniert, fügt aber Latenz hinzu. Ein Breaking Change in einer Shared Library braucht Stunden oder Tage für die Propagierung, verglichen mit Minuten in einem Monorepo.
Entscheidungsframework
Verwenden Sie dieses Framework als Leitfaden für Ihre Entscheidung. Bewerten Sie jede Dimension für Ihre Organisation:
Monorepo wählen, wenn:
- Team-Kopplung hoch ist — Teams ändern häufig den Code anderer Teams oder teilen Modelle
- Deployment-Koordination schmerzhaft ist — Sie verbringen erhebliche Zeit mit der Orchestrierung von Multi-Service-Releases
- Konsistenz wichtig ist — Regulatorische oder Compliance-Anforderungen verlangen einheitliches Tooling und Standards
- Sie Platform-Engineering-Kapazität haben — Jemand kann das Monorepo-Tooling betreuen (Nx, Bazel, CI/CD)
- Ihre Codebase hauptsächlich eine Sprache verwendet — Nx und Turborepo funktionieren am besten im JavaScript/TypeScript-Ökosystem
Multi-Repo wählen, wenn:
- Team-Autonomie kritisch ist — Teams haben unterschiedliche Tech Stacks, Release-Rhythmen oder Compliance-Anforderungen
- Zugriffskontrolle nicht verhandelbar ist — Regulatorische Anforderungen verlangen strikte Code-Zugriffsgrenzen
- Ihr CI/CD einfach ist — Jeder Service baut und deployt unabhängig ohne Cross-Dependencies
- Kein Platform Team — Sie haben nicht die Kapazität, Monorepo-Tooling zu warten
- Teams geografisch verteilt sind — Remote-Teams profitieren von kleineren, schnelleren Clones und fokussiertem Code Review
Hybrid wählen, wenn:
- Sie Bounded Contexts haben — Gruppen von 3-5 Services, die innerhalb der Gruppe eng, aber zwischen den Gruppen lose gekoppelt sind
- Gemischte Tech Stacks — Einige Teams nutzen .NET, andere TypeScript, andere Python
- Wachsende Organisation — Sie haben mit Multi-Repo begonnen und bestimmte Teams stoßen an Koordinationsgrenzen, aber nicht alle
- Schrittweise Migration — Sie möchten sich inkrementell Richtung Monorepo bewegen, ohne eine Big-Bang-Umstrukturierung
Migrationsüberlegungen
Multi-Repo zu Monorepo
# Historie bewahren beim Zusammenführen von Repos
# Für jedes Repo Pfade in Zielverzeichnis umschreiben
git clone https://github.com/org/payment-api.git
cd payment-api
git filter-repo --to-subdirectory-filter services/payment-api
# Im Monorepo als Remote hinzufügen und mergen
cd ../monorepo
git remote add payment-api ../payment-api
git fetch payment-api
git merge payment-api/main --allow-unrelated-histories
git remote remove payment-apiRisiko: CI/CD-Unterbrechung während der Migration. Mildern Sie dies, indem Sie beide Pipelines (alte Per-Repo und neue Monorepo) 2 Wochen lang parallel betreiben.
Monorepo zu Multi-Repo
# Service mit vollständiger Historie extrahieren
git clone https://github.com/org/monorepo.git service-extract
cd service-extract
git filter-repo --path services/payment-api/ --path packages/payment-models/
# Dies erstellt ein neues Repo nur mit der relevanten HistorieRisiko: Beschädigung von Shared-Library-Referenzen. Mildern Sie dies, indem Sie Shared Libraries als Packages veröffentlichen, bevor Sie Services extrahieren.
Performance im großen Maßstab
Große Monorepos stoßen an Performance-Grenzen. Hier ist, was Sie erwarten können und wie Sie es mildern:
| Größe | Herausforderung | Abhilfe |
|---|---|---|
| 50 Projekte | Keine | Standard Git funktioniert einwandfrei |
| 100 Projekte | Langsames CI (alles bauen) | Nx/Turborepo Affected-Erkennung |
| 500 Projekte | Langsames Clone, großer Working Tree | Shallow Clones, Sparse Checkout |
| 1000+ Projekte | Git Performance-Grenzen | VFS for Git, Bazel Remote Execution |
# Sparse Checkout — nur auschecken, was Sie brauchen
git clone --no-checkout --filter=blob:none https://github.com/org/monorepo.git
cd monorepo
git sparse-checkout init --cone
git sparse-checkout set services/payment-api packages/shared-models
git checkout mainFazit
Die Repository-Strategie ist eine Infrastrukturentscheidung, keine religiöse. Monorepos optimieren für Koordination und Konsistenz. Multi-Repos optimieren für Autonomie und Einfachheit. Hybride tauschen etwas Optimierung gegen Flexibilität ein.
Beginnen Sie mit dem Entscheidungsframework. Bewerten Sie Ihre Organisation ehrlich in Bezug auf Kopplung, Compliance, Platform-Kapazität und Teamstruktur. Die Antwort wird offensichtlich sein, sobald Sie aufhören, es als binäre Entscheidung zu behandeln.
Wenn Sie Hilfe bei der Evaluierung Ihrer Repository-Strategie oder der Planung einer Migration zwischen Monorepo und Multi-Repo benötigen, kontaktieren Sie uns unter mbrahim@conceptualise.de. Wir haben Enterprise Teams in beide Richtungen dieser Migration begleitet und können Ihnen helfen, die häufigsten Fallstricke zu vermeiden.
Themen