# Aula 4.4 - Serviços e injeção de dependência

## Identificação

- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** serviço de tarefas compartilhado
- **Laboratório:** [`../exemplos/aula-4.4/index.html`](../exemplos/aula-4.4/index.html)

## Introdução

### O que é um serviço?

Serviço é uma classe criada para reunir código que não pertence exclusivamente à
apresentação de um componente. Ele pode guardar estado compartilhado, aplicar regras
de negócio, acessar uma API, registrar eventos ou coordenar outras dependências.

```text
Componente: apresenta a interface e recebe interações
Serviço: mantém dados e executa operações reutilizáveis
```

Na Knowledge AI, `TaskList` não precisa conhecer todos os detalhes de armazenamento.
Ela pede tarefas ao `TaskStore` e solicita operações como alternar status.

### Serviço é uma API ou servidor?

Não necessariamente. Nesta aula, o serviço é uma classe TypeScript executada no
front-end, dentro da aplicação Angular no navegador. Mais adiante, esse serviço
usará o cliente HTTP para conversar com a API NestJS.

```text
agora:       componente → serviço Angular → estado em memória
mais adiante: componente → serviço Angular → HTTP → API NestJS → MySQL
```

O serviço Angular não se conecta diretamente ao MySQL e não guarda credenciais do
banco no navegador.

### Serviço é igual a função utilitária?

Não. Uma função pura, como `formatDate(value)`, pode ser apenas uma função exportada.
Um serviço é útil quando há estado, dependências, ciclo de vida, substituição de
implementação ou uso compartilhado pelo sistema de injeção.

### O que é uma dependência?

Dependência é algo de que uma classe precisa para realizar seu trabalho. Se
`TaskList` usa `TaskStore`, então `TaskStore` é uma dependência de `TaskList`.

```ts
export class TaskList {
  protected readonly taskStore = inject(TaskStore);
}
```

A classe declara do que precisa, sem decidir como criar a instância.

### O que é injeção de dependência?

Injeção de dependência, ou DI, é o mecanismo que entrega uma dependência pronta a
quem a solicita. O Angular mantém injetores que procuram um provider para o token
pedido e devolvem a instância correspondente.

```text
componente pede TaskStore
        ↓
injector procura provider para TaskStore
        ↓
provider entrega a instância
```

### O que é um injector?

Injector é o objeto do Angular responsável por resolver pedidos. Podemos imaginá-lo
como um catálogo: a chave é um token e o valor descreve o que deve ser entregue.

```text
token TaskStore → instância de TaskStore
```

O token pode ser uma classe ou um `InjectionToken`. Nesta aula, a própria classe
`TaskStore` será o token.

### O que é um provider?

Provider é a configuração que ensina ao injector como obter uma dependência. Para um
serviço comum, o provider pode ser registrado automaticamente na raiz. Em casos
específicos, ele pode ser registrado manualmente em uma aplicação, rota ou
componente.

### Por que não usar `new TaskStore()`?

Com `new`, o componente escolhe e cria sua dependência. Isso pode gerar uma instância
diferente em cada lugar, dificulta substituições em testes e ignora o ciclo de vida
controlado pelo Angular.

```ts
// Evite dentro do componente
const store = new TaskStore();

// Solicite ao Angular
const store = inject(TaskStore);
```

Não é que `new` seja proibido em JavaScript. Ele apenas não é a forma correta de
obter uma dependência gerenciada pelo injector.

### O que são `@Service()` e `@Injectable()`?

Na documentação do Angular 22, `@Service()` é a forma moderna e concisa de declarar
um serviço singleton de raiz:

```ts
@Service()
export class TaskStore {}
```

Em Angular 21 ou anterior, e em muitos projetos atuais, você verá:

```ts
@Injectable({ providedIn: "root" })
export class TaskStore {}
```

As duas formas permitem `inject()`. `@Injectable` continua necessário para
configurações avançadas e compatibilidade. Este curso mostrará a API atual sem
esconder o padrão encontrado em projetos existentes.

### O que significa `providedIn: "root"`?

Significa que o serviço é fornecido pelo injector raiz da aplicação. Normalmente,
isso resulta em uma instância compartilhada por todas as partes que usam aquele
provider. O serviço também pode ser removido da compilação se não for usado.

### Singleton significa uma única instância para sempre?

Somente dentro do escopo daquele provider. Um provider raiz costuma entregar uma
instância para a aplicação. Se o mesmo serviço for fornecido novamente em um
componente, aquela parte da árvore recebe outra instância, vinculada ao ciclo de vida
do componente.

### Como isso entra na Knowledge AI?

