Why We Chose a Monorepo
March 15, 2026

Managing a game with 17 domains, multiple rendering systems, and thousands of TypeScript files requires solid architecture. We use Turborepo with npm workspaces to maintain clean package boundaries.
The Problem
Early on, Adventure Craft was a single monolithic app. Everything lived in one src/ directory — combat logic next to UI components next to world generation algorithms. The problems were predictable:
- Circular dependencies everywhere
- Build times growing linearly with codebase size
- Testing required spinning up the entire app
- Contributing meant understanding the whole system
The Solution: Package Boundaries
We split the codebase into focused packages with strict dependency rules:
- packages/core — foundation, no internal deps
- packages/rpg — depends on core only
- packages/engine — depends on core only
- packages/engine-core — depends on core only
- packages/ui — depends on core only
- apps/game-client — depends on everything above
The key insight is that dependencies flow in one direction. The rpg package never imports from engine, and engine never imports from rpg. Shared types live in core. This eliminates circular dependencies by design.
Turborepo Benefits
Turborepo gives us three critical capabilities:
- Incremental builds — only rebuild packages that actually changed
- Remote caching — CI builds share cache, so unchanged packages are instant
- Parallel execution — independent packages build simultaneously
A full build from scratch takes about 45 seconds. With cache hits, most builds complete in under 5 seconds.
Domain Architecture
Within the game client, each feature lives in its own domain folder under src/domains/. Domains like combat, creative, quests, multiplayer, and arena are self-contained — they can import from @adventure-craft/* packages but never from each other directly. Cross-domain communication happens through Zustand stores and event systems.
Results
Since the migration:
- Build times dropped from 2+ minutes to 5 seconds (cached)
- New contributors can work on a single package without understanding the whole system
- Test suites run in isolation and complete in seconds
- Zero circular dependency issues in 6 months
The upfront cost of restructuring was significant, but the ongoing velocity improvement has been well worth it.