←

πŸ…°οΈ Angular Stack

Stacks Frontend

EN PT FR
IA Guidelines / Stacks Frontend / Angular Stack

Stack Angular β€” InfoWhere

Template de stack para projetos Angular
Última atualização: 25/01/2026
Uso: SecundΓ‘rio (projetos de clientes enterprise)


1. VisΓ£o Geral

Stack frontend para projetos de clientes enterprise que preferem/exigem Angular.

Filosofia

  • Bootstrap como UI framework (ou Material se cliente exigir)
  • Componentes pragmΓ‘ticos β€” dividir quando faz sentido, nΓ£o por dogma
  • Signals para estado local e UI simples
  • RxJS sΓ³ para sistemas grandes (real-time, streams complexos)
  • Standalone components como padrΓ£o (sem NgModules)
  • Diretivas quando simplificam o cΓ³digo

2. VersΓ΅es

Componente VersΓ£o Notas
Angular 19.x Standalone components
TypeScript 5.x Vem com Angular
Node 22.x LTS
pnpm Última Gerenciador de pacotes

3. DependΓͺncias Core

3.1 Framework Base

{
  "dependencies": {
    "@angular/animations": "^19.0.0",
    "@angular/common": "^19.0.0",
    "@angular/compiler": "^19.0.0",
    "@angular/core": "^19.0.0",
    "@angular/forms": "^19.0.0",
    "@angular/platform-browser": "^19.0.0",
    "@angular/platform-browser-dynamic": "^19.0.0",
    "@angular/router": "^19.0.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.6.0",
    "zone.js": "~0.15.0"
  }
}

3.2 UI Framework

PadrΓ£o: Bootstrap

{
  "dependencies": {
    "bootstrap": "^5.3.0",
    "@popperjs/core": "^2.11.0"
  }
}

Alternativa (se cliente exigir): Angular Material

{
  "dependencies": {
    "@angular/material": "^19.0.0",
    "@angular/cdk": "^19.0.0"
  }
}

### 3.3 HTTP & State

