Architecture Decisions
ADR-0001: Turborepo Monorepo Structure
Decision to use Turborepo for monorepo management with apps and shared packages
Status
Accepted
Date: 2025-02-03
Context
HanseNexus is building multiple SaaS products (CalNexus, Lexilink, Qript) that share significant infrastructure, design systems, and business logic. We needed to decide how to structure the codebase to:
- Share code efficiently between products without duplication
- Maintain independent deployments for each product
- Enable fast builds through intelligent caching
- Simplify dependency management across projects
- Support multiple teams working on different products simultaneously
Key constraints:
- All products use Next.js, React, and similar tech stacks
- Shared UI components (design system)
- Shared business logic (auth, billing, storage, analytics)
- Independent deployment schedules
- Need for local development efficiency
Decision
We chose Turborepo as our monorepo solution with the following structure:
hn-monorepo/
├── apps/
│ ├── calnexus/ # AI calendar assistant
│ ├── lexilink/ # Link management SaaS
│ └── qript/ # Scripting/automation platform
└── packages/
├── ui/ # Shared shadcn/ui components
├── auth/ # Authentication logic
├── billing/ # Stripe integration
├── storage/ # Storage helpers
├── analytics/ # Analytics integration
├── monitoring/ # OTel + SigNoz monitoring
├── email/ # Email templates and sending
├── ai/ # AI SDK integrations
├── i18n/ # Internationalization
└── config-*/ # Shared configurations
Each app is independently deployable but shares packages as workspace dependencies.
Consequences
Positive
- Zero duplication: UI components, auth flows, billing logic written once, used everywhere
- Type safety across boundaries: TypeScript ensures packages are used correctly across apps
- Fast builds: Turborepo’s remote caching means rebuilding only what changed
- Simplified dependency management: One
package.json, one lock file, one version of React - Atomic changes: Update a shared package and all apps simultaneously
- Better code review: Changes across multiple apps visible in a single PR
- Easier onboarding: New developers see the entire ecosystem in one place
Negative
- Initial setup complexity: Configuring Turborepo, workspaces, and build pipelines takes time
- Larger repository: Cloning and CI checkout times are longer
- Coordination overhead: Breaking changes in shared packages affect multiple apps
- All-or-nothing Git history: Cannot split out individual apps easily later
- Learning curve: Team members need to understand monorepo concepts and tooling
Neutral
- CI/CD changes: Had to adapt deployment pipelines for monorepo structure
- Build system is opinionated: Turborepo’s conventions must be followed
- Package boundaries must be explicit: Requires discipline to maintain clean dependencies
Alternatives Considered
Alternative 1: Separate Repositories (Polyrepo)
- Description: Each app in its own repository, shared code via npm packages
- Pros: Complete independence between products; smaller, faster repositories; no monorepo tooling required
- Cons: Duplication and drift across repos; complex versioning for shared packages; difficult to make atomic cross-cutting changes
- Why not chosen: Too much overhead for our small team, would lead to code duplication
Alternative 2: Lerna/Nx Monorepo
- Description: Alternative monorepo tools with different tradeoffs
- Pros: Lerna is mature and well-documented; Nx is very powerful for large enterprises
- Cons: Lerna has slower builds and is less opinionated; Nx has a steep learning curve and is over-engineered for our needs
- Why not chosen: Turborepo offers the best balance of simplicity and performance for Next.js-focused projects
Alternative 3: Multirepo with Git Submodules
- Description: Separate repos linked via Git submodules
- Pros: Flexible repository boundaries; can mix public/private repos
- Cons: Submodules are notoriously difficult to work with; poor developer experience; complex versioning and updates
- Why not chosen: Submodules add complexity without solving the code sharing problem
References
Notes
- We use Bun workspaces alongside Turborepo for package management (see ADR-0004)
- Remote caching is enabled for faster CI/CD builds
- The
@hn-monorepo/*namespace keeps package imports clear and prevents naming conflicts