Skip to content
Arxo Arxo

Refactoring Patterns

Concrete patterns to break cycles. For the step-by-step process, see Fixing Cycles. For an overview, see Circular Dependencies.

Before:

A.js → B.js
B.js → A.js (cycle!)

Both A and B use shared utilities from each other.

After:

A.js → shared.js
B.js → shared.js

Extract common code to a new module.

Before:

user-service.ts
import { EmailService } from './email-service';
export class UserService {
private emailService = new EmailService();
}
// email-service.ts
import { UserService } from './user-service'; // Cycle!
export class EmailService {
getUser() { return new UserService(); }
}

After:

user-service.ts
import type { IEmailService } from './interfaces';
export class UserService {
constructor(private emailService: IEmailService) {}
}
// email-service.ts
import type { IUserService } from './interfaces';
export class EmailService {
constructor(private userService: IUserService) {}
}
// interfaces.ts (new, no dependencies)
export interface IUserService { /* ... */ }
export interface IEmailService { /* ... */ }

Before:

models/user.ts
import { Address } from './address';
export interface User { address: Address; }
// models/address.ts
import { User } from './user'; // Cycle!
export interface Address { user: User; }

After:

// models/types.ts (new)
export interface User { address: Address; }
export interface Address { userId: string; }
// models/user.ts
import { User } from './types';
// models/address.ts
import { Address } from './types';

Before:

UI Layer → Business Logic
Business Logic → UI Layer (cycle!)

After:

UI Layer → Abstraction → Business Logic

Introduce an interface or event system to break the cycle.