# Aula 2.6 - Módulos e tratamento de erros

## Identificação

- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** aplicação modular com recuperação de falhas
- **Laboratório:** [`../exemplos/aula-2.6/index.html`](../exemplos/aula-2.6/index.html)

## Introdução

### O que é um módulo?

Um módulo é um arquivo que possui uma responsabilidade clara e pode compartilhar
partes do seu código com outros arquivos.

```js
// task-rules.js
export function normalizeTitle(title) {
  return title.trim();
}
```

```js
// main.js
import { normalizeTitle } from "./task-rules.js";
```

Dividir o código em módulos evita concentrar toda a aplicação em um único arquivo e
torna dependências explícitas.

### Módulo é framework?

Não. Módulos fazem parte da própria linguagem JavaScript e da plataforma web.
Angular, React e NestJS são ferramentas construídas sobre JavaScript e também
utilizam módulos, mas você pode usar `import` e `export` sem qualquer framework.

### Módulo é um pacote do npm?

Não necessariamente:

- um módulo pode ser apenas um arquivo local;
- um pacote normalmente reúne vários arquivos e metadados;
- npm é um gerenciador e registro de pacotes;
- um pacote instalado pode disponibilizar um ou mais módulos.

Nesta aula, todos os módulos serão locais.

### Por que `type="module"`?

Para que o navegador trate o arquivo como módulo:

```html
<script type="module" src="assets/main.js"></script>
```

Scripts de módulo:

- permitem `import` e `export`;
- possuem escopo próprio;
- são adiados por padrão;
- executam em modo estrito;
- seguem regras de origem e servidor.

Abra o laboratório por `http://localhost`, não diretamente por `file://`, porque
módulos dependem das regras de carregamento do navegador.

### O que é tratamento de erros?

Tratamento de erros é a estratégia para detectar, representar, registrar e comunicar
falhas sem deixar a aplicação em um estado confuso.

```js
try {
  const task = createTask(input);
  showTask(task);
} catch (error) {
  showMessage("Não foi possível criar a tarefa.");
}
```

`try...catch` não serve para esconder todo problema. Ele deve capturar falhas que a
camada atual consegue tratar ou traduzir.

### Erro e validação são a mesma coisa?

Não obrigatoriamente. Um campo vazio é um resultado esperado da interação e pode ser
representado por um retorno de validação. Uma dependência indisponível ou uma
invariante violada pode justificar uma exceção.

Nesta aula, usaremos uma exceção personalizada para representar dados de tarefa
inválidos e a converteremos em uma mensagem apropriada para a interface.

### Onde isso aparece no projeto?

O laboratório separará:

- regras e validação em `task-rules.js`;
- classes de erro em `errors.js`;
- interação com DOM em `main.js`.

A interface continuará utilizável depois de entradas inválidas e erros simulados.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar o que é um módulo;
2. diferenciar módulo, pacote, biblioteca e framework;
3. exportar e importar valores nomeados;
4. reconhecer exportação padrão;
5. compreender escopo e carregamento de módulos;
6. organizar dependências sem ciclos;
7. criar e lançar objetos `Error`;
8. utilizar `try`, `catch` e `finally`;
9. criar erros personalizados;
10. diferenciar mensagem técnica e mensagem para usuário;
11. tratar uma falha na camada adequada;
12. manter a interface consistente após um erro.

## Pré-requisitos

- Aulas 2.1 a 2.5 concluídas;
- funções, escopo, arrays e objetos;
- JSON e validação;
- projeto aberto por servidor HTTP local.

## Pergunta orientadora

> Como separar responsabilidades em arquivos e permitir que a aplicação se recupere de falhas previsíveis?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Conceito e carregamento de módulos | 20 min |
| Importações, exportações e arquitetura | 30 min |
| Erros, exceções e recuperação | 30 min |
| Laboratório modular | 25 min |
| Revisão | 5 min |

## 1. O problema do arquivo único

Um único `main.js` pode começar pequeno e acumular:

- validação;
- regras de negócio;
- chamadas de API;
- manipulação do DOM;
- mensagens;
- formatação;
- armazenamento.

Quando tudo depende de tudo, alterações simples ficam arriscadas. Módulos criam
fronteiras:

```text
main.js
  ├── usa task-rules.js
  └── reconhece errors.js
```

## 2. Exportação nomeada

