# Logic Components

{% hint style="info" %}
This section is currently being developed. Check back soon for comprehensive documentation on logic components.
{% endhint %}

Logic components are pieces of software that contain the **business logic** of your application. They are organized semantically by domain or functionality and serve as the orchestration layer between controllers and adapters.

## Purpose

Logic components:

* Implement business rules and domain logic
* Orchestrate operations across multiple adapters
* Encapsulate complex workflows
* Remain independent of transport layer (HTTP, WebSocket) concerns
* Can be thoroughly unit tested without I/O dependencies

## Location

Logic components SHOULD be placed under the `src/logic` or `src/components` directory, organized by domain:

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

## Characteristics

### 1. Adapter Consumption

Logic components are the primary consumers of adapters. They use adapters to interact with external resources while focusing on business rules.

```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> {
    // Check cache first
    const cached = await cache.get(`user:${userId}`)
    if (cached) {
      return cached
    }

    // Fetch from database
    const user = await database.query('SELECT * FROM users WHERE id = $1', [userId])
    
    // Apply business logic
    const profile = transformUserToProfile(user)
    
    // Cache the result
    await cache.set(`user:${userId}`, profile, { ttl: 3600 })
    
    return profile
  }

  return {
    getUserProfile
  }
}
```

### 2. Business Rules

Logic components enforce business rules and validation:

```tsx
async function createUser(userData: CreateUserInput): Promise<User> {
  // Business rule: username must be unique
  const existing = await database.query(
    'SELECT id FROM users WHERE username = $1',
    [userData.username]
  )
  
  if (existing.rows.length > 0) {
    throw new UserAlreadyExistsError(userData.username)
  }

  // Business rule: validate user data
  if (!isValidEmail(userData.email)) {
    throw new InvalidEmailError(userData.email)
  }

  // Create the user
  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. Workflow Orchestration

Logic components coordinate complex multi-step operations:

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

  // Step 2: Process content
  const processed = await processContent(content)

  // Step 3: Store content
  const stored = await storage.save(processed)

  // Step 4: Update indexes
  await searchIndex.index(stored)

  // Step 5: Notify subscribers
  await notifications.notifySubscribers(userId, stored)

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

  return stored
}
```

## Best Practices

### 1. Single Responsibility

Each logic component should focus on one domain or bounded context:

```tsx
// Good: Focused on user domain
createUserLogic()
createContentLogic()
createPermissionsLogic()

// Avoid: Mixed responsibilities
createUserAndContentLogic()
```

### 2. Dependency Injection

Always inject dependencies through the components parameter:

```tsx
export function createOrderLogic(
  components: Pick<AppComponents, 'database' | 'payments' | 'inventory' | 'logs'>
): IOrderLogic {
  // Use injected dependencies
}
```

### 3. Error Handling

Throw meaningful domain errors that controllers can catch and handle:

```tsx
// Define domain-specific errors
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. Pure Business Logic

Keep logic components free from transport layer concerns:

```tsx
// Good: Pure business logic
async function calculateOrderTotal(items: OrderItem[]): Promise<number> {
  return items.reduce((total, item) => total + item.price * item.quantity, 0)
}

// Avoid: HTTP/transport layer concerns
async function calculateOrderTotal(req: Request, res: Response): Promise<void> {
  // Don't do this in logic components
}
```

## Testing Logic Components

Logic components should be thoroughly unit tested. See the [Testing Services (WKC)](/contributor/contributor-guides/testing-standards/testing-services-wkc.md) documentation for details.

```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)
    })
  })
})
```

## Coming Soon

This section will be expanded with:

* Detailed examples of domain-driven logic components
* Patterns for handling complex workflows
* Guidelines for organizing large domain logic
* Integration with event systems
* Caching strategies
* Transaction management patterns


---

# Agent Instructions: 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-guides/well-known-components/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.
