Every frontend developer on a medium-sized project hits the same wall, usually in week two:
"Where do I put this component?"
Not because the team lacks discipline. Because two of the most popular architecture methods each answer only half the question.
Feature-Sliced Design is excellent at where code lives and how it accesses business logic — layers, import boundaries, slices. But it is silent on UI composition, and its widgets layer has been debated for years — teams still land it in different places.
Atomic Design is excellent at how a component looks and composes — atoms, molecules, organisms. But it says nothing about business logic or data access, and in many codebases organisms/ tends to accumulate components whose place nobody had a clear rule for.
Put the two side by side and the gap is obvious. We felt it in production.
The context: a financial tool people use every day
I lead frontend on internal enterprise systems, including a financial accounting tool with real daily users — managers, accounting, the director. Long-lived product, growing scope, several features per release cycle.
For a product like that, "where does this go?" is not a philosophical question. Every undecided placement is a debate, and every debate is slower delivery. We needed one rule set answering both halves — structure and UI composition — with no grey zones.
My answer: assemble the two methods into one and fill the gap with three decision rules. I call the result FSAA — Feature-Sliced Atomic Architecture. Full disclosure: this is not new theory. It is an opinionated assembly of two proven methods, where all the value sits in the rules neither method provides.
The skeleton: five layers, imports flow one way
| Layer | Responsibility |
|---|---|
app |
Router, providers, global config |
pages |
Route-level compositions |
features |
User scenarios and page-level blocks |
entities |
Business models + data access |
shared |
Design tokens, generic UI, utilities |
Imports flow only downward — no layer skipping, no circular dependencies, every module exposes a public API via index.ts. And one deliberate departure from FSD: no widgets layer — features double as page-level blocks. Every layer you remove is one less decision per component.
The three rules (the actual value)
1. The Graduation pattern
Feature-local UI lives flat inside features/<name>/ui/ — five to ten components, no Atomic taxonomy inside a feature. The feature scope is already narrow; adding atoms/molecules there is ceremony, not clarity.
When a component proves reusable in another feature, it graduates: promoted to shared/ui — and only then reorganized into atoms and molecules.
Features own their UI until it earns sharing. Not before.
2. The boundary test for shared
Before anything moves to shared/ui, one question:
If I removed this component from the app entirely, could you still describe what it is without mentioning users, invoices, or orders?
Yes → shared. No → it belongs to a feature.
A generic DataTable passes: columns, data, loading state. An invoiceColumns config does not — that's business logic wearing a UI costume. This single question ended most placement debates on its own.
3. Organisms are eliminated
That large composition Atomic Design would call an organism? In FSAA it is simply a feature — it owns its UI, data access, and state wiring. The Container/Presentational split happens at the export level (one file may export both InvoiceListContainer and InvoiceList), not as folder ceremony.
Atomic levels live in exactly one place: shared/ui.
What changed in practice
After rolling FSAA out across our internal portals:
- Financial breakdown processing time dropped 30–40% — according to our managers' assessment of the before/after workflow; from our side, a predictable structure is a large part of it.
- Code review got simpler: reviewers check import direction and the boundary test instead of personal taste.
- "Where does this go?" stopped being a meeting. The rules answer it.
The rules needed teeth
Here is the uncomfortable part. A year later, I wrote a small deterministic auditor for my own ruleset — four machine checks: raw fetch in UI layers, upward imports, cross-slice imports, UI components outside the layers. Then I pointed it at a production invoice tool that already "follows FSAA" — documented layers, import rules in the README, the works.
160 real boundary violations. Entities pulling hooks from app. Features importing neighboring features. Even our own "reference" component violating our own import section.
The declaration had drifted from the code — silently, review by review. That's the real lesson: a pattern lives by checks, not by declarations. Architecture rules that aren't enforced mechanically stop being rules and become folklore.
Proof in the open
Then came the test I actually control. My own open-source project — etf-rebalancer, an ETF portfolio rebalancer built on FSAA — went through the same audit:
-
Before: 3 findings (2 raw
fetchcalls in a page component, 1 logic-bearing provider inshared/lib) - The fix: two API wrapper functions in the right layers, one file move (a 16-line session provider is a shared primitive, not feature logic)
- After: audit = 0 findings, typecheck clean, production build green. Every number is a machine output, not a promise. The diff is public — it took longer to write this paragraph than to close the findings.
That is the full loop now: audit finds drift → agent migrates → the gate proves it.
The checklist I'd hand my past self
- New UI starts flat in
features/<slice>/ui/— graduates toshared/uionly when reused in a second feature -
fetch/axiosin UI layers = red, move it to the slice's API - Before moving anything to
shared: can you describe it without mentioning users, orders, or invoices? - Any architecture rule without a machine check — expect it to drift within a year
When I wouldn't use it
Architecture is a tool, not a religion. FSAA is a poor fit for throwaway prototypes, two-week MVPs, and tiny apps where folder layout isn't the bottleneck. For those, a flat src/ beats any methodology.
What I'd tell a team in the same spot
The value of an architecture is not its diagram. It is the number of decisions it removes.
If you're torn between FSD and Atomic Design — you don't have to choose. But adopting both without decision rules just moves the debates elsewhere. The rules are the architecture: where UI graduates, what passes the boundary test, what replaces organisms.
Built with React, TypeScript, and a team tired of debating folders.
🔗 Full ruleset + runnable templates: github.com/Aramex86/fsaa · scaffold a new project: npx create-fsaa@latest
🎁 Get the code + daily React tips → t.me/novamind_hub
Top comments (0)