# 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