> 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-pt/guias-do-contribuidor/componentes-bem-conhecidos/adapters.md).

# Adaptadores

Adapters são componentes responsáveis por lidar com **processos de I/O** e integrações externas. Eles servem como a ponte entre sua lógica de negócio e sistemas externos, como bancos de dados, APIs, sistemas de arquivos ou stores de cache.

## Propósito

Adapters abstraem os detalhes de implementação de dependências externas, permitindo:

* **Intercambiabilidade** - Trocar implementações facilmente (por exemplo, PostgreSQL → MongoDB)
* **Testabilidade** - Mockar dependências externas em testes
* **Isolamento** - Manter a lógica de negócio independente de preocupações de infraestrutura

## Localização

Todos os componentes adapter DEVEM ser colocados sob o `src/adapters` diretório no projeto.

```
src/
└── adapters/
    ├── database/
    ├── storage/
    ├── cache/
    └── external-api/
```

## Interfaces Compartilhadas

Adapters DEVERIAM ser construídos com intercambiabilidade em mente. Defina interfaces compartilhadas em `src/types.ts` para que múltiplos adapters possam implementar o mesmo contrato.

### Exemplo: Interface de Storage

```tsx
// src/types.ts
export interface IStorage {
  set: (key: string, value: any) => Promise<void>
  get: (key: string) => Promise<any>
  delete: (key: string) => Promise<void>
}
```

## Memory Storage Adapter

Aqui está uma implementação simples de um adapter de storage em memória, útil para testes ou desenvolvimento.

### Estrutura de Diretórios

```
src/adapters/memory-storage/
├── component.ts
├── types.ts
├── errors.ts
└── index.ts
```

### Implementação: `component.ts`

```tsx
import { IStorage } from '../../types'

export function createMemoryStorageAdapter(): IStorage {
  // Estado interno - limitado à instância deste componente
  const memory: Record<string, any> = {}

  async function set(key: string, value: any): Promise<void> {
    memory[key] = value
  }

  async function get(key: string): Promise<any> {
    return memory[key]
  }

  async function delete(key: string): Promise<void> {
    delete memory[key]
  }

  return {
    set,
    get,
    delete
  }
}
```

## Redis Storage Adapter

Aqui está um adapter Redis pronto para produção que demonstra o gerenciamento de lifecycle.

### Implementação: `component.ts`

```tsx
import { createClient, RedisClientType } from 'redis'
import { IStorage } from '../../types'
import { RedisConnectionError } from './errors'

export function createRedisStorageAdapter(
  components: Pick<AppComponents, 'config' | 'logs'>
): IStorage {
  const { config, logs } = components
  const logger = logs.getLogger('redis-storage')
  
  const hostUrl = config.requireString('REDIS_URL')
  const client: RedisClientType = createClient({ url: hostUrl })

  // Método de lifecycle: chamado quando o componente inicia
  async function start(): Promise<void> {
    try {
      await client.connect()
      logger.info('Redis client connected successfully')
    } catch (error) {
      logger.error('Failed to connect to Redis', error)
      throw new RedisConnectionError('Could not connect to Redis')
    }
  }

  // Método de lifecycle: chamado quando o componente para
  async function stop(): Promise<void> {
    try {
      await client.quit()
      logger.info('Redis client disconnected')
    } catch (error) {
      logger.error('Error disconnecting Redis client', error)
    }
  }

  async function set(key: string, value: any): Promise<void> {
    const serialized = JSON.stringify(value)
    await client.set(key, serialized)
    logger.debug(`Set key: ${key}`)
  }

  async function get(key: string): Promise<any> {
    const value = await client.get(key)
    logger.debug(`Get key: ${key}`)
    return value ? JSON.parse(value) : null
  }

  async function delete(key: string): Promise<void> {
    await client.del(key)
    logger.debug(`Deleted key: ${key}`)
  }

  return {
    [Lifecycle.ComponentStarted]: start,
    [Lifecycle.ComponentStopped]: stop,
    set,
    get,
    delete
  }
}
```

### Tratamento de Erros: `errors.ts`

