# Guía de Migración a DIP con ServiceFactory

Esta guía proporciona pasos detallados para migrar los servicios singleton existentes a un patrón de Dependency Injection usando las interfaces DIP y ServiceFactory.

## Índice

1. [Visión General](#visión-general)
2. [Paso a Paso](#paso-a-paso)
3. [Ejemplos Prácticos](#ejemplos-prácticos)
4. [Orden de Migración Recomendado](#orden-de-migración-recomendado)
5. [Testing](#testing)

## Visión General

### Antes (Singleton Pattern)
```typescript
// service.ts
export class MyService {
  doSomething() { ... }
}
export const myService = new MyService();

// consumer.ts
import { myService } from './service';
myService.doSomething();
```

### Después (DIP + Dependency Injection)
```typescript
// interfaces/my-service.interface.ts
export interface IMyService {
  doSomething(): void;
}

// service.ts
export class MyService implements IMyService {
  doSomething() { ... }
}
export const myService = new MyService();

// app initialization (index.ts)
ServiceFactory.register<IMyService>(ServiceKeys.MY_SERVICE, myService);

// consumer.ts
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import type { IMyService } from '@/shared/interfaces';

const myService = ServiceFactory.getRequired<IMyService>(ServiceKeys.MY_SERVICE);
myService.doSomething();
```

## Paso a Paso

### 1. Identificar Interface Existente o Crear Nueva

Verifica si ya existe una interface en `/shared/interfaces/`:
- `ICache` → Para servicios de caché
- `IHttpClient` → Para clientes HTTP
- `ITokenService`, `IEmailService`, etc. → Para servicios de autenticación
- `IBinanceService`, `IMarketService` → Para servicios de trading

Si no existe, créala siguiendo el patrón:

```typescript
// shared/interfaces/services/my-service.interface.ts
export interface IMyService {
  // Declara todos los métodos públicos
  myMethod(param: string): Promise<Result>;
  anotherMethod(): void;
}
```

### 2. Implementar Interface en Clase Existente

```typescript
// modules/mymodule/services/my.service.ts
import type { IMyService } from '@/shared/interfaces';

export class MyService implements IMyService {
  // Implementación existente
  myMethod(param: string): Promise<Result> {
    // ...
  }

  anotherMethod(): void {
    // ...
  }
}

// Mantener el singleton por ahora para compatibilidad
export const myService = new MyService();
```

### 3. Agregar Service Key

Edita `/shared/factories/service.factory.ts` y agrega el key en `ServiceKeys`:

```typescript
export const ServiceKeys = {
  // ... existentes
  MY_SERVICE: 'IMyService',
} as const;
```

### 4. Registrar en ServiceFactory

En `/apps/backend/src/index.ts` (o en un archivo de inicialización dedicado):

```typescript
import { ServiceFactory, ServiceKeys } from './shared/factories';

// Importar servicios singleton existentes
import { tokenService } from './modules/auth/services/token.service';
import { emailService } from './modules/auth/services/email.service';
import { marketService } from './modules/trading/services/market.service';
import { cacheService } from './modules/trading/services/cache.service';
// ... otros servicios

// Registrar todos los servicios
function registerServices(): void {
  // Auth services
  ServiceFactory.register(ServiceKeys.TOKEN_SERVICE, tokenService);
  ServiceFactory.register(ServiceKeys.EMAIL_SERVICE, emailService);

  // Trading services
  ServiceFactory.register(ServiceKeys.MARKET_SERVICE, marketService);
  ServiceFactory.register(ServiceKeys.CACHE_SERVICE, cacheService);

  // ... otros servicios
}

// Llamar durante la inicialización
async function initializeApp() {
  registerServices();
  // ... resto de inicialización
}
```

### 5. Actualizar Consumidores Gradualmente

Opción A - En constructores de clases:
```typescript
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import type { ITokenService } from '@/shared/interfaces';

export class AuthController {
  private tokenService: ITokenService;

  constructor() {
    this.tokenService = ServiceFactory.getRequired<ITokenService>(
      ServiceKeys.TOKEN_SERVICE
    );
  }

  async login() {
    const tokens = await this.tokenService.createSession(...);
    // ...
  }
}
```

Opción B - En funciones/handlers:
```typescript
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import type { IMarketService } from '@/shared/interfaces';

export async function getMarketData(req: Request, res: Response) {
  const marketService = ServiceFactory.getRequired<IMarketService>(
    ServiceKeys.MARKET_SERVICE
  );

  const data = await marketService.getKlines(...);
  res.json(data);
}
```

## Ejemplos Prácticos

### Ejemplo 1: Migrar TokenService

#### 1. Interface (ya existe en `/shared/interfaces/services/auth.interface.ts`)
```typescript
export interface ITokenService {
  generateAccessToken(user: User): string;
  createSession(...): Promise<{ session: Session; tokens: AuthTokens }>;
  // ... otros métodos
}
```

#### 2. Implementar en clase existente
```typescript
// modules/auth/services/token.service.ts
import type { ITokenService } from '@/shared/interfaces';

export class TokenService implements ITokenService {
  // Implementación existente (sin cambios)
}

export const tokenService = new TokenService();
```

#### 3. Registrar
```typescript
// index.ts
ServiceFactory.register<ITokenService>(ServiceKeys.TOKEN_SERVICE, tokenService);
```

#### 4. Usar en controllers
```typescript
// modules/auth/controllers/auth.controller.ts
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import type { ITokenService } from '@/shared/interfaces';

export class AuthController {
  private tokenService: ITokenService;

  constructor() {
    this.tokenService = ServiceFactory.getRequired<ITokenService>(
      ServiceKeys.TOKEN_SERVICE
    );
  }
}
```

### Ejemplo 2: Migrar CacheService

#### 1. Interface (ya existe en `/shared/interfaces/cache.interface.ts`)
```typescript
export interface ICache {
  get<T>(key: string): T | null;
  set<T>(key: string, data: T, ttlSeconds?: number): void;
  // ... otros métodos
}
```

#### 2. Implementar
```typescript
// modules/trading/services/cache.service.ts
import type { ICache } from '@/shared/interfaces';

export class CacheService implements ICache {
  // Implementación existente
}

export const cacheService = new CacheService(60);
export const marketDataCache = new MarketDataCache();
```

#### 3. Registrar
```typescript
// index.ts
ServiceFactory.register<ICache>(ServiceKeys.CACHE_SERVICE, cacheService);
ServiceFactory.register<ICache>(ServiceKeys.MARKET_DATA_CACHE, marketDataCache);
```

#### 4. Usar en services
```typescript
// modules/trading/services/market.service.ts
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import type { ICache } from '@/shared/interfaces';

export class MarketService {
  private cache: ICache;

  constructor() {
    this.cache = ServiceFactory.getRequired<ICache>(ServiceKeys.MARKET_DATA_CACHE);
  }

  async getKlines(...) {
    return this.cache.getOrSet(key, () => fetchFromAPI(...), 5);
  }
}
```

### Ejemplo 3: Migrar BinanceService

#### 1. Interface (ya existe en `/shared/interfaces/services/trading.interface.ts`)
```typescript
export interface IBinanceService {
  getKlines(...): Promise<Kline[]>;
  get24hrTicker(...): Promise<Ticker24h | Ticker24h[]>;
  // ... otros métodos
}
```

#### 2. Implementar
```typescript
// modules/trading/services/binance.service.ts
import type { IBinanceService } from '@/shared/interfaces';

export class BinanceService extends EventEmitter implements IBinanceService {
  // Implementación existente
}

export const binanceService = new BinanceService();
```

#### 3. Registrar
```typescript
ServiceFactory.register<IBinanceService>(ServiceKeys.BINANCE_SERVICE, binanceService);
```

#### 4. Usar
```typescript
const binanceService = ServiceFactory.getRequired<IBinanceService>(
  ServiceKeys.BINANCE_SERVICE
);
```

## Orden de Migración Recomendado

1. **Servicios de Infraestructura** (sin dependencias externas)
   - `CacheService`
   - `Logger`
   - Database clients

2. **Clientes HTTP** (dependen de cache/logger)
   - `LLMAgentClient`
   - `MLEngineClient`
   - `TradingAgentsClient`

3. **Servicios de Dominio Básicos**
   - `TokenService`
   - `BinanceService`

4. **Servicios de Autenticación** (dependen de TokenService)
   - `EmailService`
   - `OAuthService`
   - `TwoFactorService`
   - `PhoneService`

5. **Servicios de Trading** (dependen de BinanceService y Cache)
   - `MarketService`
   - `WatchlistService`
   - `AlertsService`
   - `IndicatorsService`
   - `PaperTradingService`

6. **Servicios de Alto Nivel** (dependen de múltiples servicios)
   - `PortfolioService`
   - `MLIntegrationService`
   - `LLMService`
   - `AgentsService`

7. **Controllers** (última migración)
   - Actualizar todos los controllers para usar ServiceFactory

## Testing

### Test Unitario con Mocks

```typescript
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import type { ITokenService, IEmailService } from '@/shared/interfaces';
import { AuthController } from './auth.controller';

describe('AuthController', () => {
  let mockTokenService: jest.Mocked<ITokenService>;
  let mockEmailService: jest.Mocked<IEmailService>;

  beforeEach(() => {
    // Crear mocks
    mockTokenService = {
      generateAccessToken: jest.fn(),
      createSession: jest.fn(),
      verifyAccessToken: jest.fn(),
      // ... otros métodos
    } as jest.Mocked<ITokenService>;

    mockEmailService = {
      register: jest.fn(),
      login: jest.fn(),
      sendVerificationEmail: jest.fn(),
      // ... otros métodos
    } as jest.Mocked<IEmailService>;

    // Registrar mocks
    ServiceFactory.register<ITokenService>(ServiceKeys.TOKEN_SERVICE, mockTokenService);
    ServiceFactory.register<IEmailService>(ServiceKeys.EMAIL_SERVICE, mockEmailService);
  });

  afterEach(() => {
    ServiceFactory.clear();
  });

  it('should create session on login', async () => {
    mockEmailService.login.mockResolvedValue({
      user: { id: '1', email: 'test@test.com' } as any,
      tokens: { accessToken: 'token', refreshToken: 'refresh' } as any,
    });

    const controller = new AuthController();
    const result = await controller.login({ email: 'test@test.com', password: 'pass' });

    expect(mockEmailService.login).toHaveBeenCalled();
    expect(result).toBeDefined();
  });
});
```

### Test de Integración

```typescript
import { ServiceFactory, ServiceKeys } from '@/shared/factories';
import { tokenService } from '@/modules/auth/services/token.service';
import { emailService } from '@/modules/auth/services/email.service';

describe('Auth Integration', () => {
  beforeAll(() => {
    // Usar servicios reales para test de integración
    ServiceFactory.register(ServiceKeys.TOKEN_SERVICE, tokenService);
    ServiceFactory.register(ServiceKeys.EMAIL_SERVICE, emailService);
  });

  afterAll(() => {
    ServiceFactory.clear();
  });

  it('should complete full authentication flow', async () => {
    // Test completo con servicios reales
  });
});
```

## Ventajas de la Migración

1. **Testabilidad**: Fácil crear mocks y stubs
2. **Mantenibilidad**: Cambios en implementación no afectan consumidores
3. **Flexibilidad**: Intercambiar implementaciones sin tocar código cliente
4. **Organización**: Dependencias explícitas y gestionadas centralmente
5. **Type Safety**: TypeScript valida que las implementaciones cumplan las interfaces

## Troubleshooting

### Error: "Service 'XXX' not found in ServiceFactory"
- Solución: Verificar que el servicio esté registrado en la inicialización de la app
- Verificar que el ServiceKey sea correcto

### Error: "Property 'xxx' does not exist on type 'IService'"
- Solución: Agregar el método faltante a la interface
- Verificar que la clase implemente correctamente la interface

### Tests fallan con "Cannot read property of undefined"
- Solución: Asegurar que los mocks estén registrados en beforeEach
- Verificar que ServiceFactory.clear() se llame en afterEach

## Recursos Adicionales

- [SOLID Principles](https://en.wikipedia.org/wiki/SOLID)
- [Dependency Inversion Principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle)
- [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection)
- Interface Documentation: `/shared/interfaces/README.md`
