Files
gh-cubical6-melly/skills/c4model-c3/dependency-analysis.md
2025-11-29 18:17:07 +08:00

164 lines
4.1 KiB
Markdown

# Dependency Analysis
This guide provides comprehensive methodology for analyzing component dependencies at the C3 level.
---
## Dependency Analysis
### Dependency Types
#### 1. Internal Dependencies (Within Container)
```typescript
// UserService.ts
import { UserRepository } from './UserRepository'; // Same feature
import { EmailService } from '../email/EmailService'; // Different feature
import { ValidationUtil } from '../utils/validation'; // Utility
// Dependencies:
// - UserRepository (direct dependency)
// - EmailService (cross-feature dependency)
// - ValidationUtil (utility dependency)
```
#### 2. External Dependencies (Outside Container)
```typescript
// PaymentService.ts
import Stripe from 'stripe'; // External library
import { Logger } from '@nestjs/common'; // Framework
import axios from 'axios'; // HTTP client
// External dependencies:
// - Stripe SDK
// - NestJS framework
// - Axios library
```
#### 3. Framework Dependencies
```typescript
// UserController.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
// Framework dependencies:
// - NestJS decorators
// - Swagger decorators
```
### Dependency Direction
Analyze the flow of dependencies:
**Recommended flow (Dependency Inversion):**
```
Controllers → Services → Repositories → Models
↓ ↓ ↓
(HTTP) (Business) (Data)
```
**Anti-pattern (Skip layers):**
```
Controllers → Repositories ❌ (skips business logic layer)
```
**Example: Good Dependency Direction**
```typescript
// Good: Controller depends on Service
@Controller('users')
export class UserController {
constructor(private userService: UserService) {}
@Post()
createUser(@Body() dto: CreateUserDto) {
return this.userService.createUser(dto);
}
}
// Good: Service depends on Repository
export class UserService {
constructor(private userRepo: UserRepository) {}
async createUser(dto: CreateUserDto) {
// Business logic here
return this.userRepo.save(user);
}
}
// Good: Repository depends on Model
export class UserRepository {
async save(user: User) {
// Data access here
}
}
```
### Coupling Analysis
**Types of coupling:**
#### 1. Tight Coupling (Bad)
```typescript
// Bad: Direct instantiation
export class UserService {
private emailService = new EmailService(); // ❌ Tightly coupled
async createUser(data: CreateUserDto) {
const user = await this.saveUser(data);
this.emailService.sendWelcome(user); // Can't mock in tests
}
}
```
#### 2. Loose Coupling (Good)
```typescript
// Good: Dependency injection
export class UserService {
constructor(private emailService: EmailService) {} // ✅ Loosely coupled
async createUser(data: CreateUserDto) {
const user = await this.saveUser(data);
this.emailService.sendWelcome(user); // Easy to mock
}
}
```
**Coupling metrics:**
- **Afferent Coupling (Ca)**: Number of components that depend on this component
- **Efferent Coupling (Ce)**: Number of components this component depends on
- **Instability (I)**: Ce / (Ca + Ce)
- I = 0: Very stable (many dependents, no dependencies)
- I = 1: Very unstable (no dependents, many dependencies)
**Detection:**
```bash
# Find components with many imports (high efferent coupling)
while IFS= read -r -d '' file; do
count=$(grep -c '^import' "$file" 2>/dev/null || echo 0)
printf "%d %s\n" "$count" "$file"
done < <(find src -name "*.ts" -print0) | sort -rn | head -20
# Find components imported by many others (high afferent coupling)
grep -rE "from ['\"](\.\./)+[^'\"]+['\"]" src/ | cut -d: -f2 | cut -d"'" -d'"' -f2 | sort | uniq -c | sort -rn
```
### Circular Dependency Detection
**Anti-pattern: Circular dependencies**
```
UserService → OrderService → UserService ❌
```
**Detection:**
```bash
# Use madge for Node.js projects
npx madge --circular src/
# Use dependency-cruiser
npx depcruise --validate .dependency-cruiser.js src/
```
**Solution:**
- Extract shared logic to third component
- Use events instead of direct calls
- Refactor to remove circular reference