Build Orchestration & Monorepo Optimization β
For workspace topology and app/package map see frontend.md. For the full package dependency graph see app-structure.md.
Workspace Configuration β
The monorepo uses pnpm workspaces for dependency resolution and Turborepo for task orchestration.
# pnpm-workspace.yaml
packages:
- apps/*
- studio/*
- "!studio/presentations"
- packages/*
- packages/services/*
onlyBuiltDependencies:
- vue-demiAll root-level scripts delegate to Turborepo:
{
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"dev": "turbo run dev"
}Convenience shortcuts for individual apps (e.g. dev:workspace, dev:landing) use pnpm --filter directly for targeted local development.
Turborepo Pipeline β
Task definitions live in turbo.json. Each task declares its dependency chain, cacheable inputs, and outputs.
Task Graph β
build ββdependsOnβββΊ ^build (build dependencies first)
dev ββdependsOnβββΊ ^build (build deps before starting dev servers)
lint ββdependsOnβββΊ ^build
test ββdependsOnβββΊ ^buildBuild Task β
The build task is the most heavily optimized:
- Inputs:
src/**,app/**,server/**,pages/**,components/**,composables/**,layouts/**,plugins/**,public/**, config files (*.config.*), and source files (*.ts,*.js,*.vue,*.css). - Excluded Inputs: Test files (
*.spec.*,*.test.*) and Markdown (*.md) β changes to these do not invalidate the build cache. - Outputs:
dist/**,.output/**,.nuxt/**β cached and restored on cache hits.
Dev Task β
cache: falseβ dev servers are never cached.persistent: trueβ Turborepo keeps the process alive (long-running dev servers).
Test Task β
- Outputs:
coverage/**β the system caches coverage reports between runs.
Caching Strategy β
Local Cache β
Turborepo caches task outputs in node_modules/.cache/turbo by default. On a cache hit, the system restores outputs from disk instead of re-executing the task. This provides instant feedback for unchanged packages.
Remote Cache (Planned) β
For shared CI/GitHub Actions caching:
- Option A: Vercel Remote Cache (zero config,
turbo login+turbo link). - Option B: Self-hosted via Turborepo's open-source cache server (aligns with self-hosted infrastructure strategy in infrastructure.md).
Turborepo composes cache keys from: file hashes of declared inputs, environment variable values, and the task's dependsOn chain.
Affected-Package Filtering β
For PR pipelines, only changed packages and their dependents should be built/tested:
# Build only packages affected by changes since the merge base
turbo run build --filter=...[origin/main]
# Test a specific package and everything that depends on it
turbo run test --filter=@busflow/types...The CI pipeline integrates this (see ci-cd.md Β§5).
Docker Build Strategy β
Each deployable app uses Turborepo prune to generate a minimal, isolated workspace for Docker builds. This replaces naive COPY . . approaches and dramatically reduces image sizes and layer cache invalidation.
# Stage 1: Prune the monorepo to only the target app's dependency tree
FROM node:22-alpine AS pruner
WORKDIR /app
COPY . .
RUN npx turbo prune @busflow/workspace --docker
# Stage 2: Install dependencies (cached unless lockfile changes)
FROM node:22-alpine AS installer
WORKDIR /app
COPY --from=pruner /app/out/json/ .
RUN pnpm install --frozen-lockfile
# Stage 3: Build (cached unless source changes)
COPY --from=pruner /app/out/full/ .
RUN pnpm turbo run build --filter=@busflow/workspace
# Stage 4: Production image
FROM nginx:alpine
COPY --from=installer /app/apps/workspace/dist /usr/share/nginx/htmlThis expands the build optimization strategy referenced in infrastructure.md.
Dependency Management β
pnpm Conventions β
- Workspace protocol: Internal packages use
workspace:*for local resolution (e.g."@busflow/types": "workspace:*"). - Shared devDependencies: Tooling installed at the root (
turbo, shared configs). App-specific tools stay in the app's ownpackage.json. - Overrides: Global dependency overrides for security patches live in the root
package.jsonunderpnpm.overrides(e.g."node-forge": "^1.4.0"). onlyBuiltDependencies: Packages requiring native builds (e.g.vue-demi) are explicitly allowlisted inpnpm-workspace.yaml.
Adding a New Package β
- Create the package directory under the appropriate workspace glob (
packages/,apps/,studio/). - Initialize with
pnpm initand set thenamefield to@busflow/<package-name>. - Add to consumers via
pnpm add @busflow/<package-name> --workspace --filter @busflow/<consumer>. - If the package produces build output, add the
buildscript so Turborepo includes it in the task graph.