# Guia de Migração: EventBus v1.x → v2.0

Este guia documenta as mudanças entre a versão 1.x e 2.0 do EventBus e como migrar seu código.

## 📋 Índice

- [TL;DR - Mudanças Rápidas](#tldr---mudanças-rápidas)
- [Breaking Changes](#breaking-changes)
- [Novas Features](#novas-features)
- [Migração Passo a Passo](#migração-passo-a-passo)
- [Exemplos de Migração](#exemplos-de-migração)
- [FAQ](#faq)

---

## TL;DR - Mudanças Rápidas

### Breaking Changes

1. **Campo `events` agora é privado** - use `getEventNames()`, `hasSubscribers()`, `getSubscriberCount()`

### Novas Features (Opt-in)

2. Generics para type-safety
3. Sistema de configuração (`EventBusConfig`)
4. Validação de eventos
5. Métricas opcionais
6. Método `create()` para instâncias independentes

### Código que NÃO quebra

- ✅ `EventBus.getInstance()` - ainda funciona
- ✅ `subscribe(event, callback)` - ainda funciona
- ✅ `publish(event, data)` - ainda funciona
- ✅ `unsubscribe(event, callback)` - ainda funciona
- ✅ `clearEvent()` e `clearAllEvents()` - ainda funcionam

---

## Breaking Changes

### 1. Campo `events` Agora é Privado

#### ❌ v1.x (não funciona mais)

```typescript
const bus = EventBus.getInstance()

// Acesso direto ao campo events
Object.keys(bus.events) // ❌ Erro: 'events' is private
bus.events['myEvent'] // ❌ Erro: 'events' is private
bus.events = {} // ❌ Erro: 'events' is private
```

#### ✅ v2.0 (use os métodos públicos)

```typescript
const bus = EventBus.getInstance()

// Métodos públicos de inspeção
bus.getEventNames() // ['myEvent', 'otherEvent']
bus.hasSubscribers('myEvent') // true
bus.getSubscriberCount('myEvent') // 3
```

#### Tabela de Substituição

| v1.x (não funciona)         | v2.0 (use isso)                 | Descrição                   |
| --------------------------- | ------------------------------- | --------------------------- |
| `Object.keys(bus.events)`   | `bus.getEventNames()`           | Lista eventos ativos        |
| `bus.events[event]`         | `bus.hasSubscribers(event)`     | Verifica se tem subscribers |
| `bus.events[event]?.length` | `bus.getSubscriberCount(event)` | Conta subscribers           |
| `bus.events = {}`           | `bus.clearAllEvents()`          | Limpa todos os eventos      |
| `delete bus.events[event]`  | `bus.clearEvent(event)`         | Remove evento específico    |

---

## Novas Features

### 1. Type Safety com Generics

#### Uso Básico - Tipos de Eventos

```typescript
// v1.x - sem tipagem
const bus = EventBus.getInstance()
bus.subscribe('userLogin', callback) // 'userLogin' é qualquer string

// v2.0 - com tipagem
type AppEvents = 'userLogin' | 'userLogout' | 'dataLoaded'
const bus = EventBus.getInstance<AppEvents>()
bus.subscribe('userLogin', callback) // ✅ OK
bus.subscribe('invalidEvent', callback) // ❌ Erro de compilação
```

#### Uso Avançado - Payloads Tipados

```typescript
// v2.0 - payloads tipados
type AppEvents = 'login' | 'logout'

interface AppPayloads extends EventPayloadMap<AppEvents> {
    login: { userId: string; timestamp: Date }
    logout: { reason: string }
}

const bus = EventBus.getInstance<AppEvents, AppPayloads>()

bus.subscribe('login', (payload) => {
    // payload é tipado automaticamente!
    console.log(payload.userId) // ✅ TypeScript sabe que existe
    console.log(payload.invalid) // ❌ Erro de compilação
})
```

### 2. Sistema de Configuração

```typescript
import { EventBusConfig } from './app/services'

type MyEvents = 'event1' | 'event2'

const config: EventBusConfig<MyEvents> = {
    // Validação de eventos
    allowedEvents: ['event1', 'event2'],
    validateEvents: true,

    // Error handling customizado
    onError: (error, eventName) => {
        myErrorTracker.log(error, eventName)
    },

    // Logger customizado
    logger: {
        error: (msg, ...args) => myLogger.error(msg, args),
        warn: (msg, ...args) => myLogger.warn(msg, args),
    },

    // Métricas
    enableMetrics: true,
}

const bus = EventBus.getInstance<MyEvents>(config)
```

### 3. Validação de Eventos

```typescript
const bus = EventBus.create({
    allowedEvents: ['login', 'logout'],
    validateEvents: true,
})

bus.subscribe('login', callback) // ✅ OK
bus.publish('login', data) // ✅ OK

bus.publish('invalidEvent', data) // ❌ Lança Error em runtime
// Error: Evento "invalidEvent" não está na lista de eventos permitidos
```

### 4. Métricas de Uso

```typescript
const bus = EventBus.create({ enableMetrics: true })

bus.subscribe('event1', callback1)
bus.subscribe('event1', callback2)
bus.publish('event1', data)
bus.publish('event1', data)

const metrics = bus.getMetrics()
console.log(metrics)
// {
//   totalSubscriptions: 2,
//   totalPublishes: 2,
//   totalErrors: 0,
//   lastPublishTimestamp: 1712012345678,
//   byEvent: {
//     event1: {
//       subscriptionCount: 2,
//       publishCount: 2,
//       errorCount: 0,
//       lastPublishTimestamp: 1712012345678
//     }
//   }
// }
```

### 5. Instâncias Independentes com `create()`

```typescript
// v1.x - apenas singleton
const bus1 = EventBus.getInstance()
const bus2 = EventBus.getInstance()
console.log(bus1 === bus2) // true - sempre a mesma instância

// v2.0 - create() para instâncias independentes
const authBus = EventBus.create<'login' | 'logout'>()
const dataBus = EventBus.create<'load' | 'save'>()
console.log(authBus === dataBus) // false - instâncias separadas

// Útil para módulos isolados
authBus.subscribe('login', handleAuth)
dataBus.subscribe('load', handleData)
// Não há interferência entre os dois buses
```

---

## Migração Passo a Passo

### Passo 1: Identificar Uso do Campo `events`

Procure no seu código por:

- `bus.events`
- `.events[`
- `Object.keys(` em EventBus

```bash
# Buscar no código
grep -r "\.events" --include="*.ts" --include="*.js"
```

### Passo 2: Substituir Acesso Direto

Para cada ocorrência, use a tabela de substituição acima.

**Antes:**

```typescript
if (bus.events['myEvent']) {
    const count = bus.events['myEvent'].length
    const allEvents = Object.keys(bus.events)
}
```

**Depois:**

```typescript
if (bus.hasSubscribers('myEvent')) {
    const count = bus.getSubscriberCount('myEvent')
    const allEvents = bus.getEventNames()
}
```

### Passo 3: (Opcional) Adicionar Tipos

Se estiver usando TypeScript, adicione tipos para melhor experiência:

```typescript
// Definir tipos de eventos
type MyAppEvents = 'userLogin' | 'userLogout' | 'pageView'

// Atualizar getInstance()
const bus = EventBus.getInstance<MyAppEvents>()
```

### Passo 4: (Opcional) Adicionar Configuração

Se quiser usar novas features:

```typescript
const config: EventBusConfig<MyAppEvents> = {
    validateEvents: true,
    allowedEvents: ['userLogin', 'userLogout', 'pageView'],
    enableMetrics: true,
}

const bus = EventBus.getInstance<MyAppEvents>(config)
```

### Passo 5: Testar

```typescript
// Verificar que tudo funciona
bus.subscribe('userLogin', (data) => console.log('Login:', data))
bus.publish('userLogin', { userId: '123' })

// Verificar inspeção
console.log('Eventos ativos:', bus.getEventNames())
console.log('Subscribers de userLogin:', bus.getSubscriberCount('userLogin'))

// Se métricas habilitadas
const metrics = bus.getMetrics()
console.log('Métricas:', metrics)
```

---

## Exemplos de Migração

### Exemplo 1: Verificação de Eventos Ativos

**v1.x:**

```typescript
function hasActiveEvents(bus: EventBus): boolean {
    return Object.keys(bus.events).length > 0
}
```

**v2.0:**

```typescript
function hasActiveEvents(bus: EventBus): boolean {
    return bus.getEventNames().length > 0
}
```

### Exemplo 2: Debug de Subscribers

**v1.x:**

```typescript
function debugSubscribers(bus: EventBus) {
    Object.entries(bus.events).forEach(([event, callbacks]) => {
        console.log(`${event}: ${callbacks.length} subscribers`)
    })
}
```

**v2.0:**

```typescript
function debugSubscribers(bus: EventBus) {
    bus.getEventNames().forEach((event) => {
        const count = bus.getSubscriberCount(event)
        console.log(`${event}: ${count} subscribers`)
    })
}
```

### Exemplo 3: Limpar Eventos Inativos

**v1.x:**

```typescript
function clearEmptyEvents(bus: EventBus) {
    Object.keys(bus.events).forEach((event) => {
        if (bus.events[event].length === 0) {
            delete bus.events[event]
        }
    })
}
```

**v2.0:**

```typescript
function clearEmptyEvents(bus: EventBus) {
    // Não é mais necessário! O EventBus já faz isso automaticamente
    // quando o último subscriber é removido.
    // Mas se quiser fazer manualmente:
    bus.getEventNames().forEach((event) => {
        if (bus.getSubscriberCount(event) === 0) {
            bus.clearEvent(event)
        }
    })
}
```

### Exemplo 4: Testes

**v1.x:**

```typescript
// Não tinha como resetar singleton facilmente
describe('EventBus', () => {
    it('should work', () => {
        const bus = EventBus.getInstance()
        // Problema: estado compartilhado entre testes
    })
})
```

**v2.0:**

```typescript
describe('EventBus', () => {
    afterEach(() => {
        EventBus.resetInstance() // Reseta singleton entre testes
    })

    // OU melhor ainda - use create() para isolamento
    it('should work', () => {
        const bus = EventBus.create() // Instância isolada
        // Sem interferência entre testes
    })
})
```

---

## FAQ

### P: Meu código vai quebrar ao atualizar?

**R:** Apenas se você acessa `bus.events` diretamente. API de subscribe/publish permanece igual.

### P: Como sei se estou acessando `events` diretamente?

**R:** Procure no código:

```bash
grep -r "\.events" --include="*.ts" --include="*.js"
```

### P: Preciso atualizar se não uso TypeScript?

**R:** Apenas se acessa `bus.events` diretamente. Caso contrário, funciona igual.

### P: Posso usar v2.0 sem tipos genéricos?

**R:** Sim! `EventBus.getInstance()` funciona como antes:

```typescript
const bus = EventBus.getInstance() // sem tipos
bus.subscribe('myEvent', callback)
```

### P: Quando usar `getInstance()` vs `create()`?

**R:**

- `getInstance()` - quando quer singleton global (uso típico)
- `create()` - quando quer instâncias isoladas (testes, módulos separados)

### P: As métricas afetam performance?

**R:** Não se `enableMetrics: false` (padrão). Zero overhead.

### P: Posso reconfigurar o singleton depois de criado?

**R:** Não. Config só é aplicada na primeira chamada `getInstance(config)`. Use `create(config)` para nova instância com config diferente.

### P: Como migro payloads tipados?

**R:** Opcional. Veja exemplo na seção "Uso Avançado - Payloads Tipados".

### P: Preciso alterar meu build/bundle?

**R:** Não. A biblioteca é backward compatible no bundling.

### P: A v1.x ainda será suportada?

**R:** Sim, em branch separado para bugfixes críticos. Mas novas features serão v2.0+.

---

## Suporte

- **Issues**: [GitHub Issues](https://github.com/seu-repo/issues)
- **Documentação**: Ver `examples/event-bus-usage.ts`
- **Changelog**: Ver `CHANGELOG.md`

---

## Checklist de Migração

Use este checklist para garantir migração completa:

- [ ] Buscar e substituir todos os acessos a `bus.events`
- [ ] Usar `getEventNames()` ao invés de `Object.keys(bus.events)`
- [ ] Usar `hasSubscribers()` ao invés de `!!bus.events[event]`
- [ ] Usar `getSubscriberCount()` ao invés de `bus.events[event].length`
- [ ] (Opcional) Adicionar tipos genéricos `EventBus<TEvents>`
- [ ] (Opcional) Adicionar configuração `EventBusConfig`
- [ ] (Opcional) Habilitar métricas se necessário
- [ ] Atualizar testes para usar `create()` ou `resetInstance()`
- [ ] Rodar testes e verificar que tudo funciona
- [ ] Atualizar documentação interna do projeto
- [ ] Atualizar versão no package.json para `^2.0.0`

---

**Boa migração! 🚀**