```js
// math.js
export function sum(a, b) {
  return a + b;
}

export const maximumAttempts = 3;
```

Importação:

```js
import { sum, maximumAttempts } from "./math.js";
```

Os nomes entre chaves precisam corresponder aos nomes exportados.

## 3. Exportar ao final

```js
function sum(a, b) {
  return a + b;
}

const maximumAttempts = 3;

export { sum, maximumAttempts };
```

As duas formas são válidas. Escolha um padrão consistente.

## 4. Renomear importação

```js
import { sum as addNumbers } from "./math.js";
```

Isso pode resolver conflitos, mas renomeações excessivas dificultam localizar a
origem do conceito.

## 5. Exportação padrão

```js
// formatter.js
export default function formatCurrency(value) {
  return new Intl.NumberFormat("pt-BR", {
    style: "currency",
    currency: "BRL",
  }).format(value);
}
```

Importação:

```js
import formatCurrency from "./formatter.js";
```

Uma exportação padrão pode receber qualquer nome na importação. Isso traz
flexibilidade, mas pode gerar nomes diferentes para o mesmo conceito.

Neste curso, preferiremos exportações nomeadas para regras, pois elas tornam o
contrato explícito.

## 6. Importar tudo como namespace

```js
import * as taskRules from "./task-rules.js";

taskRules.normalizeTitle("  Estudar  ");
```

É útil quando o agrupamento comunica contexto. Não use apenas para evitar escolher
as dependências realmente necessárias.

## 7. Caminhos relativos

No navegador:

```js
import { createTask } from "./task-rules.js";
```

Observe:

- `./` significa “a partir da pasta atual”;
- `../` volta uma pasta;
- a extensão `.js` normalmente precisa estar explícita;
- letras maiúsculas e minúsculas podem importar em servidores diferentes.

Este caminho está incorreto para um arquivo local:

```js
import { createTask } from "task-rules";
```

Um nome sem `./` ou `../` é um especificador de pacote e exige resolução adicional,
como bundler ou import map.

## 8. Escopo de módulo

```js
// settings.js
const privateValue = "interno";
export const publicValue = "externo";
```

`privateValue` não vira uma variável global e não pode ser importada. Somente o que
é exportado compõe o contrato público.

Evite exportar detalhes que outros módulos não precisam conhecer.

## 9. Módulos executam uma vez

Quando vários arquivos importam o mesmo módulo, o navegador carrega e avalia aquele
módulo uma vez por contexto. As importações compartilham a mesma instância.

Isso significa que estado mutável no nível do módulo pode ser compartilhado:

```js
let count = 0;

export function increment() {
  count += 1;
  return count;
}
```

Use estado de módulo conscientemente. Funções puras continuam sendo mais fáceis de
testar.

## 10. Importações são vínculos vivos

Valores importados acompanham a variável exportada:

```js
// counter.js
export let count = 0;

export function increment() {
  count += 1;
}
```

O importador observa o valor atualizado, mas não pode reatribuir diretamente
`count`.

## 11. Efeitos ao importar

Código no nível superior executa quando o módulo é avaliado:

```js
console.log("Módulo carregado");
```

Evite iniciar operações inesperadas apenas por importar. Um módulo de regras deve
preferir exportar funções e aguardar chamadas explícitas.

## 12. Dependências circulares

Uma dependência circular aparece quando:

```text
a.js importa b.js
b.js importa a.js
```

JavaScript pode resolver alguns ciclos, mas valores podem ser acessados antes da
inicialização e a arquitetura fica difícil de entender.

Soluções:

- mover conceitos compartilhados para um terceiro módulo;
- inverter a dependência;
- passar callbacks ou dados por parâmetro;
- revisar responsabilidades.

## 13. Um módulo por responsabilidade, não por função

Não é necessário criar um arquivo para cada função. Agrupe regras que mudam pelo
mesmo motivo:

```text
assets/
  main.js          # interface e coordenação
  task-rules.js    # modelo e regras de tarefa
  errors.js        # tipos de erro compartilhados
```

Módulos excessivamente pequenos podem tornar a navegação tão difícil quanto um
arquivo grande.

## 14. O objeto `Error`

```js
const error = new Error("Falha ao criar tarefa");
```

Um erro possui normalmente:

- `name`;
- `message`;
- `stack`, para diagnóstico;
- opcionalmente `cause`.

Lançar:

```js
throw new Error("Falha ao criar tarefa");
```

