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
HanseNexus 2026