# Layer Structure Reference Detailed folder structures, code examples, and naming conventions for each FSD layer. Use this reference when creating, reviewing, or reorganizing project structure. --- ## App Layer App-wide initialization: providers, routing, global styles, entry point. Organized by segments only, no slices. The methodology does not formally standardize App segment names. The common convention list (`ui`, `api`, `model`, `lib`, `config`) applies to all layers but is rarely a good fit here. In practice, projects use names that describe purpose: `routes`, `store`, `styles`, `providers`, `entrypoint`, etc. Choose names that match your stack (for example, `providers` for React/Vue provider components that wrap Redux, QueryClient, or theme contexts): ```text app/ routes/ ← Route configuration (or router.tsx for single file) store/ ← Global state store (Redux configureStore, Zustand root) styles/ ← Global CSS, reset, theme variables providers/ ← Provider components (Redux Provider, QueryClientProvider) entrypoint.tsx ← Application entry point (main.tsx, index.tsx) ``` A smaller project may collapse some of these into single files: ```text app/ router.tsx ← Route configuration store.ts ← Store configuration styles/ global.css providers.tsx ← All providers in one file index.tsx ← Entry point ``` ```typescript // app/router.tsx import { HomePage } from '@/pages/home'; import { ProfilePage } from '@/pages/profile'; export const router = createBrowserRouter([ { path: '/', element: }, { path: '/profile/:id', element: }, ]); ``` **Belongs in app:** Global providers (Redux store, QueryClient, theme), routing setup, global styles, error boundaries, analytics initialization. **Does not belong:** Feature-specific code, business logic, page-level UI. --- ## Pages Layer Route-level composition. In v2.1, pages **own substantial logic**: they are not thin wrappers. In early project stages, most code lives here. ```text pages/ home/ ui/ HomePage.tsx HeroSection.tsx FeaturesGrid.tsx model/ home-data.ts ← Page-specific state + logic api/ fetch-home-data.ts ← Page-specific API calls index.ts profile/ ui/ ProfilePage.tsx ProfileForm.tsx ProfileStats.tsx model/ profile.ts ← Profile state + validation logic api/ update-profile.ts fetch-profile.ts index.ts ``` **Belongs in pages:** Page-specific UI, forms, validation, data fetching, state management, business logic, API integrations. Even code that looks reusable stays here if it is simpler to keep local. **Does not belong:** Code that is currently being reused across multiple pages with stable boundaries (extract to a lower layer when reuse is confirmed, not anticipated). ### Page Layout Patterns A typical page composes widgets, features, and entities from lower layers, plus its own local UI components: ```typescript // pages/product-detail/ui/ProductDetailPage.tsx import { Header } from '@/widgets/header'; import { AddToCart } from '@/features/add-to-cart'; import { Product } from '@/entities/product'; export const ProductDetailPage = ({ productId }) => { const product = useProductDetail(productId); // local hook in this page return ( <>
{/* local component */} ); }; ``` For pages that only need shared + page-local code (no extracted layers): ```typescript // pages/about/ui/AboutPage.tsx import { Card } from '@/shared/ui/Card'; import { TeamSection } from './TeamSection'; // local to this page import { MissionStatement } from './MissionStatement'; export const AboutPage = () => (
); ``` --- ## Widgets Layer Composite UI blocks with their own logic, **reused across multiple pages**. Add this layer only when UI blocks actually appear in 2+ pages and sharing provides clear value. ```text widgets/ header/ ui/ Header.tsx Navigation.tsx UserMenu.tsx model/ header.ts ← Widget state api/ fetch-notifications.ts index.ts sidebar/ ui/ Sidebar.tsx model/ sidebar.ts index.ts ``` **Belongs in widgets:** Navigation bars, sidebars, dashboards, footers, complex card layouts that combine data from multiple entities/features. **Does not belong:** Simple UI primitives (→ `shared/ui/`), single-use page sections (→ keep in the page). --- ## Features Layer Independent, reusable user interactions. **Create only when used in 2+ places.** ```text features/ auth/ ui/ LoginForm.tsx RegisterForm.tsx model/ auth.ts ← Auth state + logic api/ login.ts register.ts index.ts add-to-cart/ ui/ AddToCartButton.tsx model/ cart.ts index.ts like-post/ ui/ LikeButton.tsx model/ like.ts api/ toggle-like.ts index.ts ``` **Feature composition**: features consume entities and are composed in higher layers: ```typescript // widgets/post-card/ui/PostCard.tsx import { UserAvatar } from '@/entities/user'; import { LikeButton } from '@/features/like-post'; import { CommentButton } from '@/features/comment-create'; export const PostCard = ({ post }) => (