`throw` interrompe o fluxo atual até encontrar um `catch` adequado.

## 15. Lance objetos `Error`

JavaScript permite lançar qualquer valor:

```js
throw "falhou";
```

Evite. Objetos `Error` preservam nome, mensagem, pilha e causa:

```js
throw new Error("Falhou");
```

## 16. `try...catch`

```js
try {
  const parsed = JSON.parse(jsonText);
  useValue(parsed);
} catch (error) {
  showMessage("Não foi possível ler o JSON.");
}
```

Somente erros lançados dentro do `try` são capturados por esse `catch`.

Mantenha o bloco `try` limitado às operações que realmente podem falhar e que serão
tratadas da mesma forma.

## 17. `finally`

`finally` executa independentemente de sucesso ou falha:

```js
setLoading(true);

try {
  processTask();
} catch (error) {
  showError(error);
} finally {
  setLoading(false);
}
```

É útil para liberar recursos ou restaurar estado da interface.

Evite colocar `return` em `finally`, pois ele pode substituir retornos e erros
anteriores.

## 18. Erros personalizados

```js
export class TaskValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = "TaskValidationError";
    this.field = field;
  }
}
```

Uso:

```js
if (title.length < 3) {
  throw new TaskValidationError(
    "Título deve ter ao menos 3 caracteres.",
    "title",
  );
}
```

Captura:

```js
try {
  createTask(input);
} catch (error) {
  if (error instanceof TaskValidationError) {
    showFieldError(error.field, error.message);
    return;
  }

  throw error;
}
```

Tipos personalizados permitem decisões sem comparar textos de mensagens.

## 19. Validação por retorno ou exceção?

Retorno explícito:

```js
const result = validateTask(input);

if (!result.ok) {
  showMessage(result.message);
}
```

Exceção:

```js
try {
  createTask(input);
} catch (error) {
  showMessage(error.message);
}
```

Não existe uma regra universal:

- retornos são adequados para alternativas esperadas;
- exceções ajudam quando a operação não consegue cumprir seu contrato;
- bibliotecas frequentemente combinam validação explícita e exceções inesperadas;
- o padrão deve ser consistente no projeto.

## 20. Não capture o que não sabe tratar

Este código esconde a falha:

```js
try {
  runApplication();
} catch {
  // vazio
}
```

Se a camada não consegue recuperar nem adicionar contexto, deixe o erro subir.

Capturar tudo e continuar pode deixar dados incorretos ou estado parcial.

## 21. Mensagem técnica e mensagem para usuário

Mensagem técnica:

```text
TypeError: Cannot read properties of undefined
```

Mensagem para usuário:

```text
Não foi possível processar a tarefa. Revise os dados e tente novamente.
```

O usuário precisa de orientação segura. A equipe precisa de contexto técnico em
registros controlados. Não exponha stack traces, caminhos internos, consultas ou segredos
na interface.

## 22. Adicionar contexto e preservar causa

```js
try {
  parseExternalData(text);
} catch (error) {
  throw new Error("Falha ao importar tarefas", {
    cause: error,
  });
}
```

Isso adiciona contexto sem perder a causa original.

## 23. Estado consistente após falha

Uma operação deve definir:

- o que muda antes;
- o que muda apenas no sucesso;
- o que precisa ser restaurado no `finally`;
- qual versão anterior deve ser preservada.

```js
setProcessing(true);

try {
  const task = createTask(input);
  currentTask = task;
  renderTask(task);
} catch (error) {
  renderError(error);
} finally {
  setProcessing(false);
}
```

Não substitua `currentTask` antes de saber que o novo valor é válido.

## 24. Falhas simuladas no laboratório

O laboratório oferece três cenários:

1. **sucesso:** a regra retorna uma tarefa;
2. **validação:** `TaskValidationError` produz orientação de campo;
3. **inesperado:** um erro genérico é traduzido para mensagem segura.

Em todos os casos, `finally` atualiza o indicador de finalização.

## 25. Laboratório guiado

Abra a [aplicação modular](../exemplos/aula-2.6/index.html).

### Etapa 1 — Observe os arquivos

```text
assets/
  errors.js
  task-rules.js
  main.js
  styles.css
```

Siga as importações a partir de `main.js`.

### Etapa 2 — Execute com sucesso

Informe um título válido. Observe:

