# 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)