```json
{
  "dependencies": {
    "@angular/common": "^19.0.0",
    "@ngrx/signals": "^19.0.0"
  }
}

3.4 AutenticaΓ§Γ£o

{
  "dependencies": {
    "keycloak-angular": "^16.0.0",
    "keycloak-js": "^26.0.0"
  }
}

4. Testes

{
  "devDependencies": {
    "@angular/cli": "^19.0.0",
    "@angular-devkit/build-angular": "^19.0.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "jasmine-core": "~5.4.0"
  }
}

Cobertura mΓ­nima: 70%


5. AutenticaΓ§Γ£o

Componente Escolha
Identity Provider Keycloak
Lib keycloak-angular

ConfiguraΓ§Γ£o tΓ­pica

// src/app/config/keycloak.config.ts
import { KeycloakService } from 'keycloak-angular';

export function initializeKeycloak(keycloak: KeycloakService) {
  return () =>
    keycloak.init({
      config: {
        url: environment.keycloak.url,
        realm: environment.keycloak.realm,
        clientId: environment.keycloak.clientId,
      },
      initOptions: {
        onLoad: 'check-sso',
        silentCheckSsoRedirectUri:
          window.location.origin + '/assets/silent-check-sso.html',
        pkceMethod: 'S256',
      },
    });
}

App Config

// src/app/app.config.ts
import { ApplicationConfig, APP_INITIALIZER } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { KeycloakService, KeycloakBearerInterceptor } from 'keycloak-angular';

import { routes } from './app.routes';
import { initializeKeycloak } from './config/keycloak.config';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(withInterceptors([keycloakBearerInterceptor])),
    KeycloakService,
    {
      provide: APP_INITIALIZER,
      useFactory: initializeKeycloak,
      multi: true,
      deps: [KeycloakService],
    },
  ],
};

6. Estrutura do Projeto

{projeto}/
β”œβ”€β”€ public/
β”‚   └── assets/
β”‚       └── silent-check-sso.html
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main.ts
β”‚   β”œβ”€β”€ index.html
β”‚   β”œβ”€β”€ styles.scss
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ app.component.ts
β”‚   β”‚   β”œβ”€β”€ app.config.ts
β”‚   β”‚   β”œβ”€β”€ app.routes.ts
β”‚   β”‚   β”œβ”€β”€ core/                    # Singleton services
β”‚   β”‚   β”‚   β”œβ”€β”€ guards/
β”‚   β”‚   β”‚   β”‚   └── auth.guard.ts
β”‚   β”‚   β”‚   β”œβ”€β”€ interceptors/
β”‚   β”‚   β”‚   β”‚   └── error.interceptor.ts
β”‚   β”‚   β”‚   └── services/
β”‚   β”‚   β”‚       └── auth.service.ts
β”‚   β”‚   β”œβ”€β”€ shared/                  # Shared components/pipes
β”‚   β”‚   β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ header/
β”‚   β”‚   β”‚   β”‚   └── footer/
β”‚   β”‚   β”‚   β”œβ”€β”€ directives/
β”‚   β”‚   β”‚   └── pipes/
β”‚   β”‚   β”œβ”€β”€ features/                # Feature modules
β”‚   β”‚   β”‚   β”œβ”€β”€ home/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ home.component.ts
β”‚   β”‚   β”‚   β”‚   └── home.routes.ts
β”‚   β”‚   β”‚   └── users/
β”‚   β”‚   β”‚       β”œβ”€β”€ components/
β”‚   β”‚   β”‚       β”‚   └── user-card/
β”‚   β”‚   β”‚       β”œβ”€β”€ services/
β”‚   β”‚   β”‚       β”‚   └── user.service.ts
β”‚   β”‚   β”‚       β”œβ”€β”€ models/
β”‚   β”‚   β”‚       β”‚   └── user.model.ts
β”‚   β”‚   β”‚       β”œβ”€β”€ users.component.ts
β”‚   β”‚   β”‚       └── users.routes.ts
β”‚   β”‚   └── config/
β”‚   β”‚       └── keycloak.config.ts
β”‚   └── environments/
β”‚       β”œβ”€β”€ environment.ts
β”‚       └── environment.prod.ts
β”œβ”€β”€ angular.json
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ tsconfig.app.json
β”œβ”€β”€ tsconfig.spec.json
└── README.md

7. PadrΓ΅es e ConvenΓ§Γ΅es

7.1 Componentes β€” Abordagem PragmΓ‘tica

PrincΓ­pio: Dividir quando faz sentido, nΓ£o por dogma.

Quando criar componente separado Quando NÃO separar
Reutilizado em 2+ lugares Usado sΓ³ uma vez e simples
LΓ³gica complexa isolada Poucos elementos HTML
TestΓ‘vel independentemente Acoplado demais ao pai

7.2 Signals vs RxJS β€” Quando usar cada um

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  SIGNALS (padrΓ£o para maioria dos casos)                        β”‚
β”‚                                                                 β”‚
β”‚  βœ… Estado local do componente                                  β”‚
β”‚  βœ… Valores derivados (computed)                                β”‚
β”‚  βœ… FormulΓ‘rios simples                                         β”‚
β”‚  βœ… UI state (loading, error, etc.)                             β”‚
β”‚  βœ… HTTP requests simples (com toSignal)                        β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  RXJS (sistemas grandes, casos complexos)                       β”‚
β”‚                                                                 β”‚
β”‚  βœ… WebSockets / real-time                                      β”‚
β”‚  βœ… MΓΊltiplos HTTP combinados                                   β”‚
β”‚  βœ… Debounce/throttle de inputs                                 β”‚
β”‚  βœ… Streams contΓ­nuos de eventos                                β”‚
β”‚  βœ… OperaΓ§Γ΅es complexas (switchMap, mergeMap, etc.)             β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

7.3 Exemplo com Signals (maioria dos casos)

import { Component, signal, computed } from '@angular/core';

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <p>Contagem: {{ count() }}</p>
    <p>Dobro: {{ double() }}</p>
    <button (click)="increment()">+1</button>
  `
})
export class CounterComponent {
  // signal() β€” valor reativo
  count = signal(0);
  
  // computed() β€” valor derivado (recalcula automaticamente)
  double = computed(() => this.count() * 2);
  
  increment() {
    this.count.update(c => c + 1);
  }
}

7.4 Exemplo com RxJS (sistemas grandes)

import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs';

@Component({
  selector: 'app-search',
  standalone: true,
  imports: [AsyncPipe, ReactiveFormsModule],
  template: `
    <input [formControl]="searchControl" placeholder="Buscar...">
    @for (item of results$ | async; track item.id) {
      <div>{{ item.name }}</div>
    }
  `
})
export class SearchComponent {
  private api = inject(ApiService);
  
  searchControl = new FormControl('');
  
  // RxJS para debounce + HTTP
  results$ = this.searchControl.valueChanges.pipe(
    debounceTime(300),           // Espera 300ms
    distinctUntilChanged(),      // SΓ³ se mudou
    switchMap(term => this.api.search(term))  // Cancela request anterior
  );
}

7.5 Componentes (Standalone)

// src/app/features/users/components/user-card/user-card.component.ts
import { Component, input, output } from '@angular/core';
import { User } from '../../models/user.model';

