# Componentes lógicos

{% hint style="info" %}
Esta sección está actualmente en desarrollo. Vuelve pronto para obtener documentación completa sobre los componentes de lógica.
{% endhint %}

Los componentes de lógica son piezas de software que contienen la **lógica de negocio** de tu aplicación. Están organizados semánticamente por dominio o funcionalidad y sirven como la capa de orquestación entre controllers y adapters.

## Propósito

Componentes de lógica:

* Implementan las reglas de negocio y la lógica del dominio
* Orquestan operaciones a través de múltiples adapters
* Encapsulan flujos de trabajo complejos
* Permiten permanecer independientes de las preocupaciones de la capa de transporte (HTTP, WebSocket)
* Pueden ser completamente probados con unit tests sin dependencias de I/O

## Ubicación

Los componentes de lógica DEBERÍAN colocarse bajo `src/logic` o `src/components` el directorio, organizados por dominio:

```
src/
└── logic/
    ├── users/
    ├── content/
    ├── permissions/
    └── notifications/
```

## Características

### 1. Consumo de Adapters

Los componentes de lógica son los principales consumidores de adapters. Usan adapters para interactuar con recursos externos mientras se enfocan en las reglas de negocio.

```tsx
export function createUserLogic(
  components: Pick<AppComponents, 'database' | 'cache' | 'logs'>
): IUserLogic {
  const { database, cache, logs } = components
  const logger = logs.getLogger('user-logic')

  async function getUserProfile(userId: string): Promise<UserProfile> {
    // Revisar la cache primero
    const cached = await cache.get(`user:${userId}`)
    if (cached) {
      return cached
    }

    // Obtener de la database
    const user = await database.query('SELECT * FROM users WHERE id = $1', [userId])
    
    // Aplicar lógica de negocio
    const profile = transformUserToProfile(user)
    
    // Cachear el resultado
    await cache.set(`user:${userId}`, profile, { ttl: 3600 })
    
    return profile
  }

  return {
    getUserProfile
  }
}
```

### 2. Reglas de Negocio

Los componentes de lógica hacen cumplir las reglas de negocio y la validación:

```tsx
async function createUser(userData: CreateUserInput): Promise<User> {
  // Regla de negocio: el nombre de usuario debe ser único
  const existing = await database.query(
    'SELECT id FROM users WHERE username = $1',
    [userData.username]
  )
  
  if (existing.rows.length > 0) {
    throw new UserAlreadyExistsError(userData.username)
  }

  // Regla de negocio: validar los datos del usuario
  if (!isValidEmail(userData.email)) {
    throw new InvalidEmailError(userData.email)
  }

  // Crear el usuario
  const user = await database.query(
    'INSERT INTO users (username, email) VALUES ($1, $2) RETURNING *',
    [userData.username, userData.email]
  )

  logger.info('User created', { userId: user.id })
  
  return user
}
```

### 3. Orquestación de Flujos de Trabajo

Los componentes de lógica coordinan operaciones complejas de múltiples pasos:

```tsx
async function publishContent(
  userId: string,
  content: ContentInput
): Promise<PublishedContent> {
  // Paso 1: Validar permisos
  const canPublish = await permissions.canUserPublish(userId)
  if (!canPublish) {
    throw new UnauthorizedError('User cannot publish content')
  }

  // Paso 2: Procesar el contenido
  const processed = await processContent(content)

  // Paso 3: Almacenar el contenido
  const stored = await storage.save(processed)

  // Paso 4: Actualizar índices
  await searchIndex.index(stored)

  // Paso 5: Notificar a los suscriptores
  await notifications.notifySubscribers(userId, stored)

  logger.info('Content published', { contentId: stored.id, userId })

  return stored
}
```

## Buenas prácticas

### 1. Responsabilidad Única

Cada componente de lógica debe centrarse en un dominio o contexto acotado:

```tsx
// Bueno: Enfocado en el dominio de usuario
createUserLogic()
createContentLogic()
createPermissionsLogic()

// Evitar: Responsabilidades mezcladas
createUserAndContentLogic()
```

### 2. Inyección de Dependencias

Siempre inyectar las dependencias a través del parámetro components:

```tsx
export function createOrderLogic(
  components: Pick<AppComponents, 'database' | 'payments' | 'inventory' | 'logs'>
): IOrderLogic {
  // Usar las dependencias inyectadas
}
```

### 3. Manejo de Errores

Lanzar errores de dominio significativos que los controllers puedan capturar y manejar:

```tsx
// Definir errores específicos del dominio
export class InsufficientInventoryError extends Error {
  constructor(public readonly productId: string, public readonly requested: number) {
    super(`Insufficient inventory for product ${productId}. Requested: ${requested}`)
    this.name = 'InsufficientInventoryError'
  }
}
```

### 4. Lógica de Negocio Pura

Mantén los componentes de lógica libres de preocupaciones de la capa de transporte:

```tsx
// Bueno: Lógica de negocio pura
async function calculateOrderTotal(items: OrderItem[]): Promise<number> {
  return items.reduce((total, item) => total + item.price * item.quantity, 0)
}

// Evitar: preocupaciones de HTTP/capa de transporte
async function calculateOrderTotal(req: Request, res: Response): Promise<void> {
  // No hagas esto en los componentes de lógica
}
```

## Pruebas de Componentes de Lógica

Los componentes de lógica deben ser probados exhaustivamente con unit tests. Consulta la [Testing Services (WKC)](https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/testing-standards/testing-services-wkc) documentación para más detalles.

```tsx
describe('when creating a user', () => {
  let userLogic: IUserLogic
  let mockDatabase: jest.Mocked<IDatabase>

  beforeEach(() => {
    mockDatabase = createMockDatabase()
    userLogic = createUserLogic({ database: mockDatabase, logs: mockLogs })
  })

  describe('and the username already exists', () => {
    beforeEach(() => {
      mockDatabase.query.mockResolvedValueOnce({ rows: [{ id: '123' }] })
    })

    it('should throw UserAlreadyExistsError', async () => {
      await expect(
        userLogic.createUser({ username: 'existing', email: 'test@test.com' })
      ).rejects.toThrow(UserAlreadyExistsError)
    })
  })
})
```

## Próximamente

Esta sección se ampliará con:

* Ejemplos detallados de componentes de lógica orientados al dominio
* Patrones para manejar flujos de trabajo complejos
* Directrices para organizar lógica de dominio a gran escala
* Integración con sistemas de eventos
* Estrategias de caching
* Patrones de gestión de transacciones
