Monorepo
dma check in a monorepo: multi-root discovery, workspace packages, CI.
dma check / dma doctor analyze one DMA src/ tree per root. In a monorepo you can still point at a single app, or run from the workspace root and let the CLI discover apps.
One check — one app (still valid)
npx @derived-modular/cli check apps/web
npx @derived-modular/cli check apps/admin
npx @derived-modular/cli check examples/vite-reactThe path must point at the app root with src/{app|pages|routes, features, services?, shared}.
Multi-root from the monorepo root
If path has no src/, the CLI discovers DMA roots and checks each separately:
npx @derived-modular/cli check .Default discovery: packages with src/ and a composition root (app | pages | routes).
How candidates are found: npm/bun package.json workspaces or pnpm-workspace.yaml first; otherwise a shallow directory walk (skips node_modules, dist, …).
Flags
# explicit list (skips discovery; positional path ignored)
npx @derived-modular/cli check --roots apps/web,apps/admin
# also include library packages with src/{features|services|shared} but no composition root
npx @derived-modular/cli check . --include-packagesOr put the same in dma.config.ts (see dma.config):
import { defineConfig } from "@derived-modular/cli";
export default defineConfig({
roots: ["apps/web", "apps/admin"],
});Each root is a separate graph. Workspace imports between packages are not merged — intentional.
Exit codes are aggregated: 1 if any root fails check; 2 if no roots found or a --roots entry has no src/.
CI for multiple apps
Single job from repo root
- run: npx @derived-modular/cli check .Job matrix (per app)
strategy:
matrix:
app: [apps/web, apps/admin]
steps:
- run: npx @derived-modular/cli check ${{ matrix.app }}Explicit roots
npx @derived-modular/cli check --roots apps/web,apps/adminWorkspace packages (stage 4)
When a module is consumed by multiple apps, extract it into a monorepo package:
packages/
└── cart/
├── package.json # exports point at public surface
└── src/
└── public/
└── cart.tsSame rules: direct paths, no barrel inside the package. Enforcement via exports in package.json and app imports to the published surface.
To run dma check on such a package (no composition root), use --include-packages or --roots packages/cart.
The app imports @my-org/cart/public/cart (or a path from exports) — as an external module lower in the layer stack, not as a feature.
More on stage 4 — Modules and public API.
Paths and tsconfig
@/ aliases resolve from the app's tsconfig.json, not the monorepo root tsconfig. Make sure paths are set in the package that owns src/.
Examples in this repository
Each example is a workspace package under examples/*:
bun run build # build CLI and plugins
bun run --cwd examples/vite-react dma-check
# or from repo root after build:
npx @derived-modular/cli check --roots examples/vite-reactLinters in monorepo
ESLint / Biome / Oxlint configs are usually per package. @derived-modular/* plugins are wired in the config of the app where src/ lives.
Biome Grit plugins resolve paths from the project root; some monorepo / PnP layouts may have limitations — see Biome.
Migrating a monorepo
Don't enable dma check on all legacy at once. Strangler by app:
- One app (
apps/web) — greencheck - Others — later or with a separate plan
See Migration.