Moveremos o array de tarefas e suas operações para `TaskStore`. Dois componentes
que injetarem o mesmo serviço raiz observarão o mesmo estado. A interface fica focada
em apresentar dados e encaminhar intenções.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar serviço, dependência, injector, provider e token;
2. diferenciar serviço Angular, servidor e função utilitária;
3. declarar um serviço atual com `@Service()`;
4. reconhecer `@Injectable({ providedIn: "root" })`;
5. solicitar uma dependência com `inject()`;
6. explicar por que evitar `new` para serviços gerenciados;
7. mover estado e operações de um componente para um serviço;
8. expor estado somente para leitura;
9. diferenciar provider raiz e provider de componente;
10. reconhecer erros comuns de resolução de dependências.

## Pré-requisitos

- Aulas 4.1 a 4.3 concluídas;
- classes TypeScript e modificadores de acesso;
- componentes, signals e estado derivado;
- atualização imutável de arrays;
- composição com inputs e outputs.

## Pergunta orientadora

> Como compartilhar regras e estado sem acoplar cada componente à criação das suas dependências?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| Serviço e responsabilidade | 20 min |
| Dependência, provider e injector | 30 min |
| `@Service`, `@Injectable` e `inject` | 25 min |
| Escopos e estado compartilhado | 25 min |
| Laboratório | 15 min |
| Revisão | 5 min |

## 1. Problema: componente com responsabilidades demais

```ts
export class TaskList {
  readonly tasks = signal<Task[]>(INITIAL_TASKS);

  toggleTask(id: number): void {
    // regra de atualização misturada à interface
  }
}
```

Isso funciona em uma tela pequena. Quando dashboard, lista e detalhe precisam das
mesmas tarefas, duplicar o estado cria divergências.

## 2. Extraindo o estado

```ts
import { computed, Service, signal } from "@angular/core";

@Service()
export class TaskStore {
  private readonly taskState = signal<readonly Task[]>(INITIAL_TASKS);

  readonly tasks = this.taskState.asReadonly();
  readonly openCount = computed(() =>
    this.tasks().filter((task) => task.status !== "done").length,
  );
}
```

O signal gravável fica privado. Consumidores recebem apenas a leitura e operações
nomeadas.

## 3. Serviço não precisa terminar em `Service`

O nome deve descrever o papel. `TaskStore` comunica que a classe mantém estado de
tarefas. `TaskApi`, `TaskRepository` ou `TaskLogger` teriam responsabilidades
diferentes. Evite um genérico `TaskService` que acumule tudo.

## 4. Criando pelo Angular CLI

```bash
ng generate service tasks/task-store
```

O CLI cria um arquivo próprio. A convenção atual usa nomes concisos; projetos
anteriores podem gerar `task-store.service.ts`.

## 5. `@Service()` no Angular atual

```ts
import { Service } from "@angular/core";

@Service()
export class TaskStore {}
```

No Angular 22, essa forma registra automaticamente um singleton no injector raiz e
suporta dependências obtidas com `inject()`.

## 6. Forma compatível com versões anteriores

```ts
import { Injectable } from "@angular/core";

@Injectable({ providedIn: "root" })
export class TaskStore {}
```

Use esta forma se a versão instalada ainda não oferece `@Service` ou se o projeto
mantém essa convenção. Não troque toda uma base apenas por estética.

## 7. Quando manter `@Injectable`

`@Injectable` permanece adequado quando há:

- injeção por parâmetros do construtor;
- escopo diferente de raiz, como `platform`;
- configurações avançadas de provider;
- necessidade de compatibilidade com Angular anterior;
- convenção consolidada no projeto.

## 8. Solicitando com `inject()`

```ts
import { Component, inject } from "@angular/core";
import { TaskStore } from "./task-store";

@Component({ /* ... */ })
export class TaskList {
  protected readonly taskStore = inject(TaskStore);
}
```

O guia de estilo atual prefere `inject()` pela legibilidade e inferência de tipos.

## 9. O que `inject(TaskStore)` faz?

1. usa `TaskStore` como token;
2. começa a busca no contexto de injeção atual;
3. procura um provider na hierarquia;
4. cria a instância quando necessário;
5. reutiliza a instância dentro do mesmo escopo;
6. devolve o objeto tipado.

## 10. Contexto de injeção

`inject()` não pode ser chamado em qualquer momento e lugar. Ele funciona em um
contexto onde o Angular conhece o injector atual, como inicializadores de campos de
componentes e serviços ou factories de providers.

```ts
export class TaskList {
  private readonly store = inject(TaskStore); // válido
}
```

Chamá-lo mais tarde em um clique comum pode gerar erro de contexto.

## 11. Injeção por construtor

```ts
constructor(private readonly taskStore: TaskStore) {}
```

