502 lines
12 KiB
Markdown
502 lines
12 KiB
Markdown
# File Organization
|
|
|
|
Proper file and directory structure for maintainable, scalable frontend code in the the application.
|
|
|
|
---
|
|
|
|
## features/ vs components/ Distinction
|
|
|
|
### features/ Directory
|
|
|
|
**Purpose**: Domain-specific features with their own logic, API, and components
|
|
|
|
**When to use:**
|
|
- Feature has multiple related components
|
|
- Feature has its own API endpoints
|
|
- Feature has domain-specific logic
|
|
- Feature has custom hooks/utilities
|
|
|
|
**Examples:**
|
|
- `features/posts/` - Project catalog/post management
|
|
- `features/blogs/` - Blog builder and rendering
|
|
- `features/auth/` - Authentication flows
|
|
|
|
**Structure:**
|
|
```
|
|
features/
|
|
my-feature/
|
|
api/
|
|
myFeatureApi.ts # API service layer
|
|
components/
|
|
MyFeatureMain.tsx # Main component
|
|
SubComponents/ # Related components
|
|
hooks/
|
|
useMyFeature.ts # Custom hooks
|
|
useSuspenseMyFeature.ts # Suspense hooks
|
|
helpers/
|
|
myFeatureHelpers.ts # Utility functions
|
|
types/
|
|
index.ts # TypeScript types
|
|
index.ts # Public exports
|
|
```
|
|
|
|
### components/ Directory
|
|
|
|
**Purpose**: Truly reusable components used across multiple features
|
|
|
|
**When to use:**
|
|
- Component is used in 3+ places
|
|
- Component is generic (no feature-specific logic)
|
|
- Component is a UI primitive or pattern
|
|
|
|
**Examples:**
|
|
- `components/SuspenseLoader/` - Loading wrapper
|
|
- `components/CustomAppBar/` - Application header
|
|
- `components/ErrorBoundary/` - Error handling
|
|
- `components/LoadingOverlay/` - Loading overlay
|
|
|
|
**Structure:**
|
|
```
|
|
components/
|
|
SuspenseLoader/
|
|
SuspenseLoader.tsx
|
|
SuspenseLoader.test.tsx
|
|
CustomAppBar/
|
|
CustomAppBar.tsx
|
|
CustomAppBar.test.tsx
|
|
```
|
|
|
|
---
|
|
|
|
## Feature Directory Structure (Detailed)
|
|
|
|
### Complete Feature Example
|
|
|
|
Based on `features/posts/` structure:
|
|
|
|
```
|
|
features/
|
|
posts/
|
|
api/
|
|
postApi.ts # API service layer (GET, POST, PUT, DELETE)
|
|
|
|
components/
|
|
PostTable.tsx # Main container component
|
|
grids/
|
|
PostDataGrid/
|
|
PostDataGrid.tsx
|
|
drawers/
|
|
ProjectPostDrawer/
|
|
ProjectPostDrawer.tsx
|
|
cells/
|
|
editors/
|
|
TextEditCell.tsx
|
|
renderers/
|
|
DateCell.tsx
|
|
toolbar/
|
|
CustomToolbar.tsx
|
|
|
|
hooks/
|
|
usePostQueries.ts # Regular queries
|
|
useSuspensePost.ts # Suspense queries
|
|
usePostMutations.ts # Mutations
|
|
useGridLayout.ts # Feature-specific hooks
|
|
|
|
helpers/
|
|
postHelpers.ts # Utility functions
|
|
validation.ts # Validation logic
|
|
|
|
types/
|
|
index.ts # TypeScript types/interfaces
|
|
|
|
queries/
|
|
postQueries.ts # Query key factories (optional)
|
|
|
|
context/
|
|
PostContext.tsx # React context (if needed)
|
|
|
|
index.ts # Public API exports
|
|
```
|
|
|
|
### Subdirectory Guidelines
|
|
|
|
#### api/ Directory
|
|
|
|
**Purpose**: Centralized API calls for the feature
|
|
|
|
**Files:**
|
|
- `{feature}Api.ts` - Main API service
|
|
|
|
**Pattern:**
|
|
```typescript
|
|
// features/my-feature/api/myFeatureApi.ts
|
|
import apiClient from '@/lib/apiClient';
|
|
|
|
export const myFeatureApi = {
|
|
getItem: async (id: number) => {
|
|
const { data } = await apiClient.get(`/blog/items/${id}`);
|
|
return data;
|
|
},
|
|
createItem: async (payload) => {
|
|
const { data } = await apiClient.post('/blog/items', payload);
|
|
return data;
|
|
},
|
|
};
|
|
```
|
|
|
|
#### components/ Directory
|
|
|
|
**Purpose**: Feature-specific components
|
|
|
|
**Organization:**
|
|
- Flat structure if <5 components
|
|
- Subdirectories by responsibility if >5 components
|
|
|
|
**Examples:**
|
|
```
|
|
components/
|
|
MyFeatureMain.tsx # Main component
|
|
MyFeatureHeader.tsx # Supporting components
|
|
MyFeatureFooter.tsx
|
|
|
|
# OR with subdirectories:
|
|
containers/
|
|
MyFeatureContainer.tsx
|
|
presentational/
|
|
MyFeatureDisplay.tsx
|
|
blogs/
|
|
MyFeatureBlog.tsx
|
|
```
|
|
|
|
#### hooks/ Directory
|
|
|
|
**Purpose**: Custom hooks for the feature
|
|
|
|
**Naming:**
|
|
- `use` prefix (camelCase)
|
|
- Descriptive of what they do
|
|
|
|
**Examples:**
|
|
```
|
|
hooks/
|
|
useMyFeature.ts # Main hook
|
|
useSuspenseMyFeature.ts # Suspense version
|
|
useMyFeatureMutations.ts # Mutations
|
|
useMyFeatureFilters.ts # Filters/search
|
|
```
|
|
|
|
#### helpers/ Directory
|
|
|
|
**Purpose**: Utility functions specific to the feature
|
|
|
|
**Examples:**
|
|
```
|
|
helpers/
|
|
myFeatureHelpers.ts # General utilities
|
|
validation.ts # Validation logic
|
|
transblogers.ts # Data transblogations
|
|
constants.ts # Constants
|
|
```
|
|
|
|
#### types/ Directory
|
|
|
|
**Purpose**: TypeScript types and interfaces
|
|
|
|
**Files:**
|
|
```
|
|
types/
|
|
index.ts # Main types, exported
|
|
internal.ts # Internal types (not exported)
|
|
```
|
|
|
|
---
|
|
|
|
## Import Aliases (Vite Configuration)
|
|
|
|
### Available Aliases
|
|
|
|
From `vite.config.ts` lines 180-185:
|
|
|
|
| Alias | Resolves To | Use For |
|
|
|-------|-------------|---------|
|
|
| `@/` | `src/` | Absolute imports from src root |
|
|
| `~types` | `src/types` | Shared TypeScript types |
|
|
| `~components` | `src/components` | Reusable components |
|
|
| `~features` | `src/features` | Feature imports |
|
|
|
|
### Usage Examples
|
|
|
|
```typescript
|
|
// ✅ PREFERRED - Use aliases for absolute imports
|
|
import { apiClient } from '@/lib/apiClient';
|
|
import { SuspenseLoader } from '~components/SuspenseLoader';
|
|
import { postApi } from '~features/posts/api/postApi';
|
|
import type { User } from '~types/user';
|
|
|
|
// ❌ AVOID - Relative paths from deep nesting
|
|
import { apiClient } from '../../../lib/apiClient';
|
|
import { SuspenseLoader } from '../../../components/SuspenseLoader';
|
|
```
|
|
|
|
### When to Use Which Alias
|
|
|
|
**@/ (General)**:
|
|
- Lib utilities: `@/lib/apiClient`
|
|
- Hooks: `@/hooks/useAuth`
|
|
- Config: `@/config/theme`
|
|
- Shared services: `@/services/authService`
|
|
|
|
**~types (Type Imports)**:
|
|
```typescript
|
|
import type { Post } from '~types/post';
|
|
import type { User, UserRole } from '~types/user';
|
|
```
|
|
|
|
**~components (Reusable Components)**:
|
|
```typescript
|
|
import { SuspenseLoader } from '~components/SuspenseLoader';
|
|
import { CustomAppBar } from '~components/CustomAppBar';
|
|
import { ErrorBoundary } from '~components/ErrorBoundary';
|
|
```
|
|
|
|
**~features (Feature Imports)**:
|
|
```typescript
|
|
import { postApi } from '~features/posts/api/postApi';
|
|
import { useAuth } from '~features/auth/hooks/useAuth';
|
|
```
|
|
|
|
---
|
|
|
|
## File Naming Conventions
|
|
|
|
### Components
|
|
|
|
**Pattern**: PascalCase with `.tsx` extension
|
|
|
|
```
|
|
MyComponent.tsx
|
|
PostDataGrid.tsx
|
|
CustomAppBar.tsx
|
|
```
|
|
|
|
**Avoid:**
|
|
- camelCase: `myComponent.tsx` ❌
|
|
- kebab-case: `my-component.tsx` ❌
|
|
- All caps: `MYCOMPONENT.tsx` ❌
|
|
|
|
### Hooks
|
|
|
|
**Pattern**: camelCase with `use` prefix, `.ts` extension
|
|
|
|
```
|
|
useMyFeature.ts
|
|
useSuspensePost.ts
|
|
useAuth.ts
|
|
useGridLayout.ts
|
|
```
|
|
|
|
### API Services
|
|
|
|
**Pattern**: camelCase with `Api` suffix, `.ts` extension
|
|
|
|
```
|
|
myFeatureApi.ts
|
|
postApi.ts
|
|
userApi.ts
|
|
```
|
|
|
|
### Helpers/Utilities
|
|
|
|
**Pattern**: camelCase with descriptive name, `.ts` extension
|
|
|
|
```
|
|
myFeatureHelpers.ts
|
|
validation.ts
|
|
transblogers.ts
|
|
constants.ts
|
|
```
|
|
|
|
### Types
|
|
|
|
**Pattern**: camelCase, `index.ts` or descriptive name
|
|
|
|
```
|
|
types/index.ts
|
|
types/post.ts
|
|
types/user.ts
|
|
```
|
|
|
|
---
|
|
|
|
## When to Create a New Feature
|
|
|
|
### Create New Feature When:
|
|
|
|
- Multiple related components (>3)
|
|
- Has own API endpoints
|
|
- Domain-specific logic
|
|
- Will grow over time
|
|
- Reused across multiple routes
|
|
|
|
**Example:** `features/posts/`
|
|
- 20+ components
|
|
- Own API service
|
|
- Complex state management
|
|
- Used in multiple routes
|
|
|
|
### Add to Existing Feature When:
|
|
|
|
- Related to existing feature
|
|
- Shares same API
|
|
- Logically grouped
|
|
- Extends existing functionality
|
|
|
|
**Example:** Adding export dialog to posts feature
|
|
|
|
### Create Reusable Component When:
|
|
|
|
- Used across 3+ features
|
|
- Generic, no domain logic
|
|
- Pure presentation
|
|
- Shared pattern
|
|
|
|
**Example:** `components/SuspenseLoader/`
|
|
|
|
---
|
|
|
|
## Import Organization
|
|
|
|
### Import Order (Recommended)
|
|
|
|
```typescript
|
|
// 1. React and React-related
|
|
import React, { useState, useCallback, useMemo } from 'react';
|
|
import { lazy } from 'react';
|
|
|
|
// 2. Third-party libraries (alphabetical)
|
|
import { Box, Paper, Button, Grid } from '@mui/material';
|
|
import type { SxProps, Theme } from '@mui/material';
|
|
import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query';
|
|
import { createFileRoute } from '@tanstack/react-router';
|
|
|
|
// 3. Alias imports (@ first, then ~)
|
|
import { apiClient } from '@/lib/apiClient';
|
|
import { useAuth } from '@/hooks/useAuth';
|
|
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';
|
|
import { SuspenseLoader } from '~components/SuspenseLoader';
|
|
import { postApi } from '~features/posts/api/postApi';
|
|
|
|
// 4. Type imports (grouped)
|
|
import type { Post } from '~types/post';
|
|
import type { User } from '~types/user';
|
|
|
|
// 5. Relative imports (same feature)
|
|
import { MySubComponent } from './MySubComponent';
|
|
import { useMyFeature } from '../hooks/useMyFeature';
|
|
import { myFeatureHelpers } from '../helpers/myFeatureHelpers';
|
|
```
|
|
|
|
**Use single quotes** for all imports (project standard)
|
|
|
|
---
|
|
|
|
## Public API Pattern
|
|
|
|
### feature/index.ts
|
|
|
|
Export public API from feature for clean imports:
|
|
|
|
```typescript
|
|
// features/my-feature/index.ts
|
|
|
|
// Export main components
|
|
export { MyFeatureMain } from './components/MyFeatureMain';
|
|
export { MyFeatureHeader } from './components/MyFeatureHeader';
|
|
|
|
// Export hooks
|
|
export { useMyFeature } from './hooks/useMyFeature';
|
|
export { useSuspenseMyFeature } from './hooks/useSuspenseMyFeature';
|
|
|
|
// Export API
|
|
export { myFeatureApi } from './api/myFeatureApi';
|
|
|
|
// Export types
|
|
export type { MyFeatureData, MyFeatureConfig } from './types';
|
|
```
|
|
|
|
**Usage:**
|
|
```typescript
|
|
// ✅ Clean import from feature index
|
|
import { MyFeatureMain, useMyFeature } from '~features/my-feature';
|
|
|
|
// ❌ Avoid deep imports (but OK if needed)
|
|
import { MyFeatureMain } from '~features/my-feature/components/MyFeatureMain';
|
|
```
|
|
|
|
---
|
|
|
|
## Directory Structure Visualization
|
|
|
|
```
|
|
src/
|
|
├── features/ # Domain-specific features
|
|
│ ├── posts/
|
|
│ │ ├── api/
|
|
│ │ ├── components/
|
|
│ │ ├── hooks/
|
|
│ │ ├── helpers/
|
|
│ │ ├── types/
|
|
│ │ └── index.ts
|
|
│ ├── blogs/
|
|
│ └── auth/
|
|
│
|
|
├── components/ # Reusable components
|
|
│ ├── SuspenseLoader/
|
|
│ ├── CustomAppBar/
|
|
│ ├── ErrorBoundary/
|
|
│ └── LoadingOverlay/
|
|
│
|
|
├── routes/ # TanStack Router routes
|
|
│ ├── __root.tsx
|
|
│ ├── index.tsx
|
|
│ ├── project-catalog/
|
|
│ │ ├── index.tsx
|
|
│ │ └── create/
|
|
│ └── blogs/
|
|
│
|
|
├── hooks/ # Shared hooks
|
|
│ ├── useAuth.ts
|
|
│ ├── useMuiSnackbar.ts
|
|
│ └── useDebounce.ts
|
|
│
|
|
├── lib/ # Shared utilities
|
|
│ ├── apiClient.ts
|
|
│ └── utils.ts
|
|
│
|
|
├── types/ # Shared TypeScript types
|
|
│ ├── user.ts
|
|
│ ├── post.ts
|
|
│ └── common.ts
|
|
│
|
|
├── config/ # Configuration
|
|
│ └── theme.ts
|
|
│
|
|
└── App.tsx # Root component
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
**Key Principles:**
|
|
1. **features/** for domain-specific code
|
|
2. **components/** for truly reusable UI
|
|
3. Use subdirectories: api/, components/, hooks/, helpers/, types/
|
|
4. Import aliases for clean imports (@/, ~types, ~components, ~features)
|
|
5. Consistent naming: PascalCase components, camelCase utilities
|
|
6. Export public API from feature index.ts
|
|
|
|
**See Also:**
|
|
- [component-patterns.md](component-patterns.md) - Component structure
|
|
- [data-fetching.md](data-fetching.md) - API service patterns
|
|
- [complete-examples.md](complete-examples.md) - Full feature example |