# Aula 5.2 — Módulos, controladores e provedores

## Identificação

- **Módulo:** 5 — Node.js, NestJS e APIs
- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** `TasksModule` organizado com controlador, serviço e repositório em memória
- **Laboratório:** [`../exemplos/aula-5.2/index.html`](../exemplos/aula-5.2/index.html)

## Introdução

Na aula anterior criamos uma aplicação NestJS e um endpoint de saúde. Agora surge
uma dúvida comum: se tudo pode ser escrito em um único arquivo, por que separar o
código em módulos, controladores (`controllers`) e provedores (`providers`)?

Uma aplicação pequena até cabe em um arquivo. Porém, quando tarefas, usuários,
documentos e integrações começam a crescer, misturar rotas, regras e armazenamento
torna qualquer mudança perigosa. O NestJS fornece uma forma explícita de organizar
essas responsabilidades.

Antes de programar, responda mentalmente:

- módulo é apenas uma pasta?
- controller deve decidir todas as regras?
- provider é sempre um service?
- injeção de dependência significa instalar uma biblioteca?
- `imports` do TypeScript é igual ao `imports` do `@Module()`?
- uma interface TypeScript pode ser usada como token em ambiente de execução?

Ao final, essas diferenças estarão visíveis no código e no laboratório.

## Objetivos

Ao concluir a aula, você será capaz de:

1. explicar módulo, controlador, provedor e injeção de dependência;
2. diferenciar importação de arquivo e importação de módulo NestJS;
3. organizar uma capacidade de negócio em um feature module;
4. manter regras de negócio fora do controller;
5. registrar e resolver providers pelo contêiner de IoC;
6. usar classe, string ou `Symbol` como token de injeção;
7. explicar encapsulamento, `imports` e `exports`;
8. reconhecer provider ausente e dependência circular;
9. comparar os escopos default, requisição e transient;
10. implementar um repositório em memória substituível.

## Pré-requisitos

- Aula 5.1 concluída;
- noções iniciais de classe, interface e construtor em TypeScript;
- saber que uma requisição chega por uma rota HTTP;
- Node.js e npm disponíveis apenas para executar o exemplo real.

O laboratório visual funciona diretamente no XAMPP e não exige servidor Node.

## 1. O problema de colocar tudo no controller

Considere este código:

```ts
@Controller('tasks')
export class TasksController {
  private readonly tasks = [];

  @Get()
  list() {
    return this.tasks;
  }
}
```

Ele funciona, mas o controller agora conhece HTTP, armazenamento e regras. Isso
dificulta testes, reaproveitamento e a futura troca da memória pelo MySQL.

Adotaremos este fluxo:

```text
requisição HTTP
→ TasksController
→ TasksService
→ TASK_REPOSITORY
→ InMemoryTasksRepository
```

Cada parte tem uma responsabilidade clara.

## 2. O que é um módulo?

No NestJS, um módulo é uma classe decorada com `@Module()`. O decorator fornece
metadados para o framework montar o grafo da aplicação.

```ts
@Module({
  controllers: [TasksController],
  providers: [TasksService],
})
export class TasksModule {}
```

Um módulo não é apenas uma pasta. A pasta organiza arquivos; a classe decorada
declara ao NestJS o que pertence àquela parte da aplicação.

Toda aplicação possui um módulo raiz. O `AppModule` é o ponto inicial usado pelo
NestJS para descobrir os outros módulos.

## 3. Feature module

Um feature module agrupa elementos relacionados a uma capacidade do produto.

```text
src/
├── app.module.ts
└── tasks/
    ├── domain/
    │   └── task.ts
    ├── repositories/
    │   ├── tasks-repository.ts
    │   └── in-memory-tasks.repository.ts
    ├── tasks.controller.ts
    ├── tasks.service.ts
    └── tasks.module.ts
```

O nome `TasksModule` representa a capacidade de administrar tarefas, não uma camada
genérica chamada “controllers” ou “services”. Essa organização aproxima arquivos
que mudam pelo mesmo motivo.

## 4. O que o `@Module()` recebe?

As propriedades principais são:

| Propriedade | Função |
|---|---|
| `controllers` | controllers HTTP pertencentes ao módulo |
| `providers` | dependências que o contêiner pode criar ou fornecer |
| `imports` | módulos que exportam dependências necessárias aqui |
| `exports` | providers que formam a interface pública do módulo |

```ts
@Module({
  imports: [],
  controllers: [TasksController],
  providers: [TasksService],
  exports: [TasksService],
})
export class TasksModule {}
```

Não exporte tudo automaticamente. Um provider não exportado permanece encapsulado
e só pode ser usado dentro do módulo que o registrou.

## 5. `import` TypeScript versus `imports` do NestJS