Essa forma continua suportada com `@Injectable`. É frequente em projetos anteriores.
Para código novo, seguiremos `inject()` conforme o guia de estilo atual.

## 12. Encapsulando escrita

```ts
private readonly taskState = signal<readonly Task[]>(INITIAL_TASKS);
readonly tasks = this.taskState.asReadonly();
```

Se o signal gravável fosse público, qualquer componente poderia chamar `set` com um
estado inválido. O serviço deve proteger suas invariantes.

## 13. Operações de domínio

```ts
toggle(id: number): void {
  this.taskState.update((tasks) =>
    tasks.map((task) =>
      task.id === id
        ? { ...task, status: task.status === "done" ? "todo" : "done" }
        : task,
    ),
  );
}
```

O componente diz o que deseja; o serviço controla como o estado muda.

## 14. Estado derivado no serviço

```ts
readonly openCount = computed(() =>
  this.tasks().filter((task) => task.status !== "done").length,
);
```

Como a regra interessa a diferentes telas, centralizá-la evita resultados
divergentes.

## 15. Consumindo no template

```html
<p>{{ taskStore.openCount() }} tarefas abertas</p>

@for (task of taskStore.tasks(); track task.id) {
  <app-task-card
    [task]="task"
    (toggleRequested)="taskStore.toggle($event)"
  />
}
```

Para uma aplicação maior, o componente pode expor métodos próprios para evitar
acoplar todo o template ao serviço. Aqui deixamos a ligação visível para estudo.

## 16. Raiz compartilhada

```text
Root EnvironmentInjector
└── TaskStore · instância A
    ├── Dashboard recebe A
    └── TaskList recebe A
```

Quando o dashboard altera a instância A, a lista lê o mesmo estado reativo.

## 17. Provider no componente

Com um serviço não fornecido automaticamente, ou sobrescrevendo o provider raiz:

```ts
@Component({
  providers: [TaskStore],
})
export class IsolatedTaskPanel {
  private readonly store = inject(TaskStore);
}
```

Cada instância do componente cria seu próprio escopo e pode receber um `TaskStore`
isolado.

## 18. Hierarquia de injetores

```text
componente atual
      ↓ se não encontrou
componente ancestral
      ↓ se não encontrou
EnvironmentInjector da aplicação
      ↓ se não encontrou
erro de provider ausente
```

O Angular usa o primeiro provider encontrado para aquele token.

## 19. Token e implementação

Na forma simples, a classe cumpre os dois papéis:

```ts
providers: [TaskStore]
```

É um atalho para:

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

O token é a chave; `useClass` descreve a implementação.

## 20. Interfaces não existem em ambiente de execução

Uma interface TypeScript desaparece depois da compilação e não pode ser usada
diretamente como token:

```ts
interface TaskGateway {}
// inject(TaskGateway) // não funciona
```

Para contratos sem classe, usamos `InjectionToken<TaskGateway>`. Esse recurso será
aprofundado quando houver implementações reais e simuladas.

## 21. Provider raiz ou local?

| Necessidade | Escopo sugerido |
|---|---|
| estado compartilhado pela aplicação | raiz |
| instância isolada por editor | componente |
| dependência específica de uma área | rota |
| configuração global | aplicação ou token raiz |

Escolha conscientemente. “Singleton” não deve ser uma consequência acidental.

## 22. Ciclo de vida

Um serviço raiz costuma viver durante a aplicação. Um serviço fornecido pelo
componente é destruído com aquela instância do componente. Isso importa para estado,
subscriptions e recursos externos.

## 23. Serviço não é depósito de qualquer código

Evite uma classe gigante com autenticação, tarefas, notificações e formatação. Separe
por responsabilidade e área de domínio. A injeção ajuda a compor serviços menores.

## 24. Serviço pode depender de outro serviço

```ts
@Service()
export class TaskAudit {
  private readonly clock = inject(Clock);
}
```

O injector resolve a árvore de dependências. Dependências circulares indicam uma
fronteira arquitetural que precisa ser revista.

## 25. Testabilidade

Quando o componente solicita um token, um teste pode fornecer outra implementação.
Isso permite isolar a interface sem acessar rede ou banco. Não significa criar mocks
para tudo; substitua fronteiras externas ou comportamentos difíceis de controlar.

## 26. Laboratório guiado

Abra o [mapa de serviço e injetores](../exemplos/aula-4.4/index.html).

### Etapa 1 — Use o provider raiz

Altere uma tarefa no painel A. Observe o painel B lendo a mesma instância.

### Etapa 2 — Observe a resolução

O registro mostra o pedido, a busca do token e a instância entregue.

### Etapa 3 — Ative instâncias locais

Compare dois componentes com providers próprios. Uma alteração deixa de aparecer no
outro painel.

