Why NextWhy Next
Zur Übersicht
💻 Entwicklung3 Min. Lesezeit

5 Apps in einem Repository - Das Turborepo-Monorepo eines Solo-Entwicklers

Wie man mehrere Apps und Websites in einem einzigen Repository mit Turborepo verwaltet. Gemeinsame Konfiguration teilen, mit dem Cache Builds sparen und pro App deployen, aus der Praxis erklärt.

Das Wichtigste

Wer allein mehrere Apps baut, dessen Konfiguration zerstreut sich. Mit einem Turborepo-Monorepo teilt man eslint und tsconfig über packages, der turbo-Cache baut unveränderte Apps nicht neu, und mit --filter deployt man pro App.

Auf dieser Seite

Wer allein Apps baut, sammelt Repositories wie Staub. Eine App, eine Website, noch eine App... und irgendwann kopiert man die eslint-Konfiguration zum fünften Mal. Ich habe das alles in ein einziges Monorepo zusammengeführt.

Das echte Problem ist die verstreute Konfiguration

Bei vielen Repositories schmerzt nicht der Code am meisten, sondern die Konfiguration.

  • eslint, prettier und tsconfig in jedes Repository kopieren
  • Eine Regel ändern heißt: alle Repositories der Reihe nach anfassen
  • Gemeinsame UI und Utilities sind zu klein, um sie auf npm zu veröffentlichen

Ein Monorepo dreht das um: Alles wird an einer Stelle geteilt.

Die Grundstruktur

apps/
├── web/      # 랜딩
├── blog/     # 이 블로그
├── wave/     # 오늘의 파도
└── ...
packages/
├── eslint-config/       # 공용 eslint
└── typescript-config/   # 공용 tsconfig

apps/* sind die tatsächlichen Deploy-Artefakte, packages/* enthält die von allen Apps geteilte Konfiguration und den gemeinsamen Code. Die tsconfig.json jeder App erbt einfach nur.

{ "extends": "@repo/typescript-config/nextjs.json" }

Regeln werden jetzt nur noch an einer Stelle in packages geändert.

Der turbo-Cache ist der Kern

Die eigentliche Waffe von Turborepo ist der Cache. Ändern sich die Inputs eines Tasks (Quellcode, Konfiguration) nicht, wird das vorherige Ergebnis direkt wiederverwendet.

turbo run build            # 전체 빌드(바뀐 것만 실제로 돎)
turbo run build --filter=blog   # blog 앱만

In turbo.json deklariert man nur die Abhängigkeiten zwischen Tasks und die Outputs.

{
  "tasks": {
    "build": { "dependsOn": ["^build"], "outputs": [".next/**"] }
  }
}

Ab dem zweiten Build heißt es "FULL TURBO", und alles ist in wenigen Sekunden fertig.

Pro App deployen

Auch mit einem einzigen Repository bleibt das Deployment pro App getrennt. Bei Docker schneidet turbo prune nur den Teilbaum der jeweiligen App heraus und hält das Image schlank.

turbo prune blog --docker   # blog + 내부 의존성만 남긴 out/ 생성

Auch dieser Blog wurde so zugeschnitten und läuft per Docker auf einem Mini-PC. Diese Geschichte erzähle ich im Beitrag zum Self-Hosting.

Fazit

  • apps/* = Deploy-Artefakte, packages/* = geteilte Konfiguration
  • tsconfig und eslint per Vererbung an einer Stelle pflegen
  • Der turbo-Cache baut unveränderte Apps nicht neu
  • Mit --filter und prune pro App deployen

Wer allein mehrere Produkte betreibt, für den ist ein Monorepo kein Luxus, sondern ein Überlebenswerkzeug.

Häufige Fragen

Ab wie vielen Apps lohnt sich ein Monorepo?

Schon ab 2 Apps lohnt es sich, sobald gemeinsame Konfiguration (eslint, tsconfig, UI) entsteht. Der Kern: Einstellungen an einer Stelle ändern und alle Apps übernehmen sie.

Werden die Builds dadurch nicht langsamer?

Im Gegenteil, sie werden schneller. turbo cached die Ergebnisse von Tasks, deren Inputs sich nicht geändert haben, und baut nur die geänderten Apps neu.

Ähnliche Beiträge