Files
Ken Yasue 80e195b3dc chore: initialize repository with project docs and tooling
- Add persistent docs: PRD, functional design, architecture, repository structure, development guidelines, milestones

- Add brainstorming notes (docs/ideas/)

- Configure tooling: package.json, tsconfig, eslint, prettier, vitest

- Add opencode configuration (commands, skills, agents)

- Add .gitignore for node_modules, .env, data/, backups/, build outputs
2026-06-24 23:31:06 +02:00

8.8 KiB

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

// 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

// Service classes
TaskService.ts
UserAuthenticationService.ts

// Repository classes
TaskRepository.ts
UserRepository.ts

// Controller classes
TaskController.ts

2. Function files: camelCase + start with a verb

// Utility functions
formatDate.ts
validateEmail.ts
parseCommandArguments.ts

3. Type definition files: PascalCase or kebab-case

// 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

// Constant definitions
API_ENDPOINTS.ts
ERROR_MESSAGES.ts

// or
api-endpoints.ts
error-messages.ts

Managing Dependencies

Dependency Rules Between Layers

// ✅ 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:

// 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

// 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

// 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

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:

// 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