164 lines
4.1 KiB
Markdown
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
|