# ADR-047: Project File and Folder Organization **Date**: 2026-01-09 **Status**: Proposed **Effort**: XL (Major reorganization across entire codebase) ## Context The project has grown organically with a mix of organizational patterns: - **By Type**: Components, hooks, middleware, utilities, types all in flat directories - **By Feature**: Routes, database modules, and partial feature directories - **Mixed Concerns**: Frontend and backend code intermingled in `src/` Current pain points: 1. **Flat services directory**: 75+ files with no subdirectory grouping 2. **Monolithic types.ts**: 750+ lines, unclear when to add new types 3. **Flat components directory**: 43+ components at root level 4. **Incomplete feature modules**: Features contain only UI, not domain logic 5. **No clear frontend/backend separation**: Both share `src/` root As the project scales, these issues compound, making navigation, refactoring, and onboarding increasingly difficult. ## Decision We will adopt a **domain-driven organization** with clear separation between: 1. **Client code** (React, browser-only) 2. **Server code** (Express, Node-only) 3. **Shared code** (Types, utilities used by both) Within each layer, organize by **feature/domain** rather than by file type. ### Design Principles - **Colocation**: Related code lives together (components, hooks, types, tests) - **Explicit Boundaries**: Clear separation between client, server, and shared - **Feature Ownership**: Each domain owns its entire vertical slice - **Discoverability**: New developers can find code by thinking about features, not file types - **Incremental Migration**: Structure supports gradual transition from current layout ## Target Directory Structure ``` src/ ├── client/ # React frontend (browser-only code) │ ├── app/ # App shell and routing │ │ ├── App.tsx │ │ ├── routes.tsx │ │ └── providers/ # React context providers │ │ ├── AppProviders.tsx │ │ ├── AuthProvider.tsx │ │ ├── FlyersProvider.tsx │ │ └── index.ts │ │ │ ├── features/ # Feature modules (UI + hooks + types) │ │ ├── auth/ │ │ │ ├── components/ │ │ │ │ ├── LoginForm.tsx │ │ │ │ ├── RegisterForm.tsx │ │ │ │ └── index.ts │ │ │ ├── hooks/ │ │ │ │ ├── useAuth.ts │ │ │ │ ├── useLogin.ts │ │ │ │ └── index.ts │ │ │ ├── types.ts │ │ │ └── index.ts │ │ │ │ │ ├── flyer/ │ │ │ ├── components/ │ │ │ │ ├── FlyerCard.tsx │ │ │ │ ├── FlyerGrid.tsx │ │ │ │ ├── FlyerUploader.tsx │ │ │ │ ├── BulkImporter.tsx │ │ │ │ └── index.ts │ │ │ ├── hooks/ │ │ │ │ ├── useFlyersQuery.ts │ │ │ │ ├── useFlyerUploadMutation.ts │ │ │ │ └── index.ts │ │ │ ├── types.ts │ │ │ └── index.ts │ │ │ │ │ ├── shopping/ │ │ │ ├── components/ │ │ │ ├── hooks/ │ │ │ ├── types.ts │ │ │ └── index.ts │ │ │ │ │ ├── recipes/ │ │ │ ├── components/ │ │ │ ├── hooks/ │ │ │ └── index.ts │ │ │ │ │ ├── charts/ │ │ │ ├── components/ │ │ │ └── index.ts │ │ │ │ │ ├── voice-assistant/ │ │ │ ├── components/ │ │ │ └── index.ts │ │ │ │ │ ├── user/ │ │ │ ├── components/ │ │ │ ├── hooks/ │ │ │ └── index.ts │ │ │ │ │ ├── gamification/ │ │ │ ├── components/ │ │ │ ├── hooks/ │ │ │ └── index.ts │ │ │ │ │ └── admin/ │ │ ├── components/ │ │ ├── hooks/ │ │ ├── pages/ # Admin-specific pages │ │ └── index.ts │ │ │ ├── pages/ # Route page components │ │ ├── HomePage.tsx │ │ ├── MyDealsPage.tsx │ │ ├── UserProfilePage.tsx │ │ └── index.ts │ │ │ ├── components/ # Shared UI components │ │ ├── ui/ # Primitive components (design system) │ │ │ ├── Button.tsx │ │ │ ├── Card.tsx │ │ │ ├── Input.tsx │ │ │ ├── Modal.tsx │ │ │ ├── Badge.tsx │ │ │ └── index.ts │ │ │ │ │ ├── layout/ # Layout components │ │ │ ├── Header.tsx │ │ │ ├── Footer.tsx │ │ │ ├── Sidebar.tsx │ │ │ ├── PageLayout.tsx │ │ │ └── index.ts │ │ │ │ │ ├── feedback/ # User feedback components │ │ │ ├── LoadingSpinner.tsx │ │ │ ├── ErrorMessage.tsx │ │ │ ├── Toast.tsx │ │ │ ├── ConfirmDialog.tsx │ │ │ └── index.ts │ │ │ │ │ ├── forms/ # Form components │ │ │ ├── FormField.tsx │ │ │ ├── SearchInput.tsx │ │ │ ├── DatePicker.tsx │ │ │ └── index.ts │ │ │ │ │ ├── icons/ # Icon components │ │ │ ├── ChevronIcon.tsx │ │ │ ├── UserIcon.tsx │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ ├── hooks/ # Shared hooks (not feature-specific) │ │ ├── useDebounce.ts │ │ ├── useLocalStorage.ts │ │ ├── useMediaQuery.ts │ │ └── index.ts │ │ │ ├── services/ # Client-side services (API clients) │ │ ├── apiClient.ts │ │ ├── logger.client.ts │ │ └── index.ts │ │ │ ├── lib/ # Third-party library wrappers │ │ ├── queryClient.ts │ │ ├── toast.ts │ │ └── index.ts │ │ │ └── styles/ # Global styles │ ├── globals.css │ └── tailwind.css │ ├── server/ # Express backend (Node-only code) │ ├── app.ts # Express app setup │ ├── server.ts # Server entry point │ │ │ ├── domains/ # Domain modules (business logic) │ │ ├── auth/ │ │ │ ├── auth.service.ts │ │ │ ├── auth.routes.ts │ │ │ ├── auth.controller.ts │ │ │ ├── auth.repository.ts │ │ │ ├── auth.types.ts │ │ │ ├── auth.service.test.ts │ │ │ ├── auth.routes.test.ts │ │ │ └── index.ts │ │ │ │ │ ├── flyer/ │ │ │ ├── flyer.service.ts │ │ │ ├── flyer.routes.ts │ │ │ ├── flyer.controller.ts │ │ │ ├── flyer.repository.ts │ │ │ ├── flyer.types.ts │ │ │ ├── flyer.processing.ts # Flyer-specific processing logic │ │ │ ├── flyer.ai.ts # AI integration for flyers │ │ │ └── index.ts │ │ │ │ │ ├── user/ │ │ │ ├── user.service.ts │ │ │ ├── user.routes.ts │ │ │ ├── user.controller.ts │ │ │ ├── user.repository.ts │ │ │ └── index.ts │ │ │ │ │ ├── shopping/ │ │ │ ├── shopping.service.ts │ │ │ ├── shopping.routes.ts │ │ │ ├── shopping.repository.ts │ │ │ └── index.ts │ │ │ │ │ ├── recipe/ │ │ │ ├── recipe.service.ts │ │ │ ├── recipe.routes.ts │ │ │ ├── recipe.repository.ts │ │ │ └── index.ts │ │ │ │ │ ├── gamification/ │ │ │ ├── gamification.service.ts │ │ │ ├── gamification.routes.ts │ │ │ ├── gamification.repository.ts │ │ │ └── index.ts │ │ │ │ │ ├── notification/ │ │ │ ├── notification.service.ts │ │ │ ├── email.service.ts │ │ │ └── index.ts │ │ │ │ │ ├── ai/ │ │ │ ├── ai.service.ts │ │ │ ├── ai.client.ts │ │ │ ├── ai.prompts.ts │ │ │ └── index.ts │ │ │ │ │ └── admin/ │ │ ├── admin.routes.ts │ │ ├── admin.controller.ts │ │ ├── admin.service.ts │ │ └── index.ts │ │ │ ├── middleware/ # Express middleware │ │ ├── auth.middleware.ts │ │ ├── validation.middleware.ts │ │ ├── errorHandler.middleware.ts │ │ ├── rateLimit.middleware.ts │ │ ├── fileUpload.middleware.ts │ │ └── index.ts │ │ │ ├── infrastructure/ # Cross-cutting infrastructure │ │ ├── database/ │ │ │ ├── pool.ts │ │ │ ├── migrations/ │ │ │ └── seeds/ │ │ │ │ │ ├── cache/ │ │ │ ├── redis.ts │ │ │ └── cacheService.ts │ │ │ │ │ ├── queue/ │ │ │ ├── queueService.ts │ │ │ ├── workers/ │ │ │ │ ├── email.worker.ts │ │ │ │ ├── flyer.worker.ts │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ │ │ │ ├── jobs/ │ │ │ ├── cronJobs.ts │ │ │ ├── dailyAnalytics.job.ts │ │ │ └── index.ts │ │ │ │ │ └── logging/ │ │ ├── logger.ts │ │ └── index.ts │ │ │ ├── config/ # Server configuration │ │ ├── database.config.ts │ │ ├── redis.config.ts │ │ ├── auth.config.ts │ │ └── index.ts │ │ │ └── utils/ # Server-only utilities │ ├── imageProcessor.ts │ ├── geocoding.ts │ └── index.ts │ ├── shared/ # Code shared between client and server │ ├── types/ # Shared TypeScript types │ │ ├── entities/ # Domain entities │ │ │ ├── flyer.types.ts │ │ │ ├── user.types.ts │ │ │ ├── shopping.types.ts │ │ │ ├── recipe.types.ts │ │ │ └── index.ts │ │ │ │ │ ├── api/ # API contract types │ │ │ ├── requests.ts │ │ │ ├── responses.ts │ │ │ ├── errors.ts │ │ │ └── index.ts │ │ │ │ │ └── index.ts │ │ │ ├── schemas/ # Zod validation schemas │ │ ├── flyer.schema.ts │ │ ├── user.schema.ts │ │ ├── auth.schema.ts │ │ └── index.ts │ │ │ ├── constants/ # Shared constants │ │ ├── categories.ts │ │ ├── errorCodes.ts │ │ └── index.ts │ │ │ └── utils/ # Isomorphic utilities │ ├── formatting.ts │ ├── validation.ts │ └── index.ts │ ├── tests/ # Test infrastructure │ ├── setup/ │ │ ├── vitest.setup.ts │ │ └── testDb.setup.ts │ │ │ ├── fixtures/ │ │ ├── mockFactories.ts │ │ ├── sampleFlyers/ │ │ └── index.ts │ │ │ ├── utils/ │ │ ├── testHelpers.ts │ │ └── index.ts │ │ │ ├── integration/ # Integration tests │ │ ├── api/ │ │ └── database/ │ │ │ └── e2e/ # End-to-end tests │ └── flows/ │ ├── scripts/ # Build and utility scripts │ ├── seed.ts │ ├── migrate.ts │ └── generateTypes.ts │ └── index.tsx # Client entry point ``` ## Domain Module Structure Each server domain follows a consistent structure: ``` domains/flyer/ ├── flyer.service.ts # Business logic ├── flyer.routes.ts # Express routes ├── flyer.controller.ts # Route handlers ├── flyer.repository.ts # Database access ├── flyer.types.ts # Domain-specific types ├── flyer.service.test.ts # Service tests ├── flyer.routes.test.ts # Route tests └── index.ts # Public API ``` ### Domain Index Pattern Each domain exports a clean public API: ```typescript // server/domains/flyer/index.ts export { FlyerService } from './flyer.service'; export { flyerRoutes } from './flyer.routes'; export type { FlyerWithItems, FlyerCreateInput } from './flyer.types'; ``` ## Client Feature Module Structure Each client feature follows a consistent structure: ``` client/features/flyer/ ├── components/ │ ├── FlyerCard.tsx │ ├── FlyerCard.test.tsx │ ├── FlyerGrid.tsx │ └── index.ts ├── hooks/ │ ├── useFlyersQuery.ts │ ├── useFlyerUploadMutation.ts │ └── index.ts ├── types.ts # Feature-specific client types └── index.ts # Public API ``` ## Import Path Aliases Configure TypeScript and bundler for clean imports: ```typescript // tsconfig.json paths { "paths": { "@/client/*": ["src/client/*"], "@/server/*": ["src/server/*"], "@/shared/*": ["src/shared/*"], "@/tests/*": ["src/tests/*"] } } // Usage examples import { Button, Card } from '@/client/components/ui'; import { useFlyersQuery } from '@/client/features/flyer'; import { FlyerService } from '@/server/domains/flyer'; import type { Flyer } from '@/shared/types/entities'; ``` ## Migration Strategy Given the scope of this reorganization, migrate incrementally: ### Phase 1: Create Directory Structure 1. Create `client/`, `server/`, `shared/` directories 2. Set up path aliases in tsconfig.json 3. Update build configuration (Vite) ### Phase 2: Migrate Shared Code 1. Move types to `shared/types/` 2. Move schemas to `shared/schemas/` 3. Move shared utils to `shared/utils/` 4. Update imports across codebase ### Phase 3: Migrate Server Code 1. Create `server/domains/` structure 2. Move one domain at a time (start with `auth` or `user`) 3. Move each service + routes + repository together 4. Update route registration in app.ts 5. Run tests after each domain migration ### Phase 4: Migrate Client Code 1. Create `client/features/` structure 2. Move components into features 3. Move hooks into features or shared hooks 4. Move pages to `client/pages/` 5. Organize shared components into categories ### Phase 5: Cleanup 1. Remove empty old directories 2. Update all remaining imports 3. Update CI/CD paths if needed 4. Update documentation ## Naming Conventions | Item | Convention | Example | | ----------------- | -------------------- | ----------------------- | | Domain directory | lowercase | `flyer/`, `shopping/` | | Feature directory | kebab-case | `voice-assistant/` | | Service file | domain.service.ts | `flyer.service.ts` | | Route file | domain.routes.ts | `flyer.routes.ts` | | Repository file | domain.repository.ts | `flyer.repository.ts` | | Component file | PascalCase.tsx | `FlyerCard.tsx` | | Hook file | camelCase.ts | `useFlyersQuery.ts` | | Type file | domain.types.ts | `flyer.types.ts` | | Test file | \*.test.ts(x) | `flyer.service.test.ts` | | Index file | index.ts | `index.ts` | ## File Placement Guidelines **Where does this file go?** | If the file is... | Place it in... | | ------------------------------------ | ------------------------------------------------ | | Used only by React | `client/` | | Used only by Express/Node | `server/` | | TypeScript types used by both | `shared/types/` | | Zod schemas | `shared/schemas/` | | React component for one feature | `client/features/{feature}/components/` | | React component used across features | `client/components/` | | React hook for one feature | `client/features/{feature}/hooks/` | | React hook used across features | `client/hooks/` | | Business logic for a domain | `server/domains/{domain}/` | | Database access for a domain | `server/domains/{domain}/{domain}.repository.ts` | | Express middleware | `server/middleware/` | | Background job worker | `server/infrastructure/queue/workers/` | | Cron job definition | `server/infrastructure/jobs/` | | Test factory/fixture | `tests/fixtures/` | ## Consequences ### Positive - **Clear Boundaries**: Frontend, backend, and shared code are explicitly separated - **Feature Discoverability**: Find all code for a feature in one place - **Parallel Development**: Teams can work on domains independently - **Easier Refactoring**: Domain boundaries make changes localized - **Better Onboarding**: New developers navigate by feature, not file type - **Scalability**: Structure supports growth without becoming unwieldy ### Negative - **Large Migration Effort**: Significant one-time cost (XL effort) - **Import Updates**: All imports need updating - **Learning Curve**: Team must learn new structure - **Merge Conflicts**: In-flight PRs will need rebasing ### Mitigation - Use automated tools (e.g., `ts-morph`) to update imports - Migrate one domain/feature at a time - Create a migration checklist and track progress - Coordinate with team to minimize in-flight work during migration phases - Consider using feature flags to ship incrementally ## Key Differences from Current Structure | Aspect | Current | Target | | ---------------- | -------------------------- | ----------------------------------------- | | Frontend/Backend | Mixed in `src/` | Separated in `client/` and `server/` | | Services | Flat directory (75+ files) | Grouped by domain | | Components | Flat directory (43+ files) | Categorized (ui, layout, feedback, forms) | | Types | Monolithic `types.ts` | Split by entity in `shared/types/` | | Features | UI-only | Full vertical slice (UI + hooks + types) | | Routes | Separate from services | Co-located in domain | | Tests | Co-located + `tests/` | Co-located + `tests/` for fixtures | ## Related ADRs - [ADR-034](./0034-repository-pattern-standards.md) - Repository Pattern (affects domain structure) - [ADR-035](./0035-service-layer-architecture.md) - Service Layer (affects domain structure) - [ADR-044](./0044-frontend-feature-organization.md) - Frontend Features (this ADR supersedes it) - [ADR-045](./0045-test-data-factories-and-fixtures.md) - Test Fixtures (affects tests/ directory)