---
# System prepended metadata

title: Diretrizes - Front-end

---

# Diretrizes de desenvolvimento

## Sumário
[Design System](#design-system)
* [Standalone Components](#standalone-components)
* [Organização do Projeto](#organizacao-projeto)
* [Lazy-load nas rotas ](#lazy-loading)
* [Pré-carregamento de Dados com Resolvers](#resolvers)

[Padrões de Nomeclatura](#nomeclatura)

[Reatividade](#reatividade)
* [RxJS](#rxjs)
    * [Gerenciamento de Estado e Subjects](#rxjs-gerenciamento-estado)
    * [Encapsulamento e Compartilhamento de Subjects](#rxjs-compartilhamento-subjects)
    * [Pureza e Efeitos Colaterais](#rxjs-efeitos-coleterais)
    * [Gerenciamento de Subscriptions](#rxjs-gerenciamento-subscriptions)
    * [Combinação de Múltiplos Observables](#rxjs-combinacao-observables)
* [Signals](#signals)
* [ReactiveForms](#reactive-forms)
	* [Estrutura e Tipagem](#reactive-forms-estrutura)
	* [Validação e Feedback ao Usuário](#creactive-forms-validacaos)
	* [Reatividade](#reactive-forms-reativade)

[Componentização](#componentizacao)
* [CONTAINER (Smart Component)](#componentizacao-smart-component)
* [PRESENTER (Dumb Component - Feature Específica)](#componentizacao-dumb-component)
* [SHARED COMPONENT (Dumb Component - Reutilizável)](#componentizacao-shared-component)

[Comunicação entre Componentes](#comunicacao-components)
* [Comunicação Vertical](#comunicacao-components-vertical)
* [Comunicação Horizontal/Global](#comunicacao-components-horizontal)
* [Práticas a Evitar](#comunicacao-components-evitar)
	* [Acesso Direto](#comunicacao-components-evitar-acesso-direto)
	* [Anti-Performance e Impureza](#comunicacao-components-evitar-acesso-impureza)

[Estilização](#estilizacao)
* [Tema PrimeNG](#estilizacao-tema)
    * [Estrutura](#estilizacao-tema-estrutura)
    * [Palette](#estilizacao-tema-palette)
    * [Spacing](#estilizacao-tema-spacing)
    * [Typography](#estilizacao-tema-typography)
    * [Theme](#estilizacao-tema-theme)
    * [Components](#estilizacao-tema-components)
    * [Index](#estilizacao-tema-index)
* [Tailwind CSS](#estilizacao-tailwind)

[Comunicação com Back-End](#comunicacao-back)
* [API Layer](#comunicacao-back-api-layer)
* [API Facade Layer](#comunicacao-back-api-facade-layer)
* [DTOs de Request](#comunicacao-back-dto-request)
* [DTOs de Response](#comunicacao-back-dto-response)
* [DTOs internos](#comunicacao-back-dto-interno)

[Tratamento de Exceções](#tratamento-excecoes)

[Testes](#teste)
* [Imports](#testes-imports)
* [Boas práticas](#testes-boas-praticas)
* [Organização e Nomeclatura](#testes-organizacao-nomeclatura )

[Melhores Práticas de Implementação](#melhores-praticas-de-implementacao)

[Glossário](#glossario)


___
## Design System<a name="design-system"></a>

### Standalone Components<a name="standalone-components"></a>
**Deve:**
- Criar componentes com `standalone: true` (Padrão na versão 19 do angular).
- Desde o Angular 17+, a abordagem standalone components permite eliminar a necessidade de NgModule, simplificando a estrutura e reduzindo acoplamento. Cada feature folder pode conter componentes standalone e importar diretamente dependências específicas, sem módulos intermediários.
- Importar dependências diretamente no `imports` do decorador `@Component`.
- O gerenciamento de dependências e provedores globais, incluindo as configurações de roteamento e serviços HTTP, deve ser centralizado no array providers do `app.config.ts` as funções auxiliares específicas do Angular, como `provideHttpClient` e `provideRouter`.
**Exemplo:**
```ts
export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    provideAnimationsAsync(),
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes)
  ],
};
```
___
## Padrões de nomeclatura<a name="nomeclatura"></a>
* A idioma padrão para escrita de nomes de métodos, atributos, classes e arquivos é o inglês.

| Elemento | Case / Formato | Convenção e Padrão | Exemplo |
| :--- | :--- | :--- | :--- |
| **Arquivo** | `kebab-case` | `feature.type.ts` | `user-list.component.ts` |
| **Classe** | `PascalCase` | Sufixo correspondente ao tipo. | `UserListComponent` |
| **Interface** | `PascalCase` | Nome da entidade (com prefixo `I`). | `interface IUser { ... }` |
| **Enum** | `PascalCase` | Nome do conjunto de valores. | `enum StatusOrder { ... }` |
| **Atributo (Público)** | `camelCase` | Substantivo descritivo. | `username: string;` |
| **Atributo (Privado)** | `_camelCase` | Prefixo `_` uso interno. | `private _selectedItems: Item[];` |
| **Atributo (Observable)** | `camelCase$` | Sufixo `$` para indicar um stream. | `users$: Observable<Users[]>;` |
| **Constante de Classe** | `UPPER_SNAKE_CASE` | Para valores estáticos imutáveis. | `static readonly API_URL = '...';` |
| **Método (Público)** | `camelCase()` | Verbo ou frase verbal. | `getData()` |
| **Método (Privado)** | `_camelCase()` | Prefixo `_` para uso interno. | `private _validateForm()` |
| **Método (Handler)** | `on` + `NomeEvento` | Responde a eventos. | `onClick(event: Event)` |
| **Booleano** | `is/has/should` | Prefixo para clareza. | `isVisible: boolean;` |
___
### Organização do Projeto (Folder-by-Feature) ?<a name="organizacao-projeto"></a>

- Usar nomes individuais para arquivos individuais como `cidadao.service.ts` 
- Plurais para agrupadores de multiplos itens com o `cidadaos/` 

#### 📁 app/core

Contém todos os **recursos essenciais da aplicação**, carregados uma única vez no `bootstrap`.

**Inclui:**
- **services/** – serviços globais (autenticação, logger, configuração da API, etc.);
- **interceptors/** – interceptadores HTTP (ex: AuthInterceptor, LoadingInterceptor);
- **guards/** – rotas protegidas (`AuthGuard`, `RoleGuard`);
- **layout/** – componentes de layout global (navbar, sidebar, footer);
- **config/** – constantes e tokens de injeção global (`API_URL`, `ENVIRONMENT`).

> 🔹 O módulo configurado no `app.config.ts`.

#### 📁 app/features

Agrupa cada **módulo funcional** da aplicação de forma isolada.

Cada subpasta representa uma **feature independente**, com suas próprias **rotas, componentes, serviços e estado**.

**Exemplo: `/features/users`**
```
src/
├── app/
│   ├── core/                            # Módulo central (singleton services e interceptors)
│   │   ├── guards/
│   │   │   ├── auth.guard.ts
│   │   │   ├── role.guard.ts
│   │   ├── interceptors/
│   │   │   ├── auth.interceptor.ts
│   │   │   ├── error-handler.interceptor.ts
│   │   ├── services/
│   │   │   ├── logging.service.ts
│   │   │   ├── auth.service.ts
│   │   ├── config/
│   │   │   ├── app.config.ts
│   │   └── core.providers.ts
│   │
│   ├── shared/                          # Componentes, diretivas e pipes reutilizáveis
│   │   ├── components/
│   │   │   ├── button/
│   │   │   │   ├── button.component.ts
│   │   │   │   ├── button.component.html
│   │   │   │   └── button.component.scss
│   │   ├── pipes/
│   │   │   ├── cpf-mask.pipe.ts
│   │   ├── directives/
│   │   │   ├── autofocus.directive.ts
│   │   └── utils/
│   │       └── date.util.ts
│   │   
│   ├── features/                        # Áreas funcionais do sistema
│   │   ├── cidadaos/
│   │   │   ├── pages/
│   │   │   │   ├── cidadao-list/
│   │   │   │   │   ├── cidadao-list-table/
│   │   │   │   │   │   ├── cidadao-list-table.presenter.ts   
│   │   │   │   │   │   ├── cidadao-list-table.presenter.html
│   │   │   │   │   │   └── cidadao-list-table.presenter.scss  
│   │   │   │   │   ├── cidadao-list.container.ts           
│   │   │   │   │   ├── cidadao-list.container.html
│   │   │   │   │   ├── cidadao-list.container.scss   
│   │   │   │   │   └── cidadao-list.routes.ts
│   │   │   │     
│   │   │   ├── services/
│   │   │   │   └── cidadao.service.ts
│   │   │   ├── models/
│   │   │   │   └── cidadao.model.ts
│   ├── app.routes.ts                    # Configuração das rotas standalone
│   ├── app.config.ts                    # Configuração global (bootstrapApplication)
│   └── main.ts   
```

> 🔹 Facilita o **lazy loading** e o isolamento de responsabilidades.  
> 🔹 Cada feature pode evoluir ou ser removida sem quebrar o restante da aplicação.

---

#### 📁 app/shared

Contém **elementos reutilizáveis** entre múltiplos módulos e recursos.

**Inclui:**
- **components/** – componentes genéricos (ex: `ModalComponent`, `LoadingSpinner`);
- **pipes/** – pipes de uso geral (`formatDate`, `maskCpf`);
- **directives/** – diretivas compartilhadas (`debounceClick`, `autoFocus`);
- **utils/** – helpers e funções utilitárias puras;

> 🔹 Nenhum componente aqui deve depender de um “feature” específico.

---

### Lazy-load nas rotas<a name="lazy-loading"></a>
- Carregar módulos da aplicação sob demanda (apenas quando o usuário acessa uma rota específica) e não no carregamento inicial. Isso reduz o tamanho do bundle inicial e melhora o tempo de carregamento da aplicação.
**Deve**: 
    - Utilizar **loadComponent** para carregar **Componentes** e **loadChildren** para carregar **Arquivos que definem as rotas de módulos**.
    
```ts
export const routes: Routes = [
    {
        path: Rotas.CLIENTES.listagem,
        loadChildren: () => import('./features/clientes/cliente.routes').then((routes) => routes.default)
    },
    {
        path: Rotas.LOGIN.login,
        loadChildren: () => import('./features/login/login.routes').then((routes) => routes.default)
    },
    {
        path: '',
        redirectTo: Rotas.CLIENTES.listagem,
        pathMatch: 'full',
    },
];
```

### Diretrizes de Pré-carregamento de dados<a name="resolvers"></a>
**DEVE:**
- Utilizar Resolvers para **pré-carregar dados essenciais** para a renderização de um componente antes que a navegação para a rota seja concluída.

**NÃO DEVE:** 
- Colocar lógica de negócio complexa ou chamadas excessivamente demoradas em um Resolver, pois isso pode bloquear a navegação e dar a impressão de que a aplicação está travada.

**Exemplo de Uso:**
- Criar a função Resolver:
```ts 
export const produtoDetalheResolver: ResolveFn<ProdutoDto | null> = (route, state) => {
  const produtoService = inject(ProdutoService);
  const produtoId = route.paramMap.get('id');

  if (!produtoId) {
    return of(null);
  }

  return produtoService.getById(+produtoId).pipe(
    catchError(error => {
      console.error('Falha ao buscar produto no resolver:', error);
      // Redirecionar para uma página de erro
      return of(null); 
    })
  );
};
```
- Aplicar o resolver na rota: 
```ts
export const PRODUTOS_ROUTES: Routes = [
  {
    path: ':id',
    component: ProdutoDetalheComponent,
    resolve: {
      // A chave 'produto' será usada para acessar o dado resolvido
      produto: produtoDetalheResolver 
    }
  }
];
```
- Acessar o dado no componente: 
```ts
export class ProdutoDetalheComponent {
  private _route = inject(ActivatedRoute);

  // Acessando o dado resolvido de forma reativa
  protected produto$: Observable<ProdutoDto | null> = this._route.data.pipe(
      map(data => data['produto'])
  );
// Acessando o dado resolvido de forma instantânea
  protected produto: <ProdutoDto | null> = this._route.snapshot.data['produto']
}
```
---

## Diretrizes de Reatividade<a name="reatividade"></a>

### RxJS<a name="reatividade"></a>
O **RxJS** (Reactive Extensions for JavaScript) DEVE ser utilizado em cenários de **manipulação de streams de dados assíncronos** ou para a coordenação de eventos baseada em tempo.

**Nomenclatura:** `usuarioLogado$`

####  Gerenciamento de Estado e Subjects<a name="rxjs-gerenciamento-estado"></a>

| Elemento | DEVE | Contexto/Razão |**Exemplo de Uso Prático**|
| :--- | :--- | :--- | :--- |
| **Observable** | Utilizar para expor **streams de dados assíncronos** que são **somente leitura**. | <br>É o contrato fundamental de reatividade e promovem a imutabilidade para os consumidores. | Serviço que fornece uma lista de usuários obtida via `HttpClient.get()`.|
| **BehaviorSubject** | Utilizar para gerenciar estados que **precisam de um valor inicial**. | Essencial para estados globais, pois garante que novos assinantes recebam o estado atual imediatamente. | <br>State de autenticação que guarda o usuário logado.|
| **Subject** | Utilizar para eventos sem a necessidade de um estado inicial. | Ideal para eventos efêmeros (ex: cliques) onde apenas os assinantes ativos no momento da emissão devem receber o valor. | <br>Disparar evento quando um modal deve ser fechado. <br>|

**Exemplos:**
- Observable: 
```ts 
 protected  users$: Observable<Array<User>> = this.http.get<Array<User>>('/api/users')
```
- Subject: 
```ts 
    const closeModal$ = new Subject<void>();

    protected fecharModal(): void {
      this.closeModal$.next();
    }
```
- BehaviorSubject: 
```ts 
    private userSubject = new BehaviorSubject<User | null>(null);

    public login(user: User): void {
      this.userSubject.next(user);
    }
```

#### Encapsulamento e Compartilhamento de Subjects<a name="rxjs-compartilhamento-subjects"></a>

Para garantir o encapsulamento do estado (impedindo que componentes externos emitam valores):

 **DEVE** 
 - Expor `Subject` e `BehaviorSubject` para o exterior **somente** após a conversão em um `Observable`, somente leitura.
    * **Método:** Utilizar o operador **`asObservable()`**.

```ts
  private _isLoading = new BehaviorSubject<boolean>(false);

  public isLoading$: Observable<boolean> = this._isLoading.asObservable();
```

- Considerar a conversão de *Observables* que representam estado persistente para **Signals** utilizando **`toSignal()`**.

```ts
  protected loggedUser = toSignal(this.authService.loggedUser$, { initialValue: null });
```

#### Pureza e Efeitos Colaterais<a name="rxjs-efeitos-coleterais"></a>

**DEVE** 
- Utilizar o operador **`tap()`** exclusivamente para realizar efeitos colaterais (*side-effects*), como logs, debug ou chamadas não reativas, sem modificar o *stream* principal de dados.
```ts
  public login(email: string, password: string): Observable<LoginResponse> {
    return this.http.post<LoginResponse>(this.API, { email, password }).pipe(
      tap({
        next: (response) => {
          localStorage.setItem('auth_token', response.token);
          console.log('Token armazenado com sucesso!');
        },
        error: (err) => console.error('Falha no login:', err)
      })
    );
  }
```

#### Gerenciamento de Subscriptions (Cancelamento)<a name="rxjs-gerenciamento-subscriptions"></a>

- **SEMPRE** cancelar a inscrição (unsubscribe) de **Observables** que não completam naturalmente, a fim de prevenir **vazamentos de memória**.

| Local de Uso | Método Preferencial | Detalhe |
| :--- | :--- | :--- |
| **Template (HTML)** | **AsyncPipe ($\text{Pipe Assíncrono}$)** | **MÉTODO PREFERENCIAL.** Lida automaticamente com a inscrição e o cancelamento no ciclo de vida do componente.
| **Componentes/Serviços** | **`toSignal()`** | Cria um Signal reativo a partir de um Observable. <br>A **subscription é limpa automaticamente** quando o componente é destruído.<br>| |
| **Componentes/Serviços** | Operadores do RxJS: **`takeUntil(destroy$)`** | Ideal para cancelar a inscrição na destruição do componente. <br>Permite cancelar várias inscrições ao mesmo tempo <br>|
| **Componentes/Serviços** | Operadores do RxJS: **`take(N)`** | Ideal para chamadas HTTP ou outras operações que completam após um número (N) de emissões. |
**Componentes/Serviços** | Operadores do RxJS: **`takeUntilDestroyed(destroyRef)`** | **Método moderno para Angular >=16.** Cancela automaticamente a inscrição quando o componente ou diretiva é destruído, sem necessidade de criar um `Subject` manualmente.

**Exemplos:**
* **AsyncPipe:**
    ```ts
    //ts
    public loading$: Observable<boolean> = this._loadingService.loading$;
    //html
    @if(loading$ | async){
        <p-progress-spinner ariaLabel="loading" />
    }
    ```
* **toSignal():**
    ```ts
    //ts
    protected weather = toSignal(this.weatherService.getWeather(), { initialValue: 'Carregando...' });
    //html 
    {{ weather() }}
    ```
* **takeUntil(destroy$):**
    ```ts
    export class TimerComponent implements OnDestroy {
      private _destroy$ = new Subject<void>();
      private _counter = 0;

      constructor() {
        interval(1000)
          .pipe(
            tap(() => console.log('tick')),
            takeUntil(this._destroy$)
          )
          .subscribe((n) => (this._counter = n));
      }

      ngOnDestroy(): void {
        this.destroy$.next();
        this.destroy$.complete();
      }
    }
    ```
* **take(N):**

    ```ts
    public buscarUsuarios(): Observable<Array<User>> {
        return this.http.get<User[]>('/api/users').pipe(take(1))
    }
    ```
* **takeUntilDestroyed(destroyRef):**

    ```ts
    import { destroyRef, inject } from '@angular/core'; 
    import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; 

    const destroyRef = inject(DestroyRef);         
    observable$.pipe(takeUntilDestroyed(destroyRef)).subscribe(value => console.log(value)); 
    ```

#### Combinação de Múltiplos Observables<a name="rxjs-combinacao-observables"></a>
**DEVE:**
- Utilizar operadores de combinação como `forkJoin`, `combineLatest` ou `zip` para orquestrar a execução de múltiplos Observables de forma declarativa.

**NÃO DEVE:**
- Aninhar subscribes (callback hell).

| Operador | Caso de Uso Principal | Comportamento da Emissão |
| :--- | :--- | :--- |
| **forkJoin** | Executar **requisições HTTP paralelas e independentes** onde você precisa aguardar a conclusão de **todas** elas para prosseguir. | Emite um único valor (um array com o último valor de cada stream) somente **quando todos** os Observables de entrada completam. Se algum falhar, o `forkJoin` falha imediatamente.
| **combineLatest** | Cenários de reatividade onde você precisa de um valor combinado sempre que **qualquer um** dos Observables de entrada emitir um novo valor (após todos terem emitido pelo menos uma vez). | Emite um valor combinado (array) sempre que um dos Observables emite.| |
| **zip** | Quando é necessário combinar emissões de múltiplos Observables em pares, na ordem em que foram emitidas. | Emite um valor combinado (array) apenas quando cada Observable de entrada emitiu um valor no mesmo índice.|

**Exemplos:** 
- **forkJoin:**
    ```ts
    export class DashboardComponent implements OnInit {
      private usuarioService = inject(UsuarioService);
      private permissoesService = inject(PermissoesService);
      private produtosService = inject(ProdutosService);

      protected dadosTela = toSignal(
        this.carregarDadosIniciais(), 
        { initialValue: { usuario: null, permissoes: [], produtos: [] } }
      );

      private carregarDadosIniciais(): Observable<{ usuario: any; permissoes: any[]; produtos: any[] }> {
        return forkJoin({
          usuario: this.usuarioService.getUsuarioLogado(),
          permissoes: this.permissoesService.getPermissoes(),
          produtos: this.produtosService.getProdutosDestaque()
        }).pipe(
          map(({ usuario, permissoes, produtos }) => {
            // A lógica só executa quando todas as 3 chamadas retornarem com sucesso
            console.log('Todos os dados foram carregados com sucesso!');
            return { usuario, permissoes, produtos };
          }),
          catchError(error => {
            console.error('Ocorreu um erro ao carregar dados iniciais:', error);
            return of({ usuario: null, permissoes: [], produtos: [] });
          })
        );
      }
    }
    ```
    
- **combineLatest:**
    ```ts
    const precos = {
      'Notebook': 5000,
      'Mouse': 150,
      'Teclado': 300
    };

    const taxasCambio = {
      'BRL': 1,
      'USD': 0.18,
      'EUR': 0.17 
    };

    const produtoSelecionado$ = new BehaviorSubject<string>('Notebook');

    const moedaSelecionada$ = new BehaviorSubject<string>('BRL');

    const precoFinal$ = combineLatest([
      produtoSelecionado$,
      moedaSelecionada$
    ]).pipe(
      map(([produto, moeda]) => {
        const precoBase = precos[produto];
        const taxa = taxasCambio[moeda];
        const precoConvertido = (precoBase * taxa).toFixed(2);

        console.log(`Calculando: Produto=${produto}, Moeda=${moeda}`);
        return `${moeda} ${precoConvertido}`;
      })
    );
    ```

- **zip:**
    ```ts
    const cliques$ = fromEvent(document, 'click');

    const teclasPressionadas$ = fromEvent(document, 'keydown').pipe(
      map((event: KeyboardEvent) => event.key) // Extrai apenas o nome da tecla
    );

    const combinacaoCliqueTecla$ = zip(
      cliques$,
      teclasPressionadas$
    );

    combinacaoCliqueTecla$.subscribe(([eventoClique, nomeTecla]) => {
      console.log(`Clique registrado enquanto a tecla "${nomeTecla}" havia sido a última pressionada.`);
    });
    ```
___

### Signals<a name="signals"></a>
- Os **Signals** são primitivas de reatividade simples e síncronas que garantem a propagação de mudanças de maneira eficiente e rastreável.
 **DEVE:**
    -  Ser a **opção preferencial** para estados reativos **locais** de um componente e para **leituras síncronas** no *template*.
    - Utilizar a função `signal()` para gerenciar o estado mutável principal.
    - Utilizar **`computed()`** para derivar valores de outros *Signals* de forma **declarativa e memorizada** (só recalcula quando as dependências mudam).
    - Utilizar a função **`effect()`** exclusivamente para lidar com **efeitos colaterais** (side-effects) que não podem ser expressos no fluxo de dados.
    
**Exemplos:**
```ts
//Singal
protected count: Signal<number> = signal(0);

protected increment(): void {
    this.count.update((value) => value + 1);
}

protected reset(): void {
    this.count.set(0);
}

// Computed
protected unitPrice = signal(50);
protected quantity = signal(1);

//  Valor derivado (recalcula apenas quando quantity ou unitPrice mudam)
protected total = computed(() => this.unitPrice() * this.quantity());

public increase(): void {
    this.quantity.update((q) => q + 1);
}

public decrease(): void {
    this.quantity.update((q) => Math.max(1, q - 1));
}

//effect
protected theme = signal(localStorage.getItem('theme') || 'light');

constructor() {
// Side-effect: salvar no localStorage quando o theme muda
    effect(() => {
      const current = this.theme();
      localStorage.setItem('theme', current);
      console.log(`Tema atualizado para: ${current}`);
    });
}

toggleTheme() {
    this.theme.update((t) => (t === 'light' ? 'dark' : 'light'));
}
```
    
___

### ReactiveForms (FormBuilder, FormGroup e Validators)<a name="reactive-forms"></a>
Os Formulários Reativos (Reactive Forms) **DEVEM** ser a escolha padrão para a gestão de dados complexos, validações e reatividade em formulários.

#### Estrutura e Tipagem<a name="design-system"></a>
* **DEVE:**
    -  Utilizar o **`FormBuilder`** (ou `NonNullableFormBuilder`) para construir **`FormGroup`**, **`FormControl`** e **`FormArray`**, garantindo concisão e legibilidade.
    ```ts
    export class UsuarioFormComponent {
      formUsuario!: FormGroup;
      private _fb = inject(NonNullableFormBuilder);

      constructor() {
        this.buildForm();
      }

      private buildForm(): void {
        this.formUsuario = this._fb.group<IFormUsuario>({
          nome: this._fb.control('', { validators: [Validators.required] }),
          email: this._fb.control('', { validators: [Validators.required, Validators.pattern('[0-9]{10}')]}),
          age: this._fb.control(0, { validators: [Validators.required, Validators.min(20),Validators.max(50)]}),
        });
      }
        
      public salvar(): void {
          const formClienteValue: IFormUser = this.formCliente.getRawValue();
      }
    }
    ```
    - Tipar o `FormGroup` utilizando uma **Interface** ou **Type** que reflita a estrutura do formulário. Isso garante segurança de tipo ao acessar e manipular os valores, com a criação do type ou interface seguindo o padrão de nomeclatura abaixo:
     **Nomenclatura:** `IFormUsuario` ou `FormUsuario`
    ```ts
    interface IFormUsuario {
        nome: string;
        email: string;
        age: number;
    }
    ```
    - A lógica de construção do `FormGroup` deve ser encapsulada em um **método privado** do componente (ex: `this.buildForm()`) ou isolada em um **serviço dedicado** para formulários complexos.
    - Utilizar **`FormArray`** para a gestão de listas dinâmicas e repetíveis de controles ou grupos de controles.

#### Validação e Feedback ao Usuário<a name="design-system"></a>
**DEVE:** 
- Criar **Validadores customizados** quando a lógica de validação de um campo não puder ser atendida pelos validadores padrão disponibilizados pelo framework.
```ts
import { AbstractControl, ValidationErrors } from "@angular/forms";

export const dataNascimento = (controle: AbstractControl): ValidationErrors | null => {
  let dataNascimentoFutura = 'A data de nascimento não pode ser futura.';
  let dataNascimentoAnteriorA1900 = 'Não deve ser anterior a 1900.';

  const dataNascimento = controle.value;
  const erros: ValidationErrors = {};

  if (!dataNascimento) {
    return null;
  }

  const hoje = new Date();

  if (dataNascimento > hoje) {
    erros['dataInvalidaFuturo'] = dataNascimentoFutura;
  }

  const ano1900 = new Date(1900, 0, 1);
  if (dataNascimento < ano1900) {
    erros['dataNascimentoAnteriorA1900'] = dataNascimentoAnteriorA1900;
  }

  return Object.keys(erros).length ? erros : null;
}
```
- Utilizar **Validadores Assíncronos** (Async Validators) para verificações que dependem de operações assíncronas (ex: checagem de nome de usuário via API).
```ts
import { inject } from '@angular/core';
import { AbstractControl, AsyncValidatorFn, ValidationErrors } from "@angular/forms";
import { Observable, of } from "rxjs";
import { debounceTime, switchMap, map, catchError, first } from "rxjs/operators";
import { CpfService } from "../services/cpf.service";

export const cpfJaExisteValidator: AsyncValidatorFn = (
  controle: AbstractControl
): Observable<ValidationErrors | null> => {

  const cpfService = inject(CpfService);

  if (!controle.value || controle.invalid) {
    return of(null);
  }

  return controle.valueChanges.pipe(
    debounceTime(500),
    first(),
    switchMap(cpf =>
      cpfService.verificarCpf(cpf).pipe(
        map(response => {
          return response.emUso
            ? { cpfJaExiste: 'Este CPF já está cadastrado.' }
            : null;
        }),
        catchError(() => of(null))
      )
    )
  );
};
```

#### Reatividade<a name="design-system"></a>
 **DEVE:**
- Utilizar **`valueChanges`** ou **`statusChanges`** em um `FormControl` para implementar lógica reativa complexa (ex: habilitar ou desabilitar campos, ou alterar a lista de validadores, com base no valor de outro campo).
 > 💡 **Importante:**  
> Sempre utilize uma estratégia para **encerrar a inscrição (`unsubscribe`)** das observables de `valueChanges` e `statusChanges`, pois essas streams não se completam automaticamente. Recomenda-se o uso de **`DestroyRef`** com o operador **`takeUntilDestroyed()`** — que realiza o unsubscribe automaticamente quando o componente é destruído, evitando **vazamentos de memória**.

```ts
export class UsuarioFormComponent {
    private _fb = inject(FormBuilder);
    protect formUsuario!: FormGroup;
    private _destroyRef = inject(DestroyRef);
    
      constructor() {
        this.buildForm();
      }

      private buildForm(): void {
        this.formUsuario = this._fb.group({
          nome: this._fb.control('', { validators: [Validators.required] }),
          doc: this._fb.control(CPF,{ validators: [Validators.required] } )
          email: this._fb.control('', { validators: [Validators.required, Validators.pattern('[0-9]{10}')]}),
          age: this._fb.control(0, { validators: [Validators.required, Validators.min(20),Validators.max(50)]}),
        });
      }
    }
    
    private criarEventosFormulario(): void {
        this.form.controls.tipo.valueChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((docType) => {
          const doc = this.form.controls.doc;

          if (tipo === 'CPF') {
            doc.addValidators(cpfValidator);
          } else {
            doc.clearValidators();
          }

          doc.updateValueAndValidity();
        });
    }
}
```
---

## Diretrizes de Componentização<a name="componentizacao"></a>

-  Container/Presenter/Component Pattern

### CONTAINER (Smart Component)<a name="componentizacao-smart-component"></a>

**Nomenclatura:** `cadastro-inscricao-divida.container.ts`

**O que DEVE ter:**
- Injeção de Services (DividaService, DevedorService, etc)
- Lógica de negócio e validações complexas
- Chamadas HTTP (via services)
- Gerenciamento de estado (RxJS, NgRx, Signals)
- Navegação entre rotas
- Manipulação de formulários complexos

**O que NÃO deve ter:**
- Estilização complexa (delegar para presenters)
- Lógica de apresentação pura
- Conhecimento de estrutura HTML detalhada

**Exemplo:**
```ts
export class DividasContainer implements OnInit {
  private dividaService = inject(DividaService);
  private devedorService = inject(DevedorService);
  private router = inject(Router);

  private refresh$ = new BehaviorSubject<void>(undefined);
  private isLoading$ = new BehaviorSubject<boolean>(false);
  
  dividas$: Observable<DividaDto[]>;
  devedores$: Observable<DevedorDto[]>;

  ngOnInit(): void {
    this.devedores$ = this.devedorService.getTodos();

    this.dividas$ = this.refresh$.pipe(
      tap(() => this.isLoading$.next(true)),
      switchMap(() => this.dividaService.getTodas()),
      tap(() => this.isLoading$.next(false))
    );
  }

  onSalvar(form: FormGroup): void {
    this.isLoading$.next(true);
    this.dividaService.criar(form.value).subscribe({
      next: () => {
        alert('Dívida salva com sucesso!');
        form.reset();
        this.refresh$.next();
      },
      error: (err) => {
        alert('Erro ao salvar dívida.');
        console.error(err);
        this.isLoading$.next(false);
      }
    });
  }

  onEditar(divida: DividaDto): void {
    this.router.navigate(['/dividas/editar', divida.id]);
  }

  onExcluir(id: number): void {
    if (confirm('Tem certeza que deseja excluir esta dívida?')) {
      this.isLoading$.next(true);
      this.dividaService.excluir(id).subscribe(() => {
        alert('Dívida excluída com sucesso!');
        this.refresh$.next();
      });
    }
  }
}
```

### PRESENTER (Dumb Component - Feature Específica)<a name="componentizacao-dumb-component"></a>

**Nomenclatura:** `cadastro-inscricao-divida-list.presenter.ts`

**O que DEVE ter:**
- Decoradores @Input() para receber dados
- Decoradores @Output() para emitir eventos
- Lógica de apresentação simples (formatação, condicionais visuais)
- Validações de UI (não de negócio)
- Responsividade e acessibilidade

**O que NÃO deve ter:**
- Injeção de Services de negócio
- Chamadas HTTP diretas
- Lógica de negócio complexa
- Navegação entre rotas
- Manipulação de estado global

**Exemplo:**
```ts
export class GerenciarDividasPresenter implements OnInit {
  @Input() dividas: DividaDto[] | null = [];
  @Input() devedores: DevedorDto[] | null = [];
  @Input() isLoading = false;
  
  @Output() salvarDivida = new EventEmitter<FormGroup>();
  @Output() editarDivida = new EventEmitter<DividaDto>();
  @Output() excluirDivida = new EventEmitter<number>();
    
  private fb = inject(FormBuilder)

  formDivida: FormGroup;
  colunasTabela: ColunaTabela[] = [
    { chave: 'id', rotulo: '#' },
    { chave: 'nomeDevedor', rotulo: 'Devedor' },
    { chave: 'valor', rotulo: 'Valor (R$)' }
  ];

  ngOnInit(): void {
    this.formDivida = this.fb.group({
      devedorId: [null, [Validators.required]],
      valor: [null, [Validators.required, Validators.min(0.01)]],
      descricao: ['']
    });
  }
    ...
}
```

```html
<form [formGroup]="formDivida" (ngSubmit)="onSubmit()">
  <h2>Cadastrar Nova Dívida</h2>
  
  <select formControlName="devedorId">
    <option [ngValue]="null" disabled>Selecione o devedor</option>
    <option *ngFor="let devedor of devedores" [value]="devedor.id">
      {{ devedor.nome }}
    </option>
  </select>
  
  <input type="number" formControlName="valor" placeholder="Valor da Dívida">

  <button type="submit" [disabled]="isLoading">
    {{ isLoading ? 'Salvando...' : 'Salvar Dívida' }}
  </button>
</form>

<hr>

<h2>Dívidas Cadastradas</h2>
<div *ngIf="isLoading" class="spinner">Carregando...</div>
```

### SHARED COMPONENT (Dumb Component - Reutilizável)<a name="componentizacao-shared-component"></a>

**Nomenclatura:** `tabela-dados.component.ts`

Componentes genéricos que podem ser usados em múltiplas features do projeto.

**O que DEVE ter:**
- Decoradores @Input() para receber dados
- Decoradores @Output() para emitir eventos
- Altamente configurável e genérico
- NÃO conhece DTOs ou domínio de negócio
- Lógica de apresentação agnóstica de contexto
- Validações de UI genéricas
- Responsividade e acessibilidade
- Documentação clara de uso (JSDoc/Storybook)

**O que NÃO deve ter:**
- Injeção de Services de negócio
- Chamadas HTTP diretas
- Lógica de negócio complexa
- Navegação entre rotas
- Manipulação de estado global
- Conhecimento de DTOs específicos (use tipos genéricos)
- Dependência de features específicas

```ts
export class BotaoPrimarioComponent {
  @Input() texto: string = 'Clique-me';

  @Input() tipo: 'button' | 'submit' | 'reset' = 'button';

  @Input() carregando: boolean = false;

  @Input() desabilitado: boolean = false;

  @Output() clique = new EventEmitter<void>();

  onClick(): void {
    if (!this.carregando && !this.desabilitado) {
      this.clique.emit();
    }
  }
}
```

```html
<button
  [type]="tipo"
  [disabled]="carregando || desabilitado"
  (click)="onClick()"
  class="botao-primario">

  <span *ngIf="carregando" class="spinner"></span>

  <span *ngIf="!carregando">{{ texto }}</span>
</button>
```

---

## Diretrizes de Comunicação entre Componentes<a name="comunicacao-components"></a> 

A comunicação entre componentes **DEVE** ser clara, reativa e seguir o princípio do **fluxo de dados unidirecional** sempre que possível, garantindo baixo acoplamento e alta manutenibilidade.

### Comunicação Vertical (Pai $\leftrightarrow$ Filho)<a name="comunicacao-components-vertical"></a> 


| Fluxo | Mecanismo DEVE | Detalhes e Boas Práticas |
| :--- | :--- | :--- |
| **Pai → Filho** | **Inputs** (`@Input()` ou `input()`) | **PREFERIR** a função `input()` (baseada em **Signals**), que é o padrão moderno do Angular. Utilizar para passar **dados simples** ou **configurações** do componente pai para o filho. |
| **Filho → Pai** | **Outputs** (`@Output()` $\rightarrow$ `EventEmitter`) | O `Output` DEVE ser utilizado para **emitir eventos** quando uma ação ocorre no filho. Emitir **dados tipados e brutos**, evitando lógica de negócios complexa. |

**Exemplo:**
*  Pai → Filho: 
    ```ts
    //TS componente pai
    export class ListagemClienteComponent {
        public clientes: WritableSignal<Array<ClienteListagemDto>> = signal([]);
        ...
    }
    //html componente pai
    //Propriedade de input
    <tabela-cliente [clientes]="clientes()" />
        
        
    // TS componente filho 
    export class TabelaClienteComponent {
        public clientes = input<Array<ClienteListagemDto>>();
        ...
    }
    ```
*  Filho → Pai: 
    ```ts
    export class FiltrosClienteComponent{
        public onPesquisar = output<IFormCliente>();
        
        protected form!: FormGroup<IFormCliente>;
        
        public pesquisar(): void {
            const formValue = this.form.getRawValue(); 
            this.onPesquisar.emit(formValue);
        })
      }
      ...
    }
    //html componente pai  
    <filtros-cliente (onPesquisar)="pesquisar($event)" />
        
    //TS componente pai
        ...
        public pesquisar(fitros: IFormCliente): void {
            this._apiService.pesquisar(fitros).pipe(take(1)).subscribe((response ) => {...});
        }
        ...
    ```

### Comunicação Horizontal (Mesmo Nível e Global)<a name="comunicacao-components-horizontal"></a> 

* **Serviço de Estado:** **DEVE** ser utilizado um **Serviço de Estado** injetável (`StateService`) quando os componentes não têm uma relação direta de Pai/Filho.
* **Reatividade:** O `StateService` DEVE implementar a reatividade utilizando:
    * **BehaviorSubject** (RxJS) para armazenar o estado global de forma reativa.
    * **Signals** para expor e gerenciar o estado reativo compartilhado.
* **Serviços de API:** **DEVE**-se usar serviços injetáveis para toda comunicação com APIs externas, isolando o componente da lógica de *fetch* e manipulação de dados brutos.

```ts 
export class UsuarioLogadoService {
  private _usuarioLogado: BehaviorSubject<UsuarioLogadoDto> =
    new BehaviorSubject<UsuarioLogadoDto>(new UsuarioLogadoDto({}));

 public adicionarUsuarioLogado(usuarioLogado: UsuarioLogadoDto): void {
    this._usuarioPreLogado.next(usuarioLogado);
  }  
  public get usuarioLogado$(): Observable<UsuarioLogadoDto> {
    return this._usuarioLogado.asObservable();
  }
}
``` 

### Práticas a Evitar (Antipatterns)<a name="comunicacao-components-evitar"></a>

#### Excesso de camadas de interação
* Evite criar longas cadeias de comunicação vertical (@Input e @Output) através de múltiplos níveis hierárquicos.
* Quando houver mais de um nível de distância entre o componente que origina a ação e o que a consome, DEVE ser utilizado um Serviço de Estado (State Service) para desacoplá-los.

**Exemplo**: 
`pagina-perfil (Avô)` → `detalhes-usuario` (Pai) → `card-endereco-usuario` (Filho)
```html
<app-avô (evento)="aoOcorrer($event)">
  <app-pai (evento)="aoOcorrer($event)">
    <app-filho (evento)="aoOcorrer($event)">
      ...
    </app-filho>
  </app-pai>
</app-avô>
```

* O componente `card-endereco-usuario` possui o método excluir endereço, que cria um evento repassado para `detalhes-usuario` e depois para`pagina-perfil (Avô)`

#### Emissão de Objetos Complexos sem Tipagem
* Emitir eventos com objetos genéricos (any) ou estruturas complexas não tipadas, causa perda de segurança de tipo e quebra fácil após refatorações.
```ts
@Output() usuarioAlterado = new EventEmitter<any>();
this.usuarioAlterado.emit({ nome: 'Teste', idade: 30 });
```

#### Acesso Direto (Alto Acoplamento)<a name="comunicacao-components-evitar-acesso-direto"></a>

* **Evitar o Uso de `ViewChild` e `ViewChildren`:**
    * Acessar elementos ou instâncias de componentes filhos diretamente com **`ViewChild`** **QUEBRA** o encapsulamento e cria um **acoplamento forte** entre os componentes.
```ts
@ViewChild(FiltroComponent) filtro!: FiltroComponent;

aplicarFiltro() {
  this.filtro.atualizarLista(); // chamada direta de método interno
}
```

#### Anti-Performance e Impureza<a name="comunicacao-components-evitar-acesso-impureza"></a>

* **Evitar o Acesso a Métodos de Serviço no Template:**
    * Chamar métodos de serviço diretamente no template (ex: `{{ service.getDados() }}`) pode causar **processamento desnecessários** ou chamadas de API repetidas durante o ciclo de detecção de mudanças.
    * A lógica DEVE ser movida para o componente (`.ts`), e os dados devem ser expostos como **Observable (AsyncPipe)** ou **Signal**.

---

## Diretrizes de Estilização<a name="estilizacao"></a>

### Tema PrimeNG<a name="estilizacao-tema"></a>
- Adotar uma arquitetura modular de design tokens e temas, centralizando cores, tipografia, espaçamentos e componentes personalizados.
- Preferir a personalização é feita a partir dos design tokens do PrimeNG.

#### Estrutura<a name="estilizacao-tema-estrutura"></a>
```
src/
└── styles/
    ├── theme.scss                # Tema global: gera variáveis CSS e aplica fontes, espaçamentos e cores.
    ├── components/
    │   ├── index.scss            # Ponto de entrada para os estilos de componentes. (Expõe o CSS para o thema)
    │   ├── buttons/              # Estilos específicos de botões.
    │   ├── form/                 # Campos de formulário.
    │   ├── tooltips/             # Tooltips customizados.
    │   └── message/              # Estilos de mensagens de validação ou feedback.
    └── variables/
        ├── _pallete.scss         # Tokens de cor.
        ├── _spacing.scss         # Tokens de espaçamento e borda.
        ├── _typography.scss      # Tokens e mixins de tipografia.
```

#### _palette.scss <a name="estilizacao-tema-palette"></a>
- Define o sistema de cores da aplicação.
- Cada cor (como $teal, $red, $gray) é um mapa de 10 tonalidades (50–950).
**Exemplo**: 
```scss
$teal: (
  50: #f1fdfa,
  100: #d1faf1,
  200: #a4f5e4,
  300: #73e8d3,
  400: #50d2be,
  500: #3eb6a5,
  600: #309387,
  700: #27756e,
  800: #215d59,
  900: #1e4d4a,
  950: #0c2f2e,
);
```

- As cores principais do sistema são expostas utilizando variáveis definidas nesse arquivo: 
```scss
$primary: $teal;
$success: $green;
$info: $sky;
$warning: $amber;
$danger: $red;

$gray: $slate;
$black: #000;
$white: #fff;
``` 

- Prefira usar tokens do PrimeNG como `--p-primary-color`, `--p-inputtext-border-color`, etc.
- Sempre extraia valores com map.get(), por exemplo:
**Exemplo**
```scss
@use "sass:map";

@use "../../variables/pallete" as pallete;

--p-button-primary-background: #{map.get(pallete.$primary, 50)};
```

#### _spacing.scss <a name="estilizacao-tema-spacing"></a>
- Centraliza unidades de espaçamento, bordas e sombras.
```scss
$spacing-unit: 8px;

$spacing: (
  xxs: $spacing-unit * 0.25,
  xs: $spacing-unit * 0.5,
  sm: $spacing-unit * 1,
  md: $spacing-unit * 1.5,
  lg: $spacing-unit * 2,
  xl: $spacing-unit * 3,
  xxl: $spacing-unit * 4,
  xxxl: $spacing-unit * 6,
);
```

**Exemplo:**
```scss
@use "sass:map";

@use "../../variables/spacing" as spacing;

--p-textarea-padding-x: #{map.get(spacing.$spacing, md)};
--p-textarea-padding-y: #{map.get(spacing.$spacing, lg)};
--p-textarea-border-radius: #{spacing.$border-radius};
```

#### _typography.scss <a name="estilizacao-tema-typography"></a>
- Controla a escala tipográfica e hierarquia de texto.
- Inclui 
    - Mapas `$typography` e `$typography-scale`;
    - Mixins: `@mixin apply-font($level, $variant)` e `@mixin generate-typography-vars($map)` 

```scss
@mixin generate-typography-vars($map) {
  @each $level, $weights in $map {
    @each $variant, $props in $weights {
      $size: map.get($props, size);
      $line-height: map.get($props, line-height);
      $weight: map.get($props, weight);

      --font-#{$level}-#{$variant}-size: #{$size};
      --font-#{$level}-#{$variant}-line-height: #{$line-height};
      --font-#{$level}-#{$variant}-weight: #{$weight};
    }
  }
}

@mixin apply-font($level, $variant) {
  font-family: $font-family-base;
  font-size: var(--font-#{$level}-#{$variant}-size);
  line-height: var(--font-#{$level}-#{$variant}-line-height);
  font-weight: var(--font-#{$level}-#{$variant}-weight);
}
```

**Exemplos**: 
```scss
@use "../../variables/typography" as typo;

@include typo.apply-font(2, regular);

line-height: #{typo.$field-line-height};
min-height: #{typo.$field-min-height};
```

- Níveis de 1 a 8 correspondem a tamanhos crescentes de texto.
Use regular, medium ou bold para variar o peso.

#### thema.scss <a name="estilizacao-tema-theme"></a>
- É o núcleo de integração com o PrimeNG.
- Importa as variáveis de cor, espaçamento e tipografia.
- - Gera variáveis CSS --p-* que o PrimeNG reconhece como design tokens.
- Define estilos base (`body`,`h1–h6`,`p` ,`small`).

**Exemplo:**
```scss
@use "sass:map";

@use "./variables/pallete" as colors;
@use "./variables/spacing" as spacing;
@use "./variables/typography" as typography;

@use "components/index" as components;

:root {
  --p-primary-color: #{map.get(colors.$primary, 500)};
  --p-text-color: #{colors.$text-color};
  @include typography.generate-typography-vars(typography.$typography-scale);
}

body {
  background-color: colors.$surface-ground;
  color: colors.$text-color;
  @include typography.apply-font(2, regular);
}
```

#### Diretório Components <a name="estilizacao-tema-components"></a>
- Organiza estilos específicos de cada componente customizado (ex: app-textfield, app-dropdown, app-message).
**Exemplo:**
```scss!
app-textfield {
  input[pInputText] {
    @include typo.apply-font(2, regular);
    --p-inputtext-border-color: #{map.get(pallete.$gray, 300)};
    --p-inputtext-focus-border-color: #{map.get(pallete.$primary, 500)};
  }
}
```

#### index.scss <a name="estilizacao-tema-index"></a>
- Funciona como ponto de agregação para todos os estilos de componentes:
```scss!
@use "./buttons/button.scss";
@use "./form/text-field.scss";
@use "tooltips/tooltip.scss";
@use "message/message.scss";
```

- Esse arquivo é importado diretamente no theme.scss, o que garante que todos os componentes customizados herdem os tokens e estilos globais.



### Tailwind CSS<a name="estilizacao-tailwind"></a>
- Utilizar classes do Tailwind para CSS utilitários e responsividade.
- Criar tokens CSS (design-system) para cores, espaçamentos, tipografia e mapear para Tailwind config (custom properties).

---

## Diretrizes de Comunicação com Back-End<a name="comunicacao-back"></a>

### API Layer (**.api.ts)<a name="comunicacao-back-api-layer"></a>
**Responsabilidade:** Executar chamadas HTTP puras, sem qualquer lógica de negócio.

**✅ O que DEVE ter:**
- Chamadas HTTP diretas (httpClient.get/post/put/delete)
- Configuração de URLs e endpoints
- Configuração de headers HTTP
- Tipagem com DTOs de Request/Response
- Link para documentação Swagger do backend

**❌ O que NÃO deve ter:**
- Tratamento de exceções (delegado ao Facade)
- Transformação de dados (delegado ao Facade)
- Lógica de negócio
- Retry/fallback
- Cache

**Exemplo**: 
```ts
@Injectable({
  providedIn: 'root',
})
export class ProdutoApi {
  private readonly baseUrl = environment.url;
  private http = inject(HttpClient);

  public getById(id: number): Observable<ProdutoApiResponse> {
    return this.http.get<ProdutoApiResponse>(`${this.baseUrl}/${id}`);
  }

  public create(request: CriarProdutoApiRequest): Observable<void> {
    return this.http.post<void>(this.baseUrl, request);
  }
}
```

### API Facade Layer (**.api.facade.ts)<a name="comunicacao-back-api-facade-layer"></a>

**Responsabilidade:** Orquestrar chamadas à API, tratar exceções e transformar dados entre DTOs externos e internos.

**✅ O que DEVE ter:**
- Injeção do serviço API correspondente
- Tratamento de exceções HTTP
- Transformação de DTOs de Request (Internal → Request)
- Transformação de DTOs de Response (Response → Internal)
- Lógica de retry/fallback (quando necessário)
- Cache local (quando necessário)
- Mensagens de erro amigáveis

**❌ O que NÃO deve ter:**
- Chamadas HTTP diretas (delegado ao API)
- Lógica de negócio complexa (delegado a Services)
- Acesso direto a componentes

**Exemplo**: 
```ts
@Injectable({
  providedIn: 'root',
})
export class ProdutoApiFacade {
    private produtoApi = inject(ProdutoApi);
    private notificacaoService = inject(NotificacaoService);

  public getProdutoById(id: number): Observable<ProdutoDto | null> {
    return this.produtoApi.getById(id).pipe(
      map((response: ProdutoApiResponse) => this.transformResponseToDto(response)),
      catchError((error) => {
        console.error('Falha ao buscar produto:', error);
        this.notificacaoService.exibirErro('Não foi possível carregar o produto. Tente novamente.');
        return of(null);
      })
    );
  }

  public criarProduto(produto: ProdutoDto): Observable<boolean> {
    const request = this.transformDtoToRequest(produto);

    return this.produtoApi.create(request).pipe(
      tap(() => this.notificacaoService.exibirSucesso('Produto criado com sucesso!')),
      map(() => true), 
      catchError((error) => {
        console.error('Falha ao criar produto:', error);
        this.notificacaoService.exibirErro('Não foi possível criar o produto.');
        return of(false);
      })
    );
  }

  private transformResponseToDto(res: ProdutoApiResponse): ProdutoDto {
    return new ProdutoDto({
      id: res.id_produto,
      nome: res.nome_produto,
      precoUnitario: res.preco_unitario,
      disponivel: res.disponivel_em_estoque,
      dataCadastro: new Date(res.data_cadastro),
    });
  }

  private transformDtoToRequest(dto: ProdutoDto): CriarProdutoApiRequest {
    return {
      nome_produto: dto.nome,
      preco_unitario: dto.precoUnitario,
      disponivel: dto.disponivel,
    };
  }
}
```

### DTOs de Request (`**.api.request.ts`)<a name="comunicacao-back-dto-request"></a>


**Responsabilidade:** Espelhar exatamente os DTOs de Request do backend Java.

**✅ Regras:**
- **Espelhar exatamente** a estrutura do backend
- **Nomes devem refletir** os nomes do backend
- **Apenas leitura** - não devem ser modificados em componentes
- **Documentação** com link para o DTO Java correspondente

**Exemplo**: 
```ts
/**
 * @see endereço_do_swagger
 */
export interface CriarProdutoApiRequest {
  nome_produto: string;
  preco_unitario: number;
  disponivel: boolean;
}
```

### DTOs de Response (`**.api.response.ts`)<a name="comunicacao-back-dto-response"></a>

**Responsabilidade:** Espelhar exatamente os DTOs de Response do backend Java.

**✅ Regras:**
- **Espelhar exatamente** a estrutura do backend
- **Nomes devem refletir** os nomes do backend
- **Apenas leitura** - não devem ser modificados em componentes
- **Documentação** com link para o DTO Java correspondente

**Exemplo**: 
```ts
/**
 * @see endereço_do_swagger
 */
export interface ProdutoApiResponse {
  id_produto: number;
  nome_produto: string;
  preco_unitario: number;
  disponivel_em_estoque: boolean;
  data_cadastro: string;
}
```

### DTOs Internos (`**.dto.ts`)<a name="comunicacao-back-dto-interno"></a>

**Responsabilidade:** DTOs usados internamente no frontend, podem ter propriedades calculadas e formatos específicos do Angular.

**✅ Características:**
- Podem ter propriedades **derivadas** ou **calculadas**
- Podem ter formatação específica do frontend
- Podem ter métodos auxiliares
- São usados entre componentes, services e facades

**Exemplo**: 
```ts
export class ProdutoDto {
  id: number;
  nome: string;
  precoUnitario: number;
  disponivel: boolean;
  dataCadastro: Date;

  constructor(data: Partial<ProdutoDto>) {
    Object.assign(this, data);
  }

  get statusLabel(): string {
    return this.disponivel ? 'Disponível' : 'Indisponível';
  }
}
```

---

## Diretrizes para o Tratamento de Exceções<a name="tratamento-excecoes"></a>

### Camadas de tratamento de erros: 
| Camada | Mecanismo Principal | Tipo de Erro Capturado | Objetivo Principal 
| :--- | :--- | :--- | :--- |
| **Serviços/Componentes** | **RxJS operador catchError** | Erros **específicos** da lógica, validação, falha de API. | Recuperação, Retries, manter o Contexto.
| **Rede (Global)** | **HttpInterceptor** | HttpErrorResponse  |Lidar com autenticação/autorização, erros transversais, e logging global |
| **Aplicação (Global)** | **CustomErrorHandler** | Erros de Runtime Não Capturados, Erros de Componentes, Erros do Framework | Logging de telemetria, fallback final, alerta de estado crítico |

- O operador catchError do RxJS deve ser o mecanismo primário para interceptar erros em Observables (como aqueles retornados pelo HttpClient) dentro da camada de Serviços.
**Exemplo:**
    ```ts
    public getData(): Observable<Data> {
        return this.http.get<Data>('/api/data').pipe(
            retry(3),
            catchError((error: HttpErrorResponse) => {
                if (error.status === 503) {
                    console.warn('Serviço temporariamente indisponível, tente novamente em alguns minutos', error);
                    return of(); 
                }
            return throwError(() => error);
            })
        );
    }
    ```
- Interceptors para tratamento centralizado, devem transformar erros HTTP em classes padrão de erro. 
    **Deve**: 
    * Capturar erros 401 (Não Autorizado) e 403 (Proibido) para implementar logout automático, purga de tokens e redirecionamento para a página de login.
    * Enviar logs de erro de rede para ferramentas de telemetria, garantindo que todos os erros HTTP sejam registrados.
    * Exibir notificações de erro genéricas para falhas críticas de servidor  que não exigem tratamento específico pelo Serviço.
    ```ts
    export const errorHandlerInterceptor: HttpInterceptorFn = (req, next) => {

      const TOASTER_SERVICE = inject(ToasterService);
      const ROUTER = inject(Router);

      return next(req).pipe(
        catchError((errorResponse: HttpErrorResponse) => {


          if (errorResponse instanceof HttpErrorResponse) {
            TOASTER_SERVICE.exibirMensagemErro({
              titulo: 'TITULO_ERRO',
              mensagem: 'MENSAGEM_ERRO',
            });

            if (errorResponse.status == 401) {
              ROUTER.navigate([Rotas.LOGIN.login], {
                queryParams: { returnUrl: ROUTER.routerState.snapshot.url }
              });
            }
          }
          return throwError(() => errorResponse);
        })
      );
    };
    ```
- O ErrorHandler captura erros lançados em contextos onde o framework está no controle, como durante a detecção de mudanças ou a inicialização de componentes..

    **Exemplo:** 
    ```ts
    @Injectable()
    export class CustomErrorHandler implements ErrorHandler {
      private _environment = environment;
      private _logger: AppLoggerService = inject(AppLoggerService);
      private _notificationService: NotificationService = inject(NotificationService);


      public handleError(error: any): void {
        this._notificationService.notifyCriticalError('Ocorreu um erro inesperado. Tente novamente.');
        this._logger.logError(error);
        if(!this._environment.isProduction){     
            console.error('Erro não tratado capturado pelo CustomErrorHandler:', error);
        }
      }
    }
    ```
**Deve:**
    - Exibir mensagens visuais consistentes.
    - Evitar a quebra da aplicação por exceções não tratadas.
    - Registrar erros relevantes para análise posterior.


___
## Diretrizes para implementação de testes<a name="testes"></a>

### Configuração de Testes com o Array imports<a name="testes-imports"></a>
* A principal alteração na configuração de testes para componentes standalone reside no TestBed.configureTestingModule. Em vez de importar um NgModule que declara o componente, o próprio componente standalone é adicionado diretamente ao array imports. 
```ts
await TestBed.configureTestingModule({
  imports:,
  providers: [
    //... mocks de serviços
  ]
}).compileComponents();
```

### Padrões para Testar componentes: <a name="testes-padroes"></a>

* Diretivas de Atributo: 
    * A abordagem padrão para testar diretivas é criar um componente de teste mínimo(_HostComponent_)
```ts
@Component({
  template: `<div appHighlight>Texto a ser destacado</div>`
})
class TestHostComponent {}

describe('HighlightDirective', () => {
  let fixture: ComponentFixture<TestHostComponent>;
  let divElement: DebugElement;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations:
    });
    fixture = TestBed.createComponent(TestHostComponent);
    divElement = fixture.debugElement.query(By.css('div'));
  });
});

```

### Boas Práticas <a name="testes-boas-praticas"></a>
 **Deve**
 * Utilizar o padrão AAA: Arrange(Organizar), Act (Agir) e Assert (Afirmar) para montar os cenários.
 * Os testes devem validar a funcionalidade esquecendo detalhes de implementação.
 **Exemplo:**
 `it('deve chamar o método privado _recalcularTotal ao adicionar um item',...)` Frágil.
 `it('deve exibir o novo valor total no DOM quando um item é adicionado',...)`
 * Ao testar componentes 'Conteiner' usar overrideComponente para remover a importação do componente filho e usar um mock em seu lugar.
```ts
@Component({
  selector: 'app-filho-real', //-> Mesmo seletor do componente real
  standalone: true,
  template: '<div>Componente Filho Mockado</div>'
})
class FilhoMockComponent {}

// No cenário de teste
    await TestBed.configureTestingModule({
      imports: [PaiComponent], 
    })
    .overrideComponent(PaiComponent, {
      remove: { imports: }, 
      add: { imports: [FilhoMockComponent] } 
    })
    .compileComponents();
```
 
 **Não deve:**
 * Depender de estados deixados por testes anteriores.
 * Agrupar vários cenários em um único teste.
 * Precisar de uma quantidade excessiva de mocks. Esse ponto é um alerta que a classe ou componente possui muitas responsabilidades.
 * Ser instáveis, deve manter o resultado de forma consistentente.
 

#### Organização e Nomeclatura <a name="testes-organizacao-nomeclatura"></a>
* Por convenção do Angular CLI, os arquivos de teste devem ser nomeados com o sufixo .spec.ts e colocados ao lado do arquivo que estão testando `usuario-listagem.component.ts` -> `usuario-listagem.component.spec.ts`
* As descrições nos blocos describe e it devem ser claras e formar uma frase legível que documente o comportamento esperado.
```ts
 describe('UnidadeSobTeste', () => { it('deve [fazer algo] quando [em uma certa condição]', () => {... }); });.
``` 
___
## Melhores Práticas de Implementação<a name="melhores-praticas-de-implementacao"></a>
**Deve**: 
- Usar apenas os atributos html/template e css necessários para evitar acumulo de "lixo" no código.
- Resolver todos os code smells apontados pelo Sonarqube.
- Tentar resolver todos os warnings de qualidade de código relevantes apontados pela IDE.
- Manter tipagem concisa e reutilizavel.
- Usar `trackBy` em @For para performance.
- Configurar tsconfig.json da forma que melhor enforce as boas praticas de programação com Typescript.
- Configurar angular.json da forma que melhor enforce as boas praticas de programação com Typescript.
- Usar `const` em vez de `let` quando o valor não será reatribuído, reforçando intenção de imutabilidade.

**Não Deve**:
- Não Acessar DOM Diretamente. Usar Renderer2 ou HostBinding
- Não Realizar Lógica JavaScript no Template. Mover lógica para o componente ou classe responsável. 
- Inserir HTML ou URLs diretamente no templete sem sanitização.
- Realizar operações custosas dentro do ciclo de vida do Angular (ex.: loops complexos em _ngOnInit_ ou _ngAfterViewInit_).
- Carregar módulos sem _lazy loading_.
- Encadear um subscribe dentro de outro. (Use operadores de encadeamento do RxJs).
- Usar subscribes encadeados dentro de outros.
- Usar`any` sem necessidade.


## Glossário<a name="glossario"></a>

| Tópico/Seção | Convenção de Nomenclatura | Exemplo(s) |
| :--- | :--- | :--- |
| **Reatividade (RxJS)** | Variáveis do tipo `Observable` devem terminar com o sufixo `$` (dólar). | `dividas$`, `isLoading$`, `usuarioLogado$` |
| **Reatividade (ReactiveForms)** | Interfaces ou Types que definem a estrutura de um `FormGroup` devem ser prefixados com `IForm` ou `Form`. | `IFormUsuario`, `FormCliente` |
| **Componentização (Container)** | Arquivos de *Smart Components* (Containers) devem usar o sufixo `.container.ts`. | `cadastro-inscricao-divida.container.ts` |
| **Componentização (Presenter)** | Arquivos de *Dumb Components* específicos de uma feature (Presenters) devem usar o sufixo `.presenter.ts`. | `cadastro-inscricao-divida-list.presenter.ts` |
| **Comunicação com Back-End (API Layer)** | Arquivos que realizam chamadas HTTP puras devem usar o sufixo `.api.ts`. A classe segue o padrão `[NomeDoRecurso]Api`. | `ProdutoApi` em `produto.api.ts` |
| **Comunicação com Back-End (API Facade)** | Arquivos que orquestram a camada de API, tratam erros e transformam dados devem usar o sufixo `.api.facade.ts`. A classe segue o padrão `[NomeDoRecurso]ApiFacade`. | `ProdutoApiFacade` em `produto.api.facade.ts` |
| **Comunicação com Back-End (DTOs de Request)** | Arquivos de DTOs de *Request* que espelham o backend devem usar o sufixo `.api.request.ts`. A interface segue o padrão `[Acao][Recurso]ApiRequest`. | `CriarProdutoApiRequest` em `criar-produto.api.request.ts` |
| **Comunicação com Back-End (DTOs de Response)** | Arquivos de DTOs de *Response* que espelham o backend devem usar o sufixo `.api.response.ts`. A interface segue o padrão `[Recurso]ApiResponse`. | `ProdutoApiResponse` em `produto.api.response.ts` |
| **Comunicação com Back-End (DTOs Internos)** | Arquivos de DTOs para uso interno no frontend devem usar o sufixo `.dto.ts`. A classe segue o padrão `[Recurso]Dto`. | `ProdutoDto` em `produto.dto.ts` |
| **Testes (Arquivos)** | Arquivos de teste devem usar o sufixo `.spec.ts` e estar localizados ao lado do arquivo que estão testando. | `usuario-listagem.container.spec.ts` |
| **Testes (Descrições)** | As descrições nos blocos `describe` e `it` devem ser claras e formar uma frase legível que documente o comportamento esperado. | `describe('UnidadeSobTeste', () => { it('deve [fazer algo] quando [em uma certa condição]', ...); });` |


