# ADR-044: Frontend Feature Organization Pattern
**Date**: 2026-01-09
**Status**: Accepted
**Implemented**: 2026-01-09
## Context
The React frontend has grown to include multiple distinct features:
- Flyer viewing and management
- Shopping list creation
- Budget tracking and charts
- Voice assistant
- User personalization
- Admin dashboard
Without clear organization, code becomes scattered across generic folders (`/components`, `/hooks`, `/utils`), making it hard to:
1. Understand feature boundaries
2. Find related code
3. Refactor or remove features
4. Onboard new developers
## Decision
We will adopt a **feature-based folder structure** where each major feature is self-contained in its own directory under `/features`. Shared code lives in dedicated top-level folders.
### Design Principles
- **Colocation**: Keep related code together (components, hooks, types, utils).
- **Feature Independence**: Features should minimize cross-dependencies.
- **Shared Extraction**: Only extract to shared folders when truly reused.
- **Flat Within Features**: Avoid deep nesting within feature folders.
## Implementation Details
### Directory Structure
```
src/
├── features/ # Feature modules
│ ├── flyer/ # Flyer viewing/management
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── types.ts
│ │ └── index.ts
│ ├── shopping/ # Shopping lists
│ │ ├── components/
│ │ ├── hooks/
│ │ └── index.ts
│ ├── charts/ # Budget/analytics charts
│ │ ├── components/
│ │ └── index.ts
│ ├── voice-assistant/ # Voice commands
│ │ ├── components/
│ │ └── index.ts
│ └── admin/ # Admin dashboard
│ ├── components/
│ └── index.ts
├── components/ # Shared UI components
│ ├── ui/ # Primitive components (Button, Input, etc.)
│ ├── layout/ # Layout components (Header, Footer, etc.)
│ └── common/ # Shared composite components
├── hooks/ # Shared hooks
│ ├── queries/ # TanStack Query hooks
│ ├── mutations/ # TanStack Mutation hooks
│ └── utils/ # Utility hooks (useDebounce, etc.)
├── providers/ # React context providers
│ ├── AppProviders.tsx
│ ├── UserDataProvider.tsx
│ └── FlyersProvider.tsx
├── pages/ # Route page components
├── services/ # API clients, external services
├── types/ # Shared TypeScript types
├── utils/ # Shared utility functions
└── lib/ # Third-party library wrappers
```
### Feature Module Structure
Each feature follows a consistent internal structure:
```
features/flyer/
├── components/
│ ├── FlyerCard.tsx
│ ├── FlyerGrid.tsx
│ ├── FlyerUploader.tsx
│ ├── FlyerItemList.tsx
│ └── index.ts # Re-exports all components
├── hooks/
│ ├── useFlyerDetails.ts
│ ├── useFlyerUpload.ts
│ └── index.ts # Re-exports all hooks
├── types.ts # Feature-specific types
├── utils.ts # Feature-specific utilities
└── index.ts # Public API of the feature
```
### Feature Index File
Each feature has an `index.ts` that defines its public API:
```typescript
// features/flyer/index.ts
export { FlyerCard, FlyerGrid, FlyerUploader } from './components';
export { useFlyerDetails, useFlyerUpload } from './hooks';
export type { FlyerViewProps, FlyerUploadState } from './types';
```
### Import Patterns
```typescript
// Importing from a feature (preferred)
import { FlyerCard, useFlyerDetails } from '@/features/flyer';
// Importing shared components
import { Button, Card } from '@/components/ui';
import { useDebounce } from '@/hooks/utils';
// Avoid: reaching into feature internals
// import { FlyerCard } from '@/features/flyer/components/FlyerCard';
```
### Provider Organization
Located in `src/providers/`:
```typescript
// AppProviders.tsx - Composes all providers
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
### Query/Mutation Hook Organization
Located in `src/hooks/`:
```typescript
// hooks/queries/useFlyersQuery.ts
export function useFlyersQuery(options?: { storeId?: number }) {
return useQuery({
queryKey: ['flyers', options],
queryFn: () => flyerService.getFlyers(options),
staleTime: 5 * 60 * 1000,
});
}
// hooks/mutations/useFlyerUploadMutation.ts
export function useFlyerUploadMutation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: flyerService.uploadFlyer,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['flyers'] });
},
});
}
```
### Page Components
Pages are thin wrappers that compose feature components:
```typescript
// pages/Flyers.tsx
import { FlyerGrid, FlyerUploader } from '@/features/flyer';
import { PageLayout } from '@/components/layout';
export function FliversPage() {
return (
);
}
```
### Cross-Feature Communication
When features need to communicate, use:
1. **Shared State Providers**: For global state (user, theme).
2. **Query Invalidation**: For data synchronization.
3. **Event Bus**: For loose coupling (see ADR-036).
```typescript
// Feature A triggers update
const uploadMutation = useFlyerUploadMutation();
await uploadMutation.mutateAsync(file);
// Query invalidation automatically updates Feature B's flyer list
```
## Naming Conventions
| Item | Convention | Example |
| -------------- | -------------------- | -------------------- |
| Feature folder | kebab-case | `voice-assistant/` |
| Component file | PascalCase | `FlyerCard.tsx` |
| Hook file | camelCase with `use` | `useFlyerDetails.ts` |
| Type file | lowercase | `types.ts` |
| Utility file | lowercase | `utils.ts` |
| Index file | lowercase | `index.ts` |
## When to Create a New Feature
Create a new feature folder when:
1. The functionality is distinct and self-contained.
2. It has its own set of components, hooks, and potentially types.
3. It could theoretically be extracted into a separate package.
4. It has minimal dependencies on other features.
Do NOT create a feature folder for:
- A single reusable component (use `components/`).
- A single utility function (use `utils/`).
- A single hook (use `hooks/`).
## Consequences
### Positive
- **Discoverability**: Easy to find all code related to a feature.
- **Encapsulation**: Features have clear boundaries and public APIs.
- **Refactoring**: Can modify or remove features with confidence.
- **Scalability**: Supports team growth with feature ownership.
- **Testing**: Can test features in isolation.
### Negative
- **Duplication Risk**: Similar utilities might be duplicated across features.
- **Decision Overhead**: Must decide when to extract to shared folders.
- **Import Verbosity**: Feature imports can be longer.
### Mitigation
- Regular refactoring sessions to extract shared code.
- Lint rules to prevent importing from feature internals.
- Code review focus on proper feature boundaries.
## Key Directories
- `src/features/flyer/` - Flyer viewing and management
- `src/features/shopping/` - Shopping list functionality
- `src/features/charts/` - Budget and analytics charts
- `src/features/voice-assistant/` - Voice command interface
- `src/features/admin/` - Admin dashboard
- `src/components/ui/` - Shared primitive components
- `src/hooks/queries/` - TanStack Query hooks
- `src/providers/` - React context providers
## Related ADRs
- [ADR-005](./0005-frontend-state-management-and-server-cache-strategy.md) - State Management
- [ADR-012](./0012-frontend-component-library-and-design-system.md) - Component Library
- [ADR-026](./0026-standardized-client-side-structured-logging.md) - Client Logging