401 lines
8.8 KiB
Markdown
401 lines
8.8 KiB
Markdown
# Repository Structure Document Authoring Guide
|
|
|
|
## Basic Principles
|
|
|
|
### 1. Clarify Roles
|
|
|
|
Each directory should have a single, well-defined role.
|
|
|
|
**Bad example**:
|
|
```
|
|
src/
|
|
├── stuff/ # Vague
|
|
├── misc/ # Miscellaneous
|
|
└── utils/ # Too generic
|
|
```
|
|
|
|
**Good example**:
|
|
```
|
|
src/
|
|
├── commands/ # CLI command implementations
|
|
├── services/ # Business logic
|
|
├── repositories/ # Data persistence
|
|
└── validators/ # Input validation
|
|
```
|
|
|
|
### 2. Enforce Layer Separation
|
|
|
|
Reflect the architecture's layer structure in the directory structure:
|
|
|
|
```
|
|
src/
|
|
├── ui/ # UI layer
|
|
│ └── cli/ # CLI implementation
|
|
├── services/ # Service layer
|
|
│ └── task/ # Task management service
|
|
└── repositories/ # Data layer
|
|
└── task/ # Task repository
|
|
```
|
|
|
|
### 3. Split by Technical Concern (Baseline)
|
|
|
|
Split directories by related technical concerns:
|
|
|
|
**Basic structure**:
|
|
```
|
|
src/
|
|
├── commands/ # CLI commands
|
|
├── services/ # Business logic
|
|
├── repositories/ # Data persistence
|
|
└── types/ # Type definitions
|
|
```
|
|
|
|
**Mapping to the layer structure**:
|
|
```
|
|
CLI/UI layer → commands/, cli/
|
|
Service layer → services/
|
|
Data layer → repositories/, storage/
|
|
```
|
|
|
|
## Designing the Directory Structure
|
|
|
|
### Expressing the Layer Structure
|
|
|
|
```typescript
|
|
// Bad example: flat structure
|
|
src/
|
|
├── TaskCLI.ts
|
|
├── TaskService.ts
|
|
├── TaskRepository.ts
|
|
├── UserCLI.ts
|
|
├── UserService.ts
|
|
└── UserRepository.ts
|
|
|
|
// Good example: clear layers
|
|
src/
|
|
├── cli/
|
|
│ ├── TaskCLI.ts
|
|
│ └── UserCLI.ts
|
|
├── services/
|
|
│ ├── TaskService.ts
|
|
│ └── UserService.ts
|
|
└── repositories/
|
|
├── TaskRepository.ts
|
|
└── UserRepository.ts
|
|
```
|
|
|
|
### Placement of the Test Directory
|
|
|
|
**Recommended structure**:
|
|
```
|
|
project/
|
|
├── src/
|
|
│ └── services/
|
|
│ └── TaskService.ts
|
|
└── tests/
|
|
├── unit/
|
|
│ └── services/
|
|
│ └── TaskService.test.ts
|
|
├── integration/
|
|
└── e2e/
|
|
```
|
|
|
|
**Rationale**:
|
|
- Test code is separated from production code
|
|
- Easy to exclude tests at build time
|
|
- Can be organized by test type
|
|
|
|
## Naming Convention Best Practices
|
|
|
|
### Principles for Directory Names
|
|
|
|
**1. Use the plural form (layer directories)**
|
|
```
|
|
✅ services/
|
|
✅ repositories/
|
|
✅ controllers/
|
|
|
|
❌ service/
|
|
❌ repository/
|
|
❌ controller/
|
|
```
|
|
|
|
Rationale: They hold multiple files
|
|
|
|
**2. Use kebab-case**
|
|
```
|
|
✅ task-management/
|
|
✅ user-authentication/
|
|
|
|
❌ TaskManagement/
|
|
❌ userAuthentication/
|
|
```
|
|
|
|
Rationale: Compatibility with URLs and the file system
|
|
|
|
**3. Use specific names**
|
|
```
|
|
✅ validators/ # Input validation
|
|
✅ formatters/ # Data formatting
|
|
✅ parsers/ # Data parsing
|
|
|
|
❌ utils/ # Too generic
|
|
❌ helpers/ # Vague
|
|
❌ common/ # Meaningless
|
|
```
|
|
|
|
### Principles for File Names
|
|
|
|
**1. Class files: PascalCase + role suffix**
|
|
```typescript
|
|
// Service classes
|
|
TaskService.ts
|
|
UserAuthenticationService.ts
|
|
|
|
// Repository classes
|
|
TaskRepository.ts
|
|
UserRepository.ts
|
|
|
|
// Controller classes
|
|
TaskController.ts
|
|
```
|
|
|
|
**2. Function files: camelCase + start with a verb**
|
|
```typescript
|
|
// Utility functions
|
|
formatDate.ts
|
|
validateEmail.ts
|
|
parseCommandArguments.ts
|
|
```
|
|
|
|
**3. Type definition files: PascalCase or kebab-case**
|
|
```typescript
|
|
// Interface definitions
|
|
Task.ts
|
|
UserProfile.ts
|
|
|
|
// Type definition collections
|
|
task-types.d.ts
|
|
api-types.d.ts
|
|
```
|
|
|
|
**4. Constant files: UPPER_SNAKE_CASE or kebab-case**
|
|
```typescript
|
|
// Constant definitions
|
|
API_ENDPOINTS.ts
|
|
ERROR_MESSAGES.ts
|
|
|
|
// or
|
|
api-endpoints.ts
|
|
error-messages.ts
|
|
```
|
|
|
|
## Managing Dependencies
|
|
|
|
### Dependency Rules Between Layers
|
|
|
|
```typescript
|
|
// ✅ Good example: dependency from an upper layer to a lower layer
|
|
// cli/TaskCLI.ts
|
|
import { TaskService } from '../services/TaskService';
|
|
|
|
class TaskCLI {
|
|
constructor(private taskService: TaskService) {}
|
|
}
|
|
|
|
// ❌ Bad example: dependency from a lower layer to an upper layer
|
|
// services/TaskService.ts
|
|
import { TaskCLI } from '../cli/TaskCLI'; // Forbidden!
|
|
```
|
|
|
|
### Avoiding Circular Dependencies
|
|
|
|
**Problematic code**:
|
|
```typescript
|
|
// services/TaskService.ts
|
|
import { UserService } from './UserService';
|
|
|
|
export class TaskService {
|
|
constructor(private userService: UserService) {}
|
|
}
|
|
|
|
// services/UserService.ts
|
|
import { TaskService } from './TaskService'; // Circular dependency!
|
|
|
|
export class UserService {
|
|
constructor(private taskService: TaskService) {}
|
|
}
|
|
```
|
|
|
|
**Solution 1: Extract shared type definitions**
|
|
```typescript
|
|
// types/Service.ts
|
|
export interface ITaskService { /* ... */ }
|
|
export interface IUserService { /* ... */ }
|
|
|
|
// services/TaskService.ts
|
|
import type { IUserService } from '../types/Service';
|
|
|
|
export class TaskService {
|
|
constructor(private userService: IUserService) {}
|
|
}
|
|
|
|
// services/UserService.ts
|
|
import type { ITaskService } from '../types/Service';
|
|
|
|
export class UserService {
|
|
constructor(private taskService: ITaskService) {}
|
|
}
|
|
```
|
|
|
|
**Solution 2: Rethink the dependencies**
|
|
```typescript
|
|
// Extract shared functionality into a separate service
|
|
// services/NotificationService.ts
|
|
export class NotificationService {
|
|
notifyTaskAssignment(taskId: string, userId: string): void {
|
|
// Notification handling
|
|
}
|
|
}
|
|
|
|
// services/TaskService.ts
|
|
import { NotificationService } from './NotificationService';
|
|
|
|
export class TaskService {
|
|
constructor(private notificationService: NotificationService) {}
|
|
}
|
|
|
|
// services/UserService.ts
|
|
import { NotificationService } from './NotificationService';
|
|
|
|
export class UserService {
|
|
constructor(private notificationService: NotificationService) {}
|
|
}
|
|
```
|
|
|
|
## Scaling Strategy
|
|
|
|
### Recommended Structure
|
|
|
|
**Standard pattern**:
|
|
```
|
|
src/
|
|
├── commands/
|
|
│ └── TaskCommand.ts
|
|
├── services/
|
|
│ ├── TaskService.ts
|
|
│ └── UserService.ts
|
|
├── repositories/
|
|
│ ├── TaskRepository.ts
|
|
│ └── UserRepository.ts
|
|
├── types/
|
|
│ ├── Task.ts
|
|
│ └── User.ts
|
|
├── validators/
|
|
│ └── TaskValidator.ts
|
|
└── index.ts
|
|
```
|
|
|
|
**Rationale**:
|
|
- Responsibilities are clear per layer
|
|
- No later refactoring required
|
|
- Easy to standardize across a team
|
|
|
|
### Timing for Module Separation
|
|
|
|
**Signs that separation should be considered**:
|
|
1. A directory contains 10 or more files
|
|
2. Related functionality is grouped together
|
|
3. It can be tested independently
|
|
4. It has few dependencies on other features
|
|
|
|
**Separation procedure**:
|
|
```typescript
|
|
// Before: everything placed in services/
|
|
services/
|
|
├── TaskService.ts
|
|
├── TaskValidationService.ts
|
|
├── TaskNotificationService.ts
|
|
├── UserService.ts
|
|
└── UserAuthService.ts
|
|
|
|
// After: modularized by feature
|
|
modules/
|
|
├── task/
|
|
│ ├── TaskService.ts
|
|
│ ├── TaskValidationService.ts
|
|
│ └── TaskNotificationService.ts
|
|
└── user/
|
|
├── UserService.ts
|
|
└── UserAuthService.ts
|
|
```
|
|
|
|
## Handling Special Cases
|
|
|
|
### Placement of Shared Code
|
|
|
|
**shared/ or common/ directory**
|
|
```
|
|
src/
|
|
├── shared/
|
|
│ ├── utils/ # General-purpose utilities
|
|
│ ├── types/ # Shared type definitions
|
|
│ └── constants/ # Shared constants
|
|
├── commands/
|
|
├── services/
|
|
└── repositories/
|
|
```
|
|
|
|
**Rules**:
|
|
- Only things genuinely used across multiple layers
|
|
- Do not include anything used in only a single layer
|
|
|
|
### Managing Configuration Files (when applicable)
|
|
|
|
```
|
|
config/
|
|
├── default.ts # Default settings
|
|
└── constants.ts # Constant definitions
|
|
```
|
|
|
|
### Managing Scripts (when applicable)
|
|
|
|
```
|
|
scripts/
|
|
├── build.sh # Build script
|
|
└── dev-tools.ts # Development helper script
|
|
```
|
|
|
|
## Document Placement
|
|
|
|
### Document Types and Their Locations
|
|
|
|
**Project root**:
|
|
- `README.md`: Project overview
|
|
- `CONTRIBUTING.md`: Contribution guide
|
|
- `LICENSE`: License
|
|
|
|
**docs/ directory**:
|
|
- `product-requirements.md`: PRD
|
|
- `functional-design.md`: Functional design document
|
|
- `architecture.md`: Architecture design document
|
|
- `repository-structure.md`: This document
|
|
- `development-guidelines.md`: Development guidelines
|
|
- `glossary.md`: Glossary
|
|
|
|
**Within the source code**:
|
|
- TSDoc/JSDoc comments: Descriptions of functions and classes
|
|
|
|
## Checklist
|
|
|
|
- [ ] Each directory's role is clearly defined
|
|
- [ ] The layer structure is reflected in the directories
|
|
- [ ] Naming conventions are consistent
|
|
- [ ] The placement policy for test code is decided
|
|
- [ ] Dependency rules are clear
|
|
- [ ] There are no circular dependencies
|
|
- [ ] A scaling strategy has been considered
|
|
- [ ] Placement rules for shared code are defined
|
|
- [ ] A management method for configuration files is decided
|
|
- [ ] Document locations are clear
|