- normalização;
- retorno do módulo de regras;
- atualização da interface;
- etapa `finally`.

### Etapa 3 — Provoque validação

Use um título curto. Confirme que:

- o erro é reconhecido por `instanceof`;
- o campo recebe orientação;
- nenhuma tarefa inválida substitui o último resultado válido.

### Etapa 4 — Simule falha inesperada

Selecione o cenário de falha interna. A interface deve:

- apresentar mensagem segura;
- manter detalhes técnicos fora da mensagem principal;
- continuar permitindo nova tentativa;
- executar `finally`.

## 26. Erros comuns

### Esquecer `type="module"`

O navegador exibirá erro ao encontrar `import` em script comum.

### Omitir `./`

```js
import { createTask } from "task-rules.js";
```

Para arquivo local:

```js
import { createTask } from "./task-rules.js";
```

### Abrir por `file://`

Use o servidor local:

```text
http://localhost/site/PosIA/...
```

### Criar dependência circular

Revise as responsabilidades em vez de adicionar novas importações cruzadas.

### Capturar e ignorar

Um `catch` vazio dificulta diagnóstico e pode esconder estado inválido.

### Mostrar `error.message` indiscriminadamente

Mensagens de erros externos podem conter detalhes inadequados. Traduza falhas na
fronteira com a interface.

### Usar exceção como fluxo comum em todo lugar

Não transforme toda decisão em `throw`. Use condições e retornos para alternativas
normais quando isso tornar o fluxo mais claro.

## 27. Boas práticas

- faça módulos terem uma razão clara para mudar;
- exporte apenas o contrato necessário;
- prefira dependências em uma direção;
- evite efeitos inesperados na importação;
- lance objetos `Error`;
- crie tipos de erro quando a camada precisa distingui-los;
- limite o bloco `try`;
- preserve estado anterior até o sucesso;
- use `finally` para limpeza;
- apresente mensagens seguras e acionáveis.

## 28. Exercícios

### Exercício 1 — Separação

Mova uma função de formatação para `formatters.js` e importe-a em `main.js`.

### Exercício 2 — Exportação

Crie uma exportação nomeada e uma padrão. Explique a diferença na importação.

### Exercício 3 — Erro personalizado

Crie `InvalidEstimateError` com a propriedade `receivedValue`.

### Exercício 4 — `finally`

Implemente um estado “processando” que sempre seja removido.

### Exercício 5 — Recuperação

Preserve o último resultado válido quando uma nova entrada falhar.

## 29. Desafio

Amplie o laboratório:

- adicione um módulo de formatadores;
- crie um erro específico para prioridade;
- inclua `cause` ao traduzir falhas internas;
- registre um identificador de diagnóstico sem expor stack trace;
- escreva um diagrama das dependências;
- elimine qualquer ciclo encontrado.

## 30. Lista de verificação de conclusão

- [ ] Sei diferenciar módulo, pacote e framework.
- [ ] Consigo usar `import` e `export`.
- [ ] Entendo caminhos relativos.
- [ ] Sei por que usar `type="module"`.
- [ ] Reconheço o escopo de módulo.
- [ ] Consigo lançar e capturar um `Error`.
- [ ] Sei quando `finally` executa.
- [ ] Consigo criar um erro personalizado.
- [ ] Diferencio mensagem técnica e mensagem para usuário.
- [ ] Preservo o último estado válido após uma falha.

## 31. Critérios de avaliação

| Critério | Pontos |
|---|---:|
| Separação e contrato dos módulos | 25 |
| Importações e exportações corretas | 20 |
| Tratamento específico de validação | 20 |
| Recuperação de falha inesperada | 20 |
| Estado consistente e `finally` | 10 |
| Mensagens acessíveis e seguras | 5 |
| **Total** | **100** |

## Resumo

Nesta aula, aprendemos que:

- módulos separam responsabilidades e tornam dependências explícitas;
- `import` e `export` pertencem ao JavaScript;
- módulo, pacote, biblioteca e framework são conceitos diferentes;
- exceções interrompem o fluxo até um tratamento apropriado;
- erros personalizados permitem decisões por tipo;
- `finally` restaura estado;
- a interface deve receber mensagens seguras e permanecer consistente.

## Próxima aula

Na Aula 2.7, trabalharemos com Promises, `async/await` e Fetch para consumir APIs,
tratar respostas HTTP e representar carregamento, sucesso, vazio e falha.