{post.title}

{post.content}

); ``` --- ## Entities Layer Reusable business domain models. **Create only when used in 2+ places. Starting without this layer is completely valid.** ```text // Minimal entity: model only (most common form) entities/user/ model/ user.ts ← Types + domain logic index.ts // Entity with UI (use with caution) // ⚠️ Adding UI to entities increases cross-import risk. // Other entities may want to import this UI, leading to @x dependencies. // Entity UI should only be imported from higher layers (features, widgets, // pages), never from other entities. entities/product/ model/ product.ts ui/ ProductCard.tsx index.ts ``` --- ## Shared Layer Structure Infrastructure with no business logic. Organized by segments only (no slices). Segments may import from each other. ```text shared/ ui/ ← UI kit: Button, Input, Modal, Card lib/ ← Utilities: formatDate, debounce, classnames api/ ← API client, route constants, CRUD helpers, base types auth/ ← Auth tokens, login utilities, session management config/ ← Environment variables, app settings assets/ ← Branding assets shared across the app (use sparingly) ``` ```typescript // shared/ui/Button/Button.tsx export const Button = ({ children, onClick, variant = 'primary' }) => ( ); // shared/ui/Button/index.ts export { Button } from './Button'; export type { ButtonProps } from './Button'; ``` Shared **may** contain application-aware code (route constants, API endpoints, branding assets, common types). It must **never** contain business logic, feature-specific code, or entity-specific code. For asset placement specifically (images, icons, fonts, PDFs), see `references/asset-handling.md`. --- ## Segments A segment groups related code within a slice (or within App/Shared). The standard segments cover the most common technical purposes: - **`ui`**: UI display (components, date formatters, styles). - **`api`**: backend interactions (request functions, data types, mappers). - **`model`**: data model (schemas, interfaces, stores, business logic). - **`lib`**: library code that other modules in this slice need. - **`config`**: configuration files and feature flags. Custom segments are allowed when needed (for example, `routes` and `i18n` in the Shared layer, or `auth` for token storage when split out from `shared/api`). ### Group by what it is *for*, not by what it *is* Segment names describe **purpose**, not the kind of code they hold. This is the desegmentation principle: ```text // ❌ BAD: grouping by technical kind (what the code is) shared/ components/ ← What kind of components? hooks/ ← Which feature do they serve? types/ ← Which domain do they describe? utils/ ← Utility for what? helpers/ ← Same problem actions/ ← Redux actions for what? // ✅ GOOD: grouping by purpose (what the code is for) shared/ ui/ ← For displaying UI api/ ← For talking to the backend lib/ ← For library code that supports the slice config/ ← For configuration ``` A segment named `types/` cannot answer "types for what?" without inspecting the contents. A segment named `model/` says: this is the data model. Inside `model/`, files are named by domain (`user.ts`, `order.ts`), not by technical role. This rule applies everywhere: in `shared/`, in slices, and when designing new custom segments. ## Naming Conventions ### Domain-based file naming Within a segment, name files after the business domain, not the technical role: ```text // ❌ Technical-role naming: mixes domains model/types.ts ← Which types? User? Order? model/utils.ts api/endpoints.ts model/selectors.ts // ✅ Domain-based naming: each file owns one domain model/user.ts ← User types + logic + store model/order.ts ← Order types + logic + store api/fetch-profile.ts ← Clear what this API does model/todo.ts ← Redux slice + selectors + thunks ``` ### Single-concern segments If a segment contains only one domain concern, the filename may match the slice name: ```text features/auth/ model/ auth.ts ← Single concern, matches slice name ``` ### Index files as public API Every slice must have an `index.ts` that re-exports its public interface: ```typescript // entities/user/index.ts export { UserAvatar } from "./ui/UserAvatar"; export { useUser, type User } from "./model/user"; ``` --- ## Slice Groups A **slice group** is a folder that contains related slices on the same layer, used purely to make the structure easier to navigate as the number of slices grows. A slice group is **not** a slice itself: it has no segments (`model/`, `ui/`, `api/`), no public API (`index.ts`), and no shared code. Slice isolation rules apply unchanged inside a group: sibling slices in the same group cannot import from each other. Slice groups are optional. Use them only when the layer has grown large enough that a flat structure becomes hard to scan and there is an obvious grouping criterion. ### When to use - Several slices share the same business context and are scattered across the layer. - The slice names clearly suggest they belong to the same topic. - The layer has grown to the point where it is hard to scan at a glance. ### When NOT to use - Names alone are enough for quick navigation. - There is no natural grouping criterion. - Only two or three slices would end up in the group. ### Example: grouping payment-related entities ```text entities/ payment/ ← Slice group (no public API) invoice/ ← Slice model/ ui/ index.ts receipt/ ← Slice (model/, ui/, index.ts) transaction/ ← Slice (model/, ui/, index.ts) user/ ← Slice (not in any group) product/ ← Slice ``` Imports go through the full path: ```typescript import { Invoice } from "@/entities/payment/invoice"; import { Receipt } from "@/entities/payment/receipt"; ``` The same pattern applies to the Pages layer. For example, grouping `pages/order/{list,detail,create}` when there are multiple pages on the same topic such as list, detail, create, and edit. This is one possible example and does not represent the default structure for the Pages layer. ### Features: use with caution Slice groups can be applied to Features, but features often span multiple entities and lack a natural grouping criterion. A group like `features/cart/` tends to attract everything cart-related (DTOs, mappers, helpers) until it stops being a navigation aid and starts acting as the home for the entire cart domain, which weakens the principle that features are split by use case. Before grouping features, check that the group contains only feature slices and that two or three slices is not the entire content. ### Anti-patterns - **Do not put `index.ts` on the group folder.** That promotes the group to a slice and breaks the layer's contract. - **Do not put shared `utils.ts`, `constants.ts`, or `types.ts` files inside the group.** A slice group has no shared code. Extract reusable code to `shared/` instead. If the layer is `entities` and the shared logic is genuinely domain logic, consider whether the boundaries are too granular and the slices should be merged into one isolated entity (see `references/excessive-entities.md`). The `@x` notation does not apply to slice groups. It is a cross-import surface between entity slices, not a sharing mechanism for siblings within a group. - **Do not relax slice isolation inside the group.** If two slices in the same group need to share code, extract it one layer down rather than adding a `_common/` file. --- ## Path Aliases Configure path aliases so imports follow the `@/layer/slice` pattern: ```json // tsconfig.json { "compilerOptions": { "baseUrl": ".", "paths": { "@/app/*": ["src/app/*"], "@/pages/*": ["src/pages/*"], "@/widgets/*": ["src/widgets/*"], "@/features/*": ["src/features/*"], "@/entities/*": ["src/entities/*"], "@/shared/*": ["src/shared/*"] } } } ``` For framework-specific alias configuration (Vite, Next.js, Nuxt, Astro), see `references/framework-integration.md`.