Frontend Angular 19: Arquitetura Moderna para um Aplicativo Complexo
A interface de Jogue o evento e um aplicativo Angular 19 que lida com 27 módulos funcionais, suporta 21 idiomas e integra bibliotecas avançadas como Univerjs, Konva, D3.js e MapLibre. Neste artigo exploramos as escolhas arquitetônicas que torne tudo isso possível sem sacrificar o desempenho e a facilidade de manutenção.
O que você encontrará neste artigo
- Arquitetura autônoma sem NgModules
- Gerenciamento de estado com Signals e BaseStore
- Renderização no lado do servidor com Express
- 27 moduli lazy-loaded e design responsive
- Librerie UI avanzate: Univerjs, Konva, D3.js, MapLibre, Chart.js
- Interceptor HTTP, guard premium e accessibilità
Standalone Components: Addio NgModules
Todo o aplicativo usa componentes autônomos, eliminando completamente NgModules. Cada componente declara seu próprio dependências diretamente no decorador, tornando o código mais explícito e sacudir árvores mais eficaz.
@Component({
selector: 'app-event-dashboard',
standalone: true,
imports: [
CommonModule,
TranslateModule,
ChartComponent,
ExpenseSummaryComponent,
GuestListComponent
],
templateUrl: './event-dashboard.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EventDashboardComponent {
private readonly store = inject(EventStore);
event = this.store.currentEvent;
guests = this.store.guests;
expenses = this.store.expenses;
isLoading = this.store.isLoading;
}
A principal vantagem e clareza: olhando para um componente, você sabe exatamente do que depende. Não há necessidade de navegar pelos formulários aninhados para entender o que está disponível no modelo.
Gerenciamento de estado com sinais: o padrão BaseStore
Para gerenciamento de estado, Play The Event usa um padrão personalizado com base em Sinais Angulares. O BaseStore fornece funcionalidade comum a todos as lojas de aplicativos.
export abstract class BaseStore<T> {
// Stato interno con signals
protected readonly _data = signal<T | null>(null);
protected readonly _isLoading = signal(false);
protected readonly _error = signal<string | null>(null);
protected readonly _lastFetch = signal<number>(0);
// Cache TTL configurabile (default: 5 minuti)
protected readonly cacheTTL = 5 * 60 * 1000;
// Selettori pubblici (readonly)
readonly data = this._data.asReadonly();
readonly isLoading = this._isLoading.asReadonly();
readonly error = this._error.asReadonly();
// Computed signals
readonly hasData = computed(() => this._data() !== null);
readonly isStale = computed(() => {
const elapsed = Date.now() - this._lastFetch();
return elapsed > this.cacheTTL;
});
// Fetch con cache intelligente
async loadIfNeeded(): Promise<void> {
if (this.hasData() && !this.isStale()) {
return; // Dati freschi in cache, skip
}
await this.forceLoad();
}
protected abstract fetchData(): Observable<T>;
}
Por que BaseStore e não NgRx?
- Simplicidade: NgRx requer ações, redutores, efeitos e seletores. O BaseStore é um arquivo único.
- Desempenho: Os sinais são nativos do Angular e otimizados para detecção de alterações OnPush.
- Cache integrado: TTL evita chamadas de API redundantes sem bibliotecas adicionais.
- Tipificação: Cada loja é fortemente tipada graças aos genéricos TypeScript.
Renderização no lado do servidor com Express
SSR é fundamental para o desempenho percebido e SEO. Jogue o evento usa Angular Universal com um servidor Express que lida com renderização lado do servidor.
O servidor Express não se limita à renderização: ele também lida com compactação, cache de página estática e reescrita de URL para suporte multilíngue. As páginas públicas (destino, preços, blog) são pré-renderizadas e armazenadas em cache, enquanto as páginas autenticadas são renderizadas sob demanda.
27 Moduli Lazy-Loaded
O aplicativo é dividido em 27 módulos de recursos, cada carregado sob demanda por meio de carregamento lento. Isso significa que o usuário baixa apenas o código necessário para a página que você está visitando.
export const routes: Routes = [
{
path: 'events',
loadComponent: () =>
import('./features/events/event-list.component')
.then(m => m.EventListComponent),
canActivate: [authGuard]
},
{
path: 'events/:id/expenses',
loadComponent: () =>
import('./features/expenses/expense-dashboard.component')
.then(m => m.ExpenseDashboardComponent),
canActivate: [authGuard, premiumGuard('PRO')]
},
{
path: 'events/:id/seating',
loadComponent: () =>
import('./features/seating/seating-chart.component')
.then(m => m.SeatingChartComponent),
canActivate: [authGuard, premiumGuard('ENTERPRISE')]
},
// ... altri 24 moduli
];
Design Responsive: Mobile-First
L'interfaccia e progettata con approccio mobile-first e utilizza tre breakpoint principali.
/* Breakpoint */
--breakpoint-mobile: 320px; /* Smartphone */
--breakpoint-tablet: 768px; /* Tablet */
--breakpoint-desktop: 1024px; /* Desktop */
/* Typography: Montserrat + Lato */
--font-heading: 'Montserrat', sans-serif;
--font-body: 'Lato', sans-serif;
/* Layout responsive */
.event-grid {
display: grid;
grid-template-columns: 1fr; /* Mobile: 1 colonna */
}
@media (min-width: 768px) {
.event-grid {
grid-template-columns: repeat(2, 1fr); /* Tablet: 2 colonne */
}
}
@media (min-width: 1024px) {
.event-grid {
grid-template-columns: repeat(3, 1fr); /* Desktop: 3 colonne */
}
}
21 idiomas com TranslateService
A internacionalização é gerida através de um Serviço de tradução personalizado que suporta 21 idiomas. Os arquivos de tradução são JSON organizado por módulo funcional, carregado sob demanda junto com o formulário correspondente.
Desafios i18n em 21 idiomas
Gerenciar 21 idiomas não é apenas traduzir strings. Deve ser considerado direção do texto (LTR/RTL para árabe), formatos de data e moedas locais, pluralização (que varia enormemente entre línguas), e o comprimento variável dos textos que impacta o layout.
Librerie UI Avanzate
Play The Event integra cinco bibliotecas UI especializadas, cada uma para um caso de uso específico.
Librerie Integrate
- Universidades: Planilha no navegador para orçamentos e planilhas colaborativas
- Conva: Tela 2D para editor de planta baixa e layout de mesa
- D3.js: Gráficos de relacionamentos entre participantes e visualizações de dados complexos
- MapLibre GL: Mapas interativos para itinerários de viagem e localização de locais
- Gráfico.js: Gráficos para análises, painéis estatísticos e relatórios financeiros
HTTP Interceptors
Dois interceptadores HTTP lidam com chamadas de API de maneira geral.
Auth Interceptor com atualização de token
O interceptor de autenticação adiciona automaticamente o token JWT a cada solicitação. Quando o token expira (resposta 401), o interceptador tenta uma atualização automática usando o token de atualização, repita o solicitação original com o novo token e, se a atualização também falhar, redireciona para a página de login.
Error Interceptor
O interceptador de erros centraliza o tratamento de erros HTTP: show notificações do sistema para erros do cliente (4xx), páginas de erro para erros servidor (5xx) e registrar erros para depuração.
Premium Feature Guards
Alguns recursos estão disponíveis apenas para usuários assinantes PRO ou EMPRESA. O olhar verifique o nível de assinatura antes de permitir o acesso à rota.
export function premiumGuard(requiredLevel: 'PRO' | 'ENTERPRISE') {
return () => {
const auth = inject(AuthStore);
const router = inject(Router);
const userLevel = auth.subscriptionLevel();
const levels = { 'BASIC': 0, 'PRO': 1, 'ENTERPRISE': 2 };
if (levels[userLevel] >= levels[requiredLevel]) {
return true;
}
return router.createUrlTree(['/pricing'], {
queryParams: { upgrade: requiredLevel }
});
};
}
Accessibilita WCAG 2.1 AA
A acessibilidade não é uma adição tardia: está integrada no processo de desenvolvimento. Cada componente é testado para conformidade com WCAG 2.1 AA.
Práticas de acessibilidade
- Contraste mínimo de cores 4,5:1 para texto normal, 3:1 para texto grande
- Navegação completa pelo teclado com focos visíveis
- Atributos ARIA para componentes interativos personalizados
- Anúncios de leitores de tela em ações dinâmicas
- Apoiar
prefers-reduced-motionpara reduzir animações - Testes automatizados com núcleo de machado no pipeline de CI
Testing: Jest, Playwright e axe-core
A estratégia de teste é dividida em três níveis.
Unit e Component Test
- Brincadeira: Testes unitários para serviços, pipes e lógica de negócios
- Teste de componentes com TestBed para componentes Angular
- Serviços HTTP simulados para testes isolados
E2E e Accessibilita
- Playwright: Test end-to-end cross-browser (Chrome, Firefox, Safari)
- núcleo do machado: Testes automáticos de acessibilidade integrados em testes E2E
- Teste de regressão visual para componentes críticos
Punti Chiave
- Componentes autônomos para máxima clareza e agitação eficaz de árvores
- BaseStore com sinais e cache TTL para estado responsivo e de alto desempenho
- SSR com Express para SEO e desempenho percebido
- 27 módulos de carregamento lento para tamanho de pacote ideal
- 5 librerie UI specializzate integrate armoniosamente
- Interceptador HTTP para autenticação automática e tratamento centralizado de erros
- Testing a tre livelli: unit, E2E e accessibilità
O código-fonte do frontend está disponível em GitHub. Visita www.playtheevent.com para ver o resultado final.