### Etapa 4 — Examine a fonte

Alterne entre serviço, componente e escopo do provider.

## 27. Sobre o laboratório estático

O laboratório reproduz o comportamento para abrir sem instalar Angular. A pasta
`src/app` contém a implementação Angular equivalente com `@Service()`, `inject()` e
signals. A simulação serve para observar identidade e escopo; não implementa o
injector real do framework.

## 28. Erros comuns

### Esquecer o provider

Se o injector não encontra o token na hierarquia, ocorre erro de resolução.

### Usar `new` no componente

O Angular perde controle sobre criação, compartilhamento e substituição.

### Tornar o signal gravável público

Qualquer consumidor pode quebrar as regras do estado.

### Achar que todo serviço é global

O escopo depende de onde o provider foi registrado.

### Usar `inject()` fora de contexto

A função precisa de um contexto de injeção ativo.

### Colocar todo código em serviços

Interação visual pertence ao componente; uma transformação pura pode ser uma função.

### Confundir serviço Angular com back-end

O código continua no navegador e não deve receber segredos de infraestrutura.

## 29. Boas práticas

- escolha nomes que expressem responsabilidade;
- mantenha um conceito principal por arquivo;
- prefira `inject()` em código Angular novo;
- preserve `@Injectable` quando a versão ou configuração exigir;
- mantenha estado gravável privado;
- exponha leitura e operações explícitas;
- registre o provider no menor escopo correto;
- evite dependências circulares;
- mantenha componentes focados na interface;
- nunca conecte o front-end diretamente ao MySQL.

## 30. Exercícios

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

Mova uma lista de usuários de um componente para `UserStore`.

### Exercício 2 — Leitura protegida

Exponha um signal somente para leitura e uma operação `add`.

### Exercício 3 — Injeção

Use `inject(UserStore)` em dois componentes.

### Exercício 4 — Escopo

Explique a diferença entre provider raiz e `providers: [UserStore]` no componente.

### Exercício 5 — Decisão

Classifique formatação de moeda, estado de sessão e clique de expansão como função,
serviço ou responsabilidade do componente.

## 31. Desafio

Crie um serviço de tarefas que:

- seja fornecido na raiz;
- mantenha o signal gravável privado;
- exponha tarefas somente para leitura;
- calcule totais com `computed`;
- possua operações de adicionar, alternar e remover;
- seja injetado com `inject()`;
- seja consumido por dois componentes;
- demonstre que ambos recebem a mesma instância;
- não contenha código visual;
- não acesse MySQL diretamente.

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

- [ ] Explico o que é um serviço Angular.
- [ ] Diferencio serviço front-end e servidor.
- [ ] Identifico uma dependência.
- [ ] Explico injector, provider e token.
- [ ] Reconheço `@Service()` e `@Injectable()`.
- [ ] Uso `inject()` em contexto válido.
- [ ] Evito criar serviços gerenciados com `new`.
- [ ] Encapsulo a escrita do estado.
- [ ] Diferencio provider raiz e local.
- [ ] Sei que interfaces não servem diretamente como tokens.

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

| Critério | Pontos |
|---|---:|
| Separação de responsabilidades | 20 |
| Declaração e injeção do serviço | 25 |
| Encapsulamento do estado | 20 |
| Entendimento de provider e escopo | 20 |
| Clareza, tipos e acessibilidade | 15 |
| **Total** | **100** |

## Resumo

Nesta aula, aprendemos que:

- serviço reúne estado ou comportamento reutilizável fora da apresentação;
- dependência é algo de que uma classe precisa;
- provider associa um token ao valor entregue;
- injector resolve pedidos pela hierarquia;
- `@Service()` é o atalho moderno para singleton raiz no Angular 22;
- `@Injectable({ providedIn: "root" })` continua válido e compatível;
- `inject()` é a forma preferida em código novo;
- provider raiz compartilha uma instância dentro daquele escopo;
- provider de componente pode criar instâncias isoladas;
- escrita privada e operações nomeadas protegem o estado.

## Próxima aula

Na Aula 4.5, criaremos rotas, layouts e navegação para transformar os componentes em
uma aplicação com múltiplas páginas.

## Fontes oficiais

- [Criação e uso de serviços](https://angular.dev/guide/di/creating-and-using-services)
- [Visão geral de injeção de dependência](https://angular.dev/guide/di)
- [Definição de providers](https://angular.dev/guide/di/defining-dependency-providers)
- [Hierarquia de injetores](https://angular.dev/guide/di/hierarchical-dependency-injection)
- [Contexto de injeção](https://angular.dev/guide/di/dependency-injection-context)
- [Guia de estilo do Angular](https://angular.dev/style-guide)
