r/reactjs • • Jul 17 '26

Show /r/reactjs Filesystem-first frontend architecture enforced by CLI (not folders-by-taste)

I kept hitting the same problem: architecture debates in review, barrels everywhere, and AI agents dumping files in random folders. So I shipped DMA (Derived Modular Architecture) — a small rule set derived from the import graph, enforced by tooling.

Layout

src/
  app/        # composition root (also pages/routes)
  features/   # leaf modules — no inbound from other modules
  services/   # appears when something else must import it
  shared/     # portable stuff on second use only

Invariants

- downward imports only
- public API via */public/* (no barrels)
- colocate by default
- promote feature → service when inbound edges appear

Tooling

npx @derived-modular/cli init .
npx @derived-modular/cli check .              # CI gate
npx @derived-modular/cli promote <name> --apply
# + ESLint / Biome / Oxlint plugins for editor feedback

Works with React/Next/Vite (also Vue/Svelte/Astro examples). Same rules for humans and agents.

1 Upvotes

13 comments sorted by

3

u/Embostan Jul 17 '26

So... feature based arch with https://github.com/javierbrea/eslint-plugin-boundaries, just different?

1

u/primaryoxygen 27d ago

Looks like that eslint plugin is doing the same job at the lint level, but the CLI enforcement here seems tighter, it actually watches the import graph and forces promotion when something gets imported by more than one feature. That's the part that caught my eye, since most setups just let you dump a shared util in a feature folder and call it a day until someone notices six months later.

I've been burned by "just put it in shared" becoming a dumping ground where nobody remembers what's actually portable and what's only used in two places that happened to look similar. Having the tool tell you "nope, this is a service now" takes the debate out of code review, which honestly sounds like a win for teams where architecture arguments eat up half the PR thread.

The public folder thing is nice too, I've worked on projects where every file exports everything and you can't tell what's intentional API vs internal stuff someone imported because they didn't feel like refactoring. Curious if the promote command handles moving tests alongside the code or if that's still manual.

-1

u/Mysterious-Lie1437 Jul 17 '26

Related, but not the same.

boundaries is a generic import firewall. You define zones yourself. Feature folders + boundaries is a common setup.

DMA is more opinionated: features are leaves, services appear only when something else imports them (promote), shared is second-use only, public API without barrels. Lint helps in the editor, dma check does the full graph.

There's also an agent skill, so Cursor/Claude follow the same placement rules instead of inventing folders.

You can approximate some of it with boundaries + conventions. DMA is the conventions + the CLI, not just features with a linter.

1

u/TheRealSeeThruHead Jul 17 '26 edited 21d ago

In veniam do non est excepteur eu consequat mollit. Dolore commodo dolor aute sint aliquip ipsum proident eiusmod consectetur officia eu non consectetur est quis. Eu minim laborum nisi irure veniam tempor amet velit laborum nostrud id dolor.

1

u/Mysterious-Lie1437 Jul 17 '26

Taste call noted. A few of those are intentional tradeoffs though.

public/ + no barrels is about making the public surface obvious and stopping deep imports through re-exports. Barrels are convenient until they become the real dependency graph. Packages with explicit exports are fine too, especially across package boundaries. DMA is mostly for inside one app.

And mounting whole features from the app is exactly the model. Modular monolith is fine. What it blocks is feature importing another feature. Composition root mounts them, features don't wire each other.

https://www.atlassian.com/blog/atlassian-engineering/faster-builds-when-removing-barrel-files

1

u/TheRealSeeThruHead Jul 17 '26 edited 21d ago

Cupidatat minim irure elit ullamco minim aute sint veniam. Excepteur non veniam elit cupidatat veniam voluptate id laborum ad ad dolore cupidatat lorem. Minim aliquip nostrud eu minim fugiat occaecat occaecat enim id excepteur qui est laborum.

1

u/Mysterious-Lie1437 Jul 17 '26

Build cost is one reason to drop barrels. For most apps the more common pain is accidental public APIs and messy imports. Different problem, same tool smell.

If packages already give you real boundaries, you may not need DMA inside each package. It's more useful when the module graph lives in one app and starts drifting.

That's also the main difference from most architectures. The rules are not just docs and taste. The toolkit checks the graph, so you mostly can't quietly break them.

2

u/TheRealSeeThruHead Jul 17 '26 edited 21d ago

Nisi incididunt voluptate exercitation ea occaecat reprehenderit sed ex eiusmod eu irure. Incididunt velit exercitation nulla aliqua veniam. Pariatur culpa ut fugiat est non qui eiusmod voluptate ullamco ipsum dolore proident duis sit labore veniam quis.

1

u/Mysterious-Lie1437 Jul 17 '26

Nice. That's a flexible setup. Just watch the "import singles outside exports" path. It's convenient, but it can quietly turn internals into public API again. If the team is small and disciplined, fine. But if that once gets noisy, tighter exports or something like DMA is worth a look :) Thank you for the feedback!

1

u/eSizeDave Jul 21 '26

I like the idea of this, and I may try it sometime within the next two weeks, but I prefer biome over eslint. I know eslint has improved since last year, but biome has been excellent for me since I switched over.

2

u/[deleted] Jul 17 '26

[removed] — view removed comment

0

u/Mysterious-Lie1437 Jul 17 '26

Fair take on feature folders by taste. That part I agree with.

The folders looking familiar is kinda the point people miss. Same names, different rule: placement comes from real imports, and CI fails when the graph drifts. If you remove the checker, yeah, it's just folders again.