Derived Modular Arch
Deep Dive

Why No Barrel Files

Short answer: a barrel hides the graph. In depth — what breaks and what alternatives exist.

Short answer

A barrel file (index.ts with export { Foo } from './foo') hides real dependencies. The linter and dma check build the graph from import paths — through a barrel the graph lies. That is why DMA forbids barrels inside modules and requires direct imports into */public/*.

What is a barrel

// features/checkout/public/index.ts  ✗
export { CheckoutPage } from "./checkout-page";
export { useCheckout } from "./use-checkout";

The consumer writes:

import { CheckoutPage } from "@/features/checkout/public";

Looks convenient. But the analyzer sees an import into public/index.ts, not into checkout-page.tsx. The real link between modules is lost.

What breaks

1. Dependency graph. dma check cannot say precisely who depends on whom. Cycles and promotion (feature → service) are determined by edges — a barrel blurs edges.

2. Tree-shaking and the bundler. A barrel often pulls in extra code: you imported one function and got the whole module.

3. Refactoring. You renamed a file — every import through the barrel broke, even though there were only two direct consumers.

4. A false sense of API. A barrel suggests "import anything from the folder." DMA does the opposite: it requires an explicit contract — each entrypoint is a separate file in a flat public/.

What DMA allows instead of a barrel

A direct import into a file:

import { CheckoutPage } from "@/features/checkout/public/checkout-page";

At stage 0 (a single-file module) the whole file is public — import checkout.tsx directly.

A 1:1 re-export from public/ to an internal file is allowed as a symlink, but by default write the implementation directly in public/.

"But npm packages use index.ts"

A package at the publication boundary (stage 4) is a different story: there exports in package.json is an explicit contract. Inside an application a module is not a package, and the graph must be transparent to tooling.

Alternatives we rejected

ApproachWhy not
Barrel only in public/Still hides which entrypoint each consumer pulls
"Allow barrels but forbid cycles"Cycles are invisible if the graph is inaccurate
Auto-generated barrelsAnother layer of magic that drifts from the file system

On this page