Zum Hauptinhalt springen
Alle Beiträge
DevSecOps10 Min. Lesezeit

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.

Veröffentlicht

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.

Code
# 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-Update

In 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.

Code
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.

Code
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).

JSON
// 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.

JSON
// 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.

Python
# 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:

YAML
# 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 }}
YAML
# 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 ändern

Multi-Repo CI/CD

Jedes Repository hat eine einfache, unabhängige Pipeline:

YAML
# 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: production

Die Einfachheit ist überzeugend. Aber die Koordination von Änderungen über mehrere Repos erfordert eine Shared-Library-Versionierungsstrategie:

YAML
# 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ängig

Das 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

Loading diagram...
Loading diagram...

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

Bash
# 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-api

Risiko: 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

Bash
# 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 Historie

Risiko: 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ößeHerausforderungAbhilfe
50 ProjekteKeineStandard Git funktioniert einwandfrei
100 ProjekteLangsames CI (alles bauen)Nx/Turborepo Affected-Erkennung
500 ProjekteLangsames Clone, großer Working TreeShallow Clones, Sparse Checkout
1000+ ProjekteGit Performance-GrenzenVFS for Git, Bazel Remote Execution
Bash
# 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 main

Fazit

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

Monorepo vs Multi-Repo EnterpriseNx Turborepo Bazel VergleichMonorepo CI CD PipelineRepository-Strategie EnterpriseMonorepo Migrationsleitfaden

Häufig gestellte Fragen

Nein. Ein Monorepo ist eine Repository-Strategie, kein Architekturmuster. Sie können ein Monorepo haben, das Dutzende von unabhängig deploybare Microservices, Shared Libraries und Infrastructure-Code enthält. Der Code in einem Monorepo kann hochgradig modular sein. Ein Monolith ist eine Architektur, bei der alle Funktionalität als einzelne Einheit deployt wird. Sie können einen Monolithen in einem Multi-Repo-Setup und Microservices in einem Monorepo haben.

Expert engagement

Brauchen Sie Expertenberatung?

Unser Team ist spezialisiert auf Cloud-Architektur, Security, KI-Plattformen und DevSecOps. Lassen Sie uns besprechen, wie wir Ihrem Unternehmen helfen können.

Kontakt aufnehmenNo commitment · No sales pressure

Verwandte Artikel

Alle Beiträge