All docs/Monorepo Patterns

docs/architecture/app-scoped-feature-packages.md

App-Scoped Feature Packages

Use a single app-scoped feature package when functionality is tightly coupled to one app. Avoid micro-packages like kanban-core and kanban-fs; instead expose intent-based surfaces.

Standard Pattern

  • Package name: @repo/app-<app> (e.g., @repo/app-kanban)
  • Surfaces: ./server, ./web, ./types (optional ./client)
  • Internals: Organize freely (server/{core,fs,...}) but keep them private
  • Exports: types from src/*, runtime from dist/* (compiled package pattern)
  • No process.env in packages; follow environment getter and lazy init rules

Example

// Server code
import { loadKanbanTasks } from "@repo/app-kanban/server";

// Client component
"use client";
import { sortTasksByPriority } from "@repo/app-kanban/web";

// Shared contracts
import type { KanbanTask } from "@repo/app-kanban/types";

Tier-Based Pattern

When an app has distinct feature tiers (free/paid), use tier-based subpaths instead of runtime-based surfaces:

  • Package name: @repo/app-<app> (e.g., @repo/app-library)
  • Surfaces: ./shared, ./free, ./paid
  • Shared: Types, transformers, mocks used by all tiers
  • Free/Paid: Tier-specific UI components

Example: @repo/app-library

// Free tier page - gets free components + shared
import { FlowsListView, VideoInfo } from "@repo/app-library/free";

// Paid tier page - gets paid components + shared
import { VideosDashboard, VideoInfo } from "@repo/app-library/paid";

// Just shared utilities
import { toVideoInfo, mockFlowInfos } from "@repo/app-library/shared";

Folder Structure

packages/app-library/
├── src/
│   ├── index.ts              # Barrel for shared only
│   ├── shared/               # Types, transformers, mocks
│   │   ├── index.ts
│   │   ├── types.ts
│   │   ├── transformers.ts
│   │   └── mocks.ts
│   ├── free/                 # Free tier components
│   │   ├── index.ts
│   │   └── [components].tsx
│   └── paid/                 # Paid tier components
│       ├── index.ts
│       └── [components].tsx

When to Use Tier Pattern

Use tier-based organization when:

  • App has distinct pricing/access tiers
  • Components clearly belong to one tier
  • Tiers share common types and utilities

Use standard pattern (server/web/types) when:

  • Separation is by runtime environment
  • No pricing/access tier distinction
  • Components shared across app contexts

See Demo App and Tier Architecture for complete details.

Promotion Path

If another app needs the feature, rename the package to @repo/<feature> and keep surfaces stable to minimize churn.

Next.js

List app-scoped packages in transpilePackages of the consuming app.