Docs /architecture/monorepo-structure

Monorepo Structure

Understanding how the Izri codebase is organized

📁 Repository Layout

izri/
├── apps/                      # Application packages
│   ├── web/                   # React Router frontend
│   └── api/                   # Hono + tRPC backend
│
├── packages/                  # Shared packages
│   ├── database/              # Drizzle ORM + schema
│   ├── trpc/                  # tRPC routers + procedures
│   ├── auth/                  # Authentication (Lucia)
│   ├── shared/                # Common utilities
│   ├── repo-analyzer/         # Repository analysis
│   └── ai-service/            # Python AI service
│
├── docs/                      # Documentation
│   ├── getting-started/
│   ├── architecture/
│   ├── development/
│   └── ...
│
├── docker/                    # Dockerfiles
│   ├── web/
│   ├── api/
│   └── ai-service/
│
├── .turbo/                    # Turbo cache
├── node_modules/              # Dependencies
├── package.json               # Root package config
├── pnpm-workspace.yaml        # Workspace definition
├── turbo.json                 # Turbo configuration
├── tsconfig.json              # Base TypeScript config
└── docker-compose.yml         # Local infrastructure

🏗️ Workspace Configuration

pnpm Workspace

File: pnpm-workspace.yaml

packages:
  - apps/*
  - packages/*

onlyBuiltDependencies:
  - '@tailwindcss/oxide'

What it does:

  • Defines which directories contain packages
  • Enables cross-package dependencies
  • Links packages via symlinks (fast, efficient)
  • Hoists common dependencies to root

Turbo Configuration

File: turbo.json

{
  "globalDependencies": [".env", "tsconfig.json"],
  "globalEnv": ["DATABASE_URL", "API_URL", ...],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", "lib/**"],
      "cache": true
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

What it does:

  • Orchestrates task execution
  • Manages build dependencies
  • Enables intelligent caching
  • Runs tasks in parallel

📦 Application Packages

apps/web

Purpose: Frontend React Router application

Structure:

apps/web/
├── app/                       # React Router app
│   ├── routes/                # File-based routes
│   │   ├── _index.tsx        # Home page
│   │   ├── projects/         # Projects section
│   │   │   ├── $id.tsx       # Project detail
│   │   │   └── new.tsx       # Create project
│   │   ├── login.tsx         # Auth pages
│   │   └── ...
│   ├── components/            # React components
│   │   ├── ui/               # shadcn/ui components
│   │   ├── layout/           # Layout components
│   │   └── features/         # Feature-specific
│   ├── lib/                   # Utilities
│   │   ├── trpc.ts           # tRPC client setup
│   │   ├── utils.ts          # Helper functions
│   │   └── cn.ts             # Class name utility
│   ├── styles/                # CSS files
│   │   └── globals.css       # Global styles
│   ├── root.tsx               # Root component
│   └── entry.client.tsx       # Client entry
│
├── public/                    # Static assets
│   ├── favicon.ico
│   └── images/
│
├── package.json               # Dependencies + scripts
├── tsconfig.json              # TypeScript config
├── vite.config.ts             # Vite configuration
├── tailwind.config.js         # Tailwind config
└── react-router.config.ts     # React Router config

Key Files:

  • app/root.tsx: Root layout with providers
  • app/entry.client.tsx: Client-side entry point
  • app/lib/trpc.ts: tRPC client configuration
  • vite.config.ts: Build and dev server config

Dependencies:

{
  "dependencies": {
    "react": "^19.0.0",
    "react-router": "^7.1.3",
    "@tanstack/react-query": "^5.0.0",
    "@izri/trpc": "workspace:*",
    "@izri/shared": "workspace:*"
  }
}

apps/api

Purpose: Backend API server (Hono + tRPC)

Structure:

apps/api/
├── src/
│   ├── index.ts               # Server entry point
│   ├── app.ts                 # Hono app setup
│   ├── middleware/            # Custom middleware
│   │   ├── auth.ts           # Auth middleware
│   │   └── logger.ts         # Logging
│   ├── lib/                   # Utilities
│   │   └── trpc.ts           # tRPC server setup
│   └── utils/                 # Helper functions
│
├── package.json
└── tsconfig.json

Key Files:

  • src/index.ts: Starts Hono server, mounts tRPC
  • src/app.ts: Configures middleware, routes
  • src/middleware/auth.ts: Authentication logic

Dependencies:

{
  "dependencies": {
    "hono": "^4.0.0",
    "@trpc/server": "^11.0.0",
    "@izri/trpc": "workspace:*",
    "@izri/database": "workspace:*",
    "@izri/auth": "workspace:*"
  }
}

📚 Shared Packages

packages/database

Purpose: Database schema, migrations, and queries

Structure:

packages/database/
├── src/
│   ├── index.ts               # Exports
│   ├── db.ts                  # Database client
│   ├── schema.ts              # Drizzle schema
│   ├── seed.ts                # Seed script
│   └── migrations/            # (unused, see root)
│
├── migrations/                # Migration files (root)
│   ├── 0000_initial.sql
│   ├── 0001_add_projects.sql
│   └── ...
│
├── drizzle.config.ts          # Drizzle Kit config
├── package.json
└── tsconfig.json

Key Files:

  • src/schema.ts: All table definitions
  • src/db.ts: Drizzle client setup
  • drizzle.config.ts: Migration configuration

Exports:

export { db } from './db'
export * from './schema'
export type { 
  User, NewUser,
  Project, NewProject,
  // ... all types
} from './schema'

packages/trpc

Purpose: tRPC routers, procedures, and context

Structure:

packages/trpc/
├── src/
│   ├── index.ts               # Main exports
│   ├── router.ts              # Root router
│   ├── context.ts             # tRPC context
│   ├── trpc.ts                # tRPC setup
│   └── routers/               # Feature routers
│       ├── health.ts         # Health check
│       ├── projects.ts       # Projects CRUD
│       ├── test-runs.ts      # Test runs
│       ├── organizations.ts  # Organizations
│       └── users.ts          # User management
│
├── package.json
└── tsconfig.json

Key Files:

  • src/trpc.ts: Procedure builders
  • src/context.ts: Request context creation
  • src/router.ts: Combines all routers

Pattern:

// src/routers/projects.ts
import { router, publicProcedure } from '../trpc'
import { z } from 'zod'

export const projectRouter = router({
  list: publicProcedure.query(async ({ ctx }) => {
    return ctx.db.select().from(projects)
  }),
  
  create: publicProcedure
    .input(z.object({ name: z.string() }))
    .mutation(async ({ ctx, input }) => {
      return ctx.db.insert(projects).values(input)
    })
})

packages/auth

Purpose: Authentication with Lucia

Structure:

packages/auth/
├── src/
│   ├── index.ts               # Exports
│   ├── lucia.ts               # Lucia setup
│   ├── providers/             # OAuth providers
│   │   └── github.ts         # GitHub OAuth
│   └── middleware.ts          # Auth middleware
│
├── package.json
└── tsconfig.json

packages/shared

Purpose: Common utilities, types, and helpers

Structure:

packages/shared/
├── src/
│   ├── index.ts               # Exports
│   ├── types/                 # Shared types
│   │   ├── api.ts            # API types
│   │   └── common.ts         # Common types
│   ├── utils/                 # Utilities
│   │   ├── validation.ts     # Zod schemas
│   │   ├── errors.ts         # Error classes
│   │   └── format.ts         # Formatters
│   └── constants/             # Constants
│       └── index.ts
│
├── package.json
└── tsconfig.json

packages/repo-analyzer

Purpose: TypeScript repository analysis utilities

Structure:

packages/repo-analyzer/
├── src/
│   ├── index.ts
│   ├── analyzer.ts            # Main analyzer
│   └── parsers/               # Language parsers
│
├── package.json
└── tsconfig.json

packages/ai-service

Purpose: Python AI service (FastAPI)

Structure:

packages/ai-service/
├── main.py                    # FastAPI app
├── requirements.txt           # Python dependencies
└── README.md

🔗 Package Dependencies

Dependency Graph

apps/web
  ├─→ @izri/trpc
  └─→ @izri/shared

apps/api
  ├─→ @izri/trpc
  ├─→ @izri/database
  ├─→ @izri/auth
  └─→ @izri/shared

@izri/trpc
  ├─→ @izri/database
  └─→ @izri/shared

@izri/auth
  ├─→ @izri/database
  └─→ @izri/shared

@izri/database
  └─→ (no workspace deps)

@izri/shared
  └─→ (no workspace deps)

How Workspace Dependencies Work

In package.json:

{
  "dependencies": {
    "@izri/trpc": "workspace:*"
  }
}

What happens:

  1. pnpm creates symlink to local package
  2. Changes reflect immediately (no reinstall)
  3. TypeScript follows symlinks
  4. Build order managed by Turbo

🔧 Build Pipeline

Build Order (Turbo)

graph TD
    A[shared] --> B[database]
    B --> C[trpc]
    B --> D[auth]
    C --> E[api]
    C --> F[web]
    D --> E
    A --> C
    A --> D

Execution:

pnpm build  # Turbo builds in correct order

What happens:

  1. @izri/shared builds first
  2. @izri/database builds (depends on shared)
  3. @izri/trpc and @izri/auth build in parallel
  4. apps/api and apps/web build last

Build Outputs

Package Output Directory Contents
shared lib/ Compiled .js + .d.ts
database lib/ Compiled .js + .d.ts
trpc lib/ Compiled .js + .d.ts
auth lib/ Compiled .js + .d.ts
api dist/ Bundled server
web dist/ Bundled client

📝 TypeScript Configuration

Root tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2023"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "resolveJsonModule": true
  }
}

Package-Specific Configs

Apps extend root:

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist"
  },
  "include": ["src"]
}

Import Resolution

Path aliases (in tsconfig.json):

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./app/components/*"]
    }
  }
}

Usage:

import { Button } from '@/components/ui/button'
import { api } from '@/lib/trpc'

🚀 Development Workflow

Installing Dependencies

# Install all packages
pnpm install

# Add to specific package
pnpm --filter @izri/web add react-query

# Add to root (shared dev tool)
pnpm add -w -D vitest

Running Tasks

# Run dev servers (all)
pnpm dev

# Run specific package
pnpm dev:web
pnpm dev:api

# Build all
pnpm build

# Build specific package
pnpm build:web

# Type check
pnpm type:check

# Lint
pnpm lint

Creating New Packages

  1. Create directory:

    mkdir packages/my-package
    cd packages/my-package
    
  2. Initialize package:

    pnpm init
    
  3. Update package.json:

    {
      "name": "@izri/my-package",
      "version": "0.0.0",
      "main": "./lib/index.js",
      "types": "./lib/index.d.ts",
      "scripts": {
        "build": "tsc",
        "dev": "tsc --watch"
      }
    }
    
  4. Create tsconfig.json:

    {
      "extends": "../../tsconfig.json",
      "compilerOptions": {
        "outDir": "./lib",
        "rootDir": "./src"
      },
      "include": ["src"]
    }
    
  5. Add to Turbo (if needed): Already configured via wildcards

🎯 Best Practices

Do's ✅

  • Use workspace protocol: "@izri/package": "workspace:*"
  • Keep packages focused: Single responsibility
  • Export explicitly: Don't export everything
  • Version together: Keep all packages in sync
  • Build before use: Ensure packages are built

Don'ts ❌

  • Don't create circular dependencies
  • Don't skip builds: Always build packages before use
  • Don't duplicate code: Move shared code to shared package
  • Don't use relative imports: Use package names
  • Don't mix concerns: Keep packages focused

🔗 Related Documentation


Questions about package structure? Check with the team or refer to existing packages as examples.

Reading this with an agent? /docs/architecture/monorepo-structure.md serves the raw markdown.

All docs