Monorepo Architecture
Understanding Tuturuuu monorepo architecture and the benefits of using Turborepo.
Prerequisite: You should have followed the Development setup guide to understand the basic structure of the codebase.
What is a Monorepo?
A monorepo is a single repository containing multiple distinct projects with well-defined relationships. This approach differs from a polyrepo strategy where each project has its own separate repository.
“A monorepo is a single repository containing multiple distinct projects, with well-defined relationships.” — monorepo.tools
It’s important to note that a monorepo is not the same as a monolith. A monolith is a single, tightly coupled application, while a monorepo can contain many independent applications, libraries, and tools that can be deployed separately.
Why Tuturuuu Uses a Monorepo
Tuturuuu’s platform is built as a monorepo (@tutur3u/platform
) for several key reasons:
1. Code Sharing Without Overhead
Our monorepo structure makes it easy to share code across different applications and services without the overhead of publishing packages. Core components, utilities, and business logic can be shared across multiple applications without duplicating code.
2. Atomic Changes Across Projects
When making changes that affect multiple parts of the platform, we can make those changes in a single commit. This ensures that all parts of the system remain compatible with each other, reducing integration issues.
For example, when updating a database schema:
- We can update the Supabase schema in
apps/db/supabase/migrations
- Update the TypeScript types in
packages/types/src/supabase.ts
- Update all affected applications in the same pull request
3. Consistent Developer Experience
A monorepo allows us to standardize tooling, testing, and deployment processes across all projects. This creates a consistent development experience regardless of which part of the platform a developer is working on.
All developers use the same:
- Package manager (pnpm)
- Build system (Turborepo)
- Code style guidelines (enforced by ESLint and Prettier)
- Testing framework
- CI/CD pipelines
4. Simplified Dependency Management
Managing dependencies in a monorepo is simpler because we can ensure all projects use the same versions of shared dependencies, avoiding version conflicts.
Why We Chose Turborepo
Tuturuuu uses Turborepo as its build system for several reasons:
1. Incremental Builds with Caching
Turborepo provides intelligent caching of build artifacts. This means that if a file hasn’t changed, Turborepo will use the cached result instead of rebuilding it, significantly reducing build times.
2. Parallel Task Execution
Turborepo automatically parallelizes tasks, maximizing the use of available CPU cores.
3. Task Orchestration
Turborepo understands dependencies between packages and ensures tasks are run in the correct order.
4. Remote Caching
For CI/CD pipelines, Turborepo supports remote caching, which means build artifacts can be shared across different environments.
Monorepo Structure
Our monorepo is organized into several main directories:
apps/
Contains all deployable applications. Each application is a standalone project that can be deployed independently.
packages/
Contains shared libraries and utilities that are used by multiple applications.
Development Workflow
When working in our monorepo, you’ll typically:
- Clone the repository:
git clone https://github.com/tutur3u/platform.git
- Install dependencies:
pnpm install
- Start the development servers:
pnpm dev
orpnpm devx
(with Supabase)
For more detailed instructions on development workflow, see the Development and Local Supabase Development guides.
Best Practices
When working in our monorepo, follow these best practices:
1. Keep Projects Modular
Even though all code lives in one repository, maintain clear boundaries between projects. Each package or app should have a well-defined purpose and API.
2. Use Workspace References
When one project depends on another within the monorepo, use workspace references:
3. Think About Build Order
Be mindful of dependencies between packages. If package B depends on package A, ensure that changes to package A trigger rebuilds of package B.
4. Use Correct Scope for Changes
- For changes that affect a single application, focus your changes there
- For changes that affect multiple applications, consider extracting common code to a shared package
- For global changes (like tooling updates), ensure you test across all affected projects
Common Challenges and Solutions
Challenge: Long Build Times
Solution: Turborepo’s caching mechanism helps, but also:
- Be selective about what you build during development
- Use
pnpm --filter
to focus on specific packages
Challenge: Dependency Management
Solution:
- Regularly update dependencies
- Use
pnpm-workspace.yaml
to define which packages are part of the workspace - Consider using
pnpm dedupe
to eliminate duplicate dependencies
Challenge: CI/CD Pipeline Complexity
Solution:
- Use GitHub Actions workflows that are specific to the affected parts of the codebase
- Leverage Turborepo’s
--filter
and--since
flags to only build what’s changed
Further Resources
- monorepo.tools: Comprehensive information about monorepos
- Turborepo Documentation: Official Turborepo documentation
- PNPM Workspace: How PNPM handles monorepos
Was this page helpful?