```tsx
export class RedisConnectionError extends Error {
  constructor(message: string) {
    super(message)
    this.name = 'RedisConnectionError'
  }
}
```

## Exemplo de Adapter de Banco de Dados

Aqui está um exemplo de um adapter de banco de dados PostgreSQL.

```tsx
import { Pool, QueryResult } from 'pg'
import { IDatabase } from '../../types'

export function createPostgresAdapter(
  components: Pick<AppComponents, 'config' | 'logs'>
): IDatabase {
  const { config, logs } = components
  const logger = logs.getLogger('postgres-adapter')

  const pool = new Pool({
    connectionString: config.requireString('DATABASE_URL'),
    max: config.getNumber('DATABASE_POOL_SIZE', 20)
  })

  async function start(): Promise<void> {
    // Testar a conexão
    const client = await pool.connect()
    logger.info('Database connection pool initialized')
    client.release()
  }

  async function stop(): Promise<void> {
    await pool.end()
    logger.info('Database connection pool closed')
  }

  async function query<T = any>(
    sql: string,
    params?: any[]
  ): Promise<QueryResult<T>> {
    logger.debug('Executing query', { sql, params })
    return pool.query<T>(sql, params)
  }

  return {
    [Lifecycle.ComponentStarted]: start,
    [Lifecycle.ComponentStopped]: stop,
    query
  }
}
```

## Métodos de Lifecycle

WKC fornece métodos de lifecycle especiais que são chamados automaticamente:

* `[Lifecycle.ComponentStarted]` ou `[START_COMPONENT]` - Chamado quando o serviço inicia
* `[Lifecycle.ComponentStopped]` ou `[STOP_COMPONENT]` - Chamado quando o serviço é encerrado

### Quando Usar Métodos de Lifecycle

Use métodos de lifecycle quando seu adapter precisar:

* Estabelecer conexões (banco de dados, cache, fila de mensagens)
* Inicializar pools ou clients
* Realizar checagens de saúde
* Limpar recursos no shutdown
* Fechar conexões graciosamente

## Melhores Práticas

### 1. Tratamento de Erros

Sempre trate erros de conexão e lance erros customizados significativos:

```tsx
try {
  await client.connect()
} catch (error) {
  logger.error('Connection failed', error)
  throw new ConnectionError('Failed to connect to external service')
}
```

### 2. Configuração

Use o componente config para gerenciar as configurações do adapter:

```tsx
const {
  host: config.requireString('DB_HOST'),
  port: config.getNumber('DB_PORT', 5432),
  timeout: config.getNumber('DB_TIMEOUT', 30000)
}
```

### 3. Logging

Registre operações importantes para depuração e monitoramento:

```tsx
logger.info('Operation completed', { recordId, duration })
logger.warn('Slow query detected', { query, duration })
logger.error('Operation failed', { error, context })
```

### 4. Segurança de Tipos

Sempre tipar corretamente seus valores de retorno:

```tsx
async function getUser(id: string): Promise<User | null> {
  const result = await query<User>('SELECT * FROM users WHERE id = $1', [id])
  return result.rows[0] || null
}
```

### 5. Gerenciamento de Recursos

Sempre limpe recursos no método de lifecycle stop:

```tsx
async function stop(): Promise<void> {
  await pool.end()
  await client.disconnect()
  logger.info('Resources cleaned up')
}
```

## Testando Adapters

Veja a [Testing Services (WKC)](/contributor/contributor-pt/guias-do-contribuidor/testing-standards/testing-services-wkc.md) documentação para orientação detalhada sobre testes de adapters.

### Exemplo Rápido

```tsx
describe('when creating a memory storage adapter', () => {
  let storage: IStorage

  beforeEach(() => {
    storage = createMemoryStorageAdapter()
  })

  describe('when setting a value', () => {
    it('should store and retrieve the value', async () => {
      await storage.set('key', 'value')
      const result = await storage.get('key')
      expect(result).toBe('value')
    })
  })
})
```


---

# 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, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-pt/guias-do-contribuidor/componentes-bem-conhecidos/adapters.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