@Component({
  selector: 'app-user-card',
  standalone: true,
  template: `
    <div class="user-card">
      <h3>{{ user().name }}</h3>
      <p>{{ user().email }}</p>
      <button (click)="onSelect()">Select</button>
    </div>
  `,
  styles: [`
    .user-card {
      padding: 1rem;
      border: 1px solid #ccc;
      border-radius: 8px;
    }
  `]
})
export class UserCardComponent {
  // Novos inputs baseados em Signals (Angular 17+)
  user = input.required<User>();
  
  // Novo output
  select = output<User>();

  onSelect(): void {
    this.select.emit(this.user());
  }
}

7.2 Services

// src/app/features/users/services/user.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { User } from '../models/user.model';
import { environment } from '../../../../environments/environment';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  private http = inject(HttpClient);
  private apiUrl = `${environment.apiUrl}/users`;

  getAll(): Observable<User[]> {
    return this.http.get<User[]>(this.apiUrl);
  }

  getById(id: string): Observable<User> {
    return this.http.get<User>(`${this.apiUrl}/${id}`);
  }

  create(user: Partial<User>): Observable<User> {
    return this.http.post<User>(this.apiUrl, user);
  }

  update(id: string, user: Partial<User>): Observable<User> {
    return this.http.put<User>(`${this.apiUrl}/${id}`, user);
  }

  delete(id: string): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`);
  }
}

7.3 Signals (State Management)

// src/app/features/users/users.store.ts
import { computed, inject } from '@angular/core';
import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
import { UserService } from './services/user.service';
import { User } from './models/user.model';

interface UsersState {
  users: User[];
  loading: boolean;
  error: string | null;
}

const initialState: UsersState = {
  users: [],
  loading: false,
  error: null,
};

export const UsersStore = signalStore(
  withState(initialState),
  withComputed((state) => ({
    activeUsers: computed(() => state.users().filter(u => u.active)),
    userCount: computed(() => state.users().length),
  })),
  withMethods((store, userService = inject(UserService)) => ({
    async loadUsers() {
      patchState(store, { loading: true, error: null });
      try {
        const users = await userService.getAll().toPromise();
        patchState(store, { users: users ?? [], loading: false });
      } catch (error) {
        patchState(store, { error: 'Failed to load users', loading: false });
      }
    },
  }))
);

7.4 Guards

// src/app/core/guards/auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { KeycloakService } from 'keycloak-angular';

export const authGuard: CanActivateFn = async (route, state) => {
  const keycloak = inject(KeycloakService);
  const router = inject(Router);

  const isAuthenticated = await keycloak.isLoggedIn();

  if (!isAuthenticated) {
    await keycloak.login({
      redirectUri: window.location.origin + state.url,
    });
    return false;
  }

  return true;
};

7.5 Routes

// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { authGuard } from './core/guards/auth.guard';

export const routes: Routes = [
  {
    path: '',
    loadComponent: () => import('./features/home/home.component')
      .then(m => m.HomeComponent),
  },
  {
    path: 'users',
    canActivate: [authGuard],
    loadChildren: () => import('./features/users/users.routes')
      .then(m => m.USERS_ROUTES),
  },
  {
    path: '**',
    redirectTo: '',
  },
];

8. Infraestrutura

Componente Escolha
Build Angular CLI
Deploy Nginx / Azure Static Web Apps
CI/CD GitHub Actions / Azure DevOps

Dockerfile tΓ­pico

# Build stage
FROM node:22-alpine AS builder

WORKDIR /app
RUN npm install -g pnpm @angular/cli

COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

COPY . .
RUN pnpm build --configuration=production

# Production stage
FROM nginx:alpine

COPY --from=builder /app/dist/{projeto}/browser /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

9. O que NÃO usar

Tecnologia Motivo
MΓ³dulos (NgModules) Preferir Standalone Components
RxJS para tudo Preferir Signals quando possΓ­vel
NgRx Store completo Preferir @ngrx/signals (mais simples)
Angular < 19 Sempre versΓ£o recente
npm Preferir pnpm

10. Checklist de Novo Projeto

  • Criar projeto com ng new --standalone
  • Instalar Angular Material ou PrimeNG
  • Configurar Keycloak
  • Criar estrutura de pastas (core, shared, features)
  • Configurar environments
  • Configurar guards
  • Criar interceptors (error, loading)
  • Criar Dockerfile + nginx.conf
  • Configurar Azure DevOps / GitHub Actions
  • Criar README.md com instruΓ§Γ΅es

11. Links de ReferΓͺncia


Nota: Este template Γ© a base. Cada projeto de cliente pode ter ajustes especΓ­ficos documentados no technical_context.md do projeto.