ADR-001: Monorepo Structure
Status
Accepted (2024-01-15)
Context
Tenki Cloud consists of multiple interconnected services:
- Frontend applications (Next.js web app, future mobile apps)
- Backend services (Go microservices)
- Shared packages (TypeScript utilities, proto definitions)
- Infrastructure code (Kubernetes manifests, Terraform)
We need a repository structure that:
- Enables code sharing between services
- Ensures coordinated deployments
- Maintains clear boundaries between services
- Provides good developer experience
Decision
We will use a monorepo structure with:
- pnpm workspaces for TypeScript/JavaScript projects
- Go modules with
replacedirectives for Go services - Turborepo for orchestrated builds
- Shared tooling across all services
Repository structure:
tenki.app/
├── apps/ # Deployable applications
├── backend/ # Go services
├── packages/ # Shared libraries
├── proto/ # Protocol buffer definitions
└── infra/ # Infrastructure code
Consequences
Positive
- Atomic changes - Features spanning multiple services can be implemented in a single commit
- Shared tooling - Linting, formatting, and testing tools configured once
- Simplified dependencies - No need for private package registries
- Consistent versioning - All services released together
- Easier refactoring - Moving code between services is straightforward
- Single source of truth - Proto definitions shared directly
Negative
- Larger repository - Clone and fetch times increase over time
- Complex CI/CD - Need to determine which services to build/deploy
- Steeper learning curve - New developers must understand entire structure
- Potential for coupling - Easier to create inappropriate dependencies
- Tooling requirements - Requires pnpm, Go, and other tools installed
Alternatives Considered
1. Separate Repositories
Rejected because:
- Coordination overhead for cross-service changes
- Dependency version management complexity
- Need for private package registry
- Difficult to maintain API contracts
2. Git Submodules
Rejected because:
- Poor developer experience
- Complex update workflows
- Easy to get into inconsistent states
- Limited tool support
3. Lerna (instead of Turborepo)
Rejected because:
- Turborepo has better performance
- Native pnpm workspace support
- Better caching mechanisms
- Simpler configuration
Implementation Notes
-
Use
pnpmfilters for targeted operations:pnpm -F app dev # Run only app pnpm -F "backend/*" test # Test all backend -
Go services use local replace:
replace github.com/luxorlabs/proto => ../../proto -
CI uses Turborepo caching:
{ "pipeline": { "build": { "cache": true } } }