Monorepo Structure with Turborepo
Context and Problem Statement​
CellixJS is a complex Domain-Driven Design (DDD) application built on Azure Functions with multiple interconnected packages including domain models, API layers, persistence, GraphQL, and UI components. Initially, the codebase was organized in a way that made it difficult to manage dependencies, optimize builds, and maintain consistent development workflows across packages. We needed a scalable approach to:
- Manage shared code and dependencies across multiple packages
- Optimize build and test performance
- Maintain consistent tooling and configurations
- Enable efficient CI/CD pipelines
- Support both frontend and backend development workflows
The challenge was to choose between maintaining separate repositories or consolidating into a monorepo, and selecting appropriate tooling for build orchestration and caching.
Decision Drivers​
- Build Performance: Need for fast incremental builds and efficient caching
- Dependency Management: Shared code and consistent dependency versions across packages
- Developer Experience: Unified development environment and tooling
- CI/CD Efficiency: Selective builds and caching in automated pipelines
- Code Organization: Clear separation between application and library packages, with logical grouping
- Scalability: Ability to add new packages without architectural overhead
Considered Options​
- NPM Workspaces: Leverage NPM workspaces with typescript project references. Each package in
packages/directory - Monorepo with Turborepo: Focused build orchestration and caching tool. Repository structure following turborepo conventions
Decision Outcome​
We will use a monorepo structure with Turborepo for build orchestration, caching, and task management. The repository is organized with:
packages/directory containing shared libraries and domain-specific packagesapps/directory containing deployable applications- Turborepo configuration for selective builds and dependency management
- npm workspaces for package management
See the following Turborepo documentation for reference.
Consequences​
Positive​
- Improved Build Performance: Turborepo's intelligent caching reduces build times by 50-80% for unchanged packages
- Selective Execution: Only affected packages and their dependents are built/tested
- Unified Development Environment: Single repository with consistent tooling and configurations
- Efficient CI/CD: Azure Pipelines integration with selective builds and remote caching
- Better Dependency Management: npm workspaces ensure consistent dependency versions
- Clear Code Organization: Logical separation between domain, application, and infrastructure concerns
Negative​
- Increased Repository Size: All packages in one repository can lead to larger clones
- Complex Initial Setup: Turborepo configuration requires careful task dependency management
- Learning Curve: Team needs to understand Turborepo concepts and selective execution
- Potential for Tighter Coupling: Monorepo can encourage tighter coupling between packages
Validation​
The monorepo structure and Turborepo usage is validated through:
- Build Performance Metrics: Local builds complete in ~160ms for cached packages
- CI/CD Pipeline Efficiency: PR builds are 30-60% faster due to selective execution
- Code Quality Gates: All packages maintain consistent linting, testing, and coverage standards
- Dependency Resolution: npm workspaces ensure no version conflicts between packages
Pros and Cons of the Options​
NPM Workspaces​
- Good: Built-in to npm, no additional tooling required
- Good: Simple setup with package.json workspaces field
- Neutral: Basic support for linking local packages
- Bad: No advanced build orchestration or caching
- Bad: Limited to package management only
Monorepo with Turborepo​
- Good: Excellent build performance with intelligent caching
- Good: Simple configuration and focused on build orchestration
- Good: Seamless CI/CD integration with remote caching
- Good: Fast and reliable for our package structure
- Neutral: Less code generation features compared to Nx
- Bad: Limited to build/task orchestration (no code generation)
Repository Structure​
The monorepo follows this structure:
packages/ # Shared libraries
├── cellix/ # Shared seedwork libraries for CellixJS applications
│ ├── domain-seedwork/ # Domain modeling base classes
│ ├── typescript-config/ # Shared TypeScript configurations
│ ├── vitest-config/ # Shared testing configurations
│ └── mock-* # Mocking utilities for local development
│ └── ui-core/ # Shared UI components
│ └── ...
├── ocom/ # Application-specific packages (OwnerCommunity)
│ ├── domain/ # Domain models and business logic
│ ├── graphql/ # GraphQL schema and resolvers
│ ├── persistence/ # Data access layer
│ └── service-* # Infrastructure services
| └── ...
apps/ # Deployable applications
├── api/ # Azure Functions application (Backend)
├── docs/ # Documentation site (Docusaurus)
└── ui-community/ # React UI application (Frontend)
Turborepo Configuration​
Task Dependencies​
Turborepo manages task execution with dependency graphs:
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "build/**"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
Selective Execution​
- Local Development:
turbo run buildbuilds only affected packages - CI/CD: Azure Pipelines detects changes and builds selectively
- Caching: Local
.turbodirectory and remote Azure Cache@2
Package Categories​
Packages are categorized by tags for selective deployments:
- frontend: UI applications and components
- backend: API, domain, and infrastructure packages
- mock: Local development utilities
- config: Shared configurations
- docs: Documentation
Task Graph Optimization​
Overview​
Turborepo task graphs can accumulate unnecessary transitive dependencies over time, increasing
build times. The turbo-graph-optimization agent skill automates analysis and optimization
(located in .agents/skills/turbo-graph-optimization/SKILL.md, with symlink at
.github/skills/turbo-graph-optimization).
The skill autonomously discovers build targets, analyzes task graphs, proposes safe optimizations (verifying via static import analysis), flags risky changes for human review, and verifies the build succeeds.
When to Optimize​
- New package added pulling in unnecessary upstream tasks
- Build slowdown investigation (task graph bloat)
- Periodic hygiene (quarterly or semi-annually)
- Pre-release verification
- CI/CD optimization
Current CellixJS build targets: @apps/api#build, @apps/ui-community#build, @apps/docs#build
Safety Approach​
- Static import analysis verifies removed tasks aren't consumed
- Build verification runs
turbo run buildto confirm success - Flagging (not applying) changes unverifiable via static analysis (dynamic imports, conditional requires, runtime dependencies)
- Before/after tables document results with task breakdown and percentages
See ADR-0020 for CI change detection notes — task graph updates may affect change detection in Azure Pipelines.
CellixJS Task Graph Analysis (April 2026)​
Baseline Analysis — Turborepo 2.9.3 with turbo query --dry=json:
Per-target task counts:
- @apps/api#build: 26 tasks (24 build, 1 gen, 1 audit)
- @apps/ui-community#build: 7 tasks (5 build, 1 gen, 1 audit)
- @apps/docs#build: 5 tasks (3 build, 1 gen, 1 audit)
Full monorepo build (turbo run build):
- Total tasks: 36 (34 build, 1 gen, 1 audit)
Optimization Scan Results:
✓ No unnecessary transitive dependencies found
✓ Task graph is clean and well-optimized
✓ All dependencies are necessary for build outputs
Key Findings:
- All
buildtasks correctly depend on^build(upstream packages) //#gen(monorepo-wide code generation) runs once, appropriately scoped//#audit(monorepo-wide audit) runs once, appropriately scoped- No uncacheable tasks blocking critical path
- No diamond dependencies inflating task count
Conclusion: The CellixJS task graph is currently well-optimized with no low-hanging optimization opportunities. Future optimization candidates would emerge as:
- New packages with unnecessary upstream dependencies are added
- New task types (e.g., typegen, type-check) are introduced
- Build time regressions trigger periodic reviews
Usage​
# Via GitHub Copilot or Claude:
# "Optimize the Turborepo task graph using the turbo-graph-optimization skill"
When run, the skill will:
- Discover build targets (
@apps/api,@apps/ui-community,@apps/docs) - Analyze current task graph (as captured above)
- Identify optimization opportunities (if any emerge)
- Apply safe changes and verify build succeeds
- Present before/after comparison with summary
References​
- Agent Skill: turbo-graph-optimization
- ADR-0020: Azure DevOps Pipeline
- ADR-0024: Agent Skills Framework
- turbo query Docs