> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/components-conocidos/logic-components.md).

# Components de lógica

{% 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)](/contributor/contributor-es/guias-para-colaboradores/testing-standards/testing-services-wkc.md) 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


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/components-conocidos/logic-components.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