Estas linhas cumprem trabalhos diferentes:

```ts
import { TasksModule } from './tasks/tasks.module';

@Module({
  imports: [TasksModule],
})
export class AppModule {}
```

O primeiro `import` permite que o arquivo TypeScript referencie a classe. O array
`imports` informa ao contêiner NestJS que o módulo faz parte do grafo da aplicação.
Um não substitui o outro.

## 6. O que é um controller?

Controller é a porta de entrada HTTP. Ele deve:

- declarar rotas e verbos;
- ler parâmetros, cabeçalhos e corpo;
- delegar trabalho;
- devolver a resposta adequada.

```ts
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  list(): Task[] {
    return this.tasksService.list();
  }
}
```

O controller não cria o service com `new TasksService()`. Ele declara que precisa
da dependência e o NestJS a entrega.

## 7. O que é um provider?

Provider é qualquer valor que pode ser gerenciado e injetado pelo contêiner do
NestJS. Services, repositórios, factories, configurações e adaptadores podem ser
providers.

```ts
@Injectable()
export class TasksService {
  list(): Task[] {
    return [];
  }
}
```

`@Injectable()` adiciona os metadados necessários para a classe participar do
sistema de injeção. O nome “service” é uma convenção de responsabilidade, não um
tipo especial diferente de provider.

## 8. Injeção de dependência sem mistério

Sem injeção:

```ts
const repository = new InMemoryTasksRepository();
const service = new TasksService(repository);
const controller = new TasksController(service);
```

Com o contêiner:

```ts
constructor(private readonly tasksService: TasksService) {}
```

O NestJS lê o token solicitado, procura o registro, cria as dependências na ordem
correta e entrega a instância. Isso é inversão de controle: a classe informa do que
precisa, mas não controla a montagem completa.

## 9. Token de injeção

O contêiner funciona como um mapa:

```text
token → provider
```

Quando a própria classe é usada:

```ts
providers: [TasksService]
```

Isso equivale conceitualmente a:

```ts
providers: [
  { provide: TasksService, useClass: TasksService },
]
```

O token é `TasksService` e a implementação também.

## 10. Por que uma interface não basta como token?

Interfaces TypeScript desaparecem quando o código vira JavaScript. Portanto, o
NestJS não consegue procurar uma interface em ambiente de execução.

```ts
export interface TasksRepository {
  findAll(): Task[];
}
```

Criamos um token que existe em JavaScript:

```ts
export const TASK_REPOSITORY = Symbol('TASK_REPOSITORY');
```

E fazemos a associação:

```ts
{
  provide: TASK_REPOSITORY,
  useClass: InMemoryTasksRepository,
}
```

## 11. Implementando o domínio

```ts
export interface Task {
  readonly id: string;
  readonly title: string;
  readonly completed: boolean;
}
```

O domínio não precisa conhecer decorators HTTP, NestJS ou MySQL.

## 12. Contrato do repositório

```ts
import type { Task } from '../domain/task';

export const TASK_REPOSITORY = Symbol('TASK_REPOSITORY');

export interface TasksRepository {
  findAll(): Task[];
  add(title: string): Task;
}
```

Na Aula 6, outra classe poderá implementar o mesmo contrato usando MySQL e Prisma.
O controller não precisará ser reescrito por causa dessa troca.

## 13. Repositório em memória

```ts
@Injectable()
export class InMemoryTasksRepository implements TasksRepository {
  private readonly tasks: Task[] = [
    { id: 'task-1', title: 'Estudar módulos NestJS', completed: false },
  ];

  findAll(): Task[] {
    return this.tasks.map((task) => ({ ...task }));
  }

  add(title: string): Task {
    const task = {
      id: `task-${this.tasks.length + 1}`,
      title,
      completed: false,
    } as const;
    this.tasks.push(task);
    return { ...task };
  }
}
```

Retornar cópias reduz o risco de outro componente alterar o array interno sem
passar pelas regras do repositório.

## 14. Service com regra de aplicação

```ts
@Injectable()
export class TasksService {
  constructor(
    @Inject(TASK_REPOSITORY)
    private readonly repository: TasksRepository,
  ) {}

  list(): Task[] {
    return this.repository.findAll();
  }

  create(title: string): Task {
    const normalizedTitle = title.trim();
    if (normalizedTitle.length < 3) {
      throw new BadRequestException('O título deve possuir ao menos 3 caracteres.');
    }
    return this.repository.add(normalizedTitle);
  }
}
```

O `@Inject(TASK_REPOSITORY)` é necessário porque o tipo da interface não existe em
ambiente de execução. O token existe.

## 15. Controller fino

```ts
@Controller('tasks')
export class TasksController {
  constructor(private readonly tasksService: TasksService) {}

  @Get()
  list(): Task[] {
    return this.tasksService.list();
  }

  @Post()
  create(@Body('title') title: string): Task {
    return this.tasksService.create(title);
  }
}
```

Na Aula 5.4 substituiremos a leitura direta do corpo por um DTO validado.

## 16. Registrando o `TasksModule`

```ts
@Module({
  controllers: [TasksController],
  providers: [
    TasksService,
    {
      provide: TASK_REPOSITORY,
      useClass: InMemoryTasksRepository,
    },
  ],
  exports: [TasksService],
})
export class TasksModule {}
```

O `TasksController` solicita `TasksService`. O service solicita `TASK_REPOSITORY`.
O contêiner resolve o grafo de baixo para cima.

## 17. Importando no módulo raiz

```ts
@Module({
  imports: [HealthModule, TasksModule],
})
export class AppModule {}
```

Com o prefixo global `/api`, as rotas ficam:

```text
GET  /api/health
GET  /api/tasks
POST /api/tasks
```

## 18. Encapsulamento entre módulos

Suponha que `ReportsModule` precise de `TasksService`:

1. `TasksModule` precisa exportar `TasksService`;
2. `ReportsModule` precisa importar `TasksModule`;
3. só então o service pode ser injetado no módulo consumidor.

```ts
@Module({
  imports: [TasksModule],
  providers: [ReportsService],
})
export class ReportsModule {}
```

`exports` não cria uma resposta HTTP nem exporta arquivo JavaScript. Ele controla a
visibilidade do provider no grafo NestJS.

## 19. Providers globais

`@Global()` pode tornar providers disponíveis em toda a aplicação, mas o uso excessivo
esconde dependências. Prefira imports explícitos. Global costuma fazer sentido para
infraestrutura transversal cuidadosamente controlada, como configuração.

## 20. Escopo e tempo de vida

| Escopo | Instância |
|---|---|
| `DEFAULT` | uma instância compartilhada; padrão recomendado |
| `REQUEST` | nova instância para cada requisição |
| `TRANSIENT` | nova instância para cada consumidor |

Não use escopo por requisição apenas porque existe uma requisição HTTP. Ele tem custo adicional
e pode propagar o escopo pela cadeia de dependências.

## 21. Provider ausente

Se `TasksService` solicitar `TASK_REPOSITORY`, mas o módulo não registrar esse token,
a inicialização falhará. A mensagem normalmente informa que o NestJS não consegue
resolver uma dependência em determinada posição do construtor.

Lista de verificação de diagnóstico:

1. o provider possui decorator quando necessário?
2. foi adicionado a `providers`?
3. o token registrado é exatamente o token injetado?
4. se vem de outro módulo, foi exportado?
5. o módulo consumidor importou o módulo fornecedor?

## 22. Dependência circular

Uma dependência circular aparece quando A depende de B e B depende de A:

```text
TasksService → ReportsService → TasksService
```

O NestJS possui `forwardRef()` para casos inevitáveis, mas a primeira ação deve ser
reavaliar responsabilidades. Muitas dependências circulares indicam limites mal
definidos.

## 23. Decorators e metadados

Decorators como `@Module()`, `@Controller()`, `@Get()` e `@Injectable()` não são
comentários. Eles associam metadados às classes e métodos. Durante a inicialização, o
NestJS usa esses metadados para construir módulos, registrar rotas e resolver
dependências.

## 24. Comparação com Angular

Angular e NestJS usam conceitos parecidos de componentes, decorators e DI, mas têm
responsabilidades diferentes:

| Angular | NestJS |
|---|---|
| executa a interface no navegador | executa a API no Node.js |
| componente recebe interação visual | controller recebe HTTP |
| service atende a interface | provider executa regras e infraestrutura |
| router troca telas | router seleciona endpoints |

Eles podem compartilhar ideias arquiteturais, mas não são a mesma aplicação.

## 25. E o XAMPP?

O Apache do XAMPP continua servindo este curso e pode servir arquivos do Angular.
A API NestJS real é outro processo:

```text
Apache/XAMPP: http://localhost/site/PosIA
NestJS:       http://localhost:3000/api/tasks
MySQL:        serviço de banco, sem acesso direto pelo navegador
```

Usar XAMPP não obriga a escrever a API em PHP. Apenas tome cuidado para não escolher
uma porta já ocupada.

## 26. Testabilidade

Com dependências injetadas, um teste pode trocar o repositório real por um objeto
controlado:

```ts
const repository: TasksRepository = {
  findAll: () => [],
  add: (title) => ({ id: 'test-1', title, completed: false }),
};

const service = new TasksService(repository);
```

Não há MySQL, rede ou servidor HTTP nesse teste. A regra é verificada isoladamente.

## 27. Roteiro de implementação

| Etapa | Tempo sugerido |
|---|---:|
| Introdução e responsabilidades | 15 min |
| Módulos e encapsulamento | 20 min |
| Controllers e providers | 20 min |
| DI, tokens e repositório | 30 min |
| Escopos e erros comuns | 15 min |
| Laboratório | 15 min |
| Revisão | 5 min |

## 28. Executando o exemplo real

Na pasta do laboratório:

```bash
npm install
npm run start:dev
```

Depois teste:

```bash
curl http://localhost:3000/api/tasks
```

O laboratório visual não executa esses comandos. Ele simula o contêiner para ser
usado com segurança no navegador.

## 29. Laboratório guiado

Abra o [Mapa do contêiner NestJS](../exemplos/aula-5.2/index.html).

### Etapa 1 — Resolva o grafo correto

1. mantenha “Registro correto”;
2. selecione o escopo `DEFAULT`;
3. clique em **Executar inicialização**;
4. observe a ordem repositório → service → controller.

### Etapa 2 — Faça requisições

1. liste as tarefas;
2. crie uma tarefa válida;
3. liste novamente;
4. observe que a instância default preserva os dados em memória.

### Etapa 3 — Quebre o registro

1. selecione “Token do repositório ausente”;
2. execute a inicialização;
3. identifique exatamente qual dependência não pôde ser resolvida.

### Etapa 4 — Compare escopos

1. volte ao registro correto;
2. compare `DEFAULT`, `REQUEST` e `TRANSIENT`;
3. envie duas requisições;
4. acompanhe a quantidade simulada de instâncias.

## 30. Erros comuns

### Criar dependências manualmente

```ts
const service = new TasksService(new InMemoryTasksRepository());
```

Isso contorna o contêiner e aumenta o acoplamento.

### Colocar regra no controller

Controllers grandes ficam difíceis de testar e reaproveitar.

### Confundir interface com token

A interface ajuda o TypeScript, mas desaparece em ambiente de execução.

### Exportar tudo

Transforma detalhes internos em dependências públicas.

### Usar módulo global para evitar imports

Reduz a visibilidade das relações e dificulta manutenção.

### Resolver arquitetura circular somente com `forwardRef()`

O código pode iniciar, mas o problema de responsabilidades continua.

## 31. Exercício de fixação

Crie um `DocumentsModule` contendo:

- `DocumentsController`;
- `DocumentsService`;
- contrato `DocumentsRepository`;
- token `DOCUMENTS_REPOSITORY`;
- implementação em memória;
- rota `GET /api/documents`.

Explique por escrito qual elemento seria substituído ao conectar MySQL.

## 32. Desafio individual

Crie um `ReportsModule` que consuma uma versão pública do `TasksService`:

1. exporte somente o provider necessário;
2. importe `TasksModule` em `ReportsModule`;
3. gere um resumo com total de tarefas;
4. não importe arquivos internos do repositório;
5. desenhe o grafo final de módulos e providers.

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

- [ ] Sei explicar por que módulo não é apenas pasta.
- [ ] Diferencio `import` TypeScript de `imports` do NestJS.
- [ ] Meu controller apenas traduz HTTP e delega regras.
- [ ] Registrei controller e providers no módulo correto.
- [ ] Sei por que uma interface não funciona sozinha como token.
- [ ] Consigo usar um `Symbol` como token.
- [ ] Entendo `imports`, `providers`, `controllers` e `exports`.
- [ ] Reconheço provider ausente e dependência circular.
- [ ] Sei comparar os três escopos.
- [ ] Mantive o navegador sem acesso direto ao MySQL.

## 34. Rubrica da entrega

| Critério | Pontos |
|---|---:|
| Organização do feature module | 20 |
| Controller fino | 15 |
| Regra no service | 15 |
| Contrato e token do repositório | 15 |
| Registro correto dos providers | 15 |
| Encapsulamento e exports | 10 |
| Explicação do grafo | 10 |
| **Total** | **100** |

## 35. Fontes oficiais

- [NestJS — Modules](https://docs.nestjs.com/modules)
- [NestJS — Controllers](https://docs.nestjs.com/controllers)
- [NestJS — Providers](https://docs.nestjs.com/providers)
- [NestJS — Custom providers](https://docs.nestjs.com/fundamentals/custom-providers)
- [NestJS — Injection scopes](https://docs.nestjs.com/fundamentals/injection-scopes)
- [NestJS — Circular dependency](https://docs.nestjs.com/fundamentals/circular-dependency)

## Próxima aula

Na Aula 5.3, transformaremos o módulo de tarefas em uma API REST completa, estudando
recursos, verbos, parâmetros, status HTTP, idempotência e contratos de resposta.
