Frontend Angular 19: Architettura Moderna per un'App Complessa
Il frontend di Play The Event e un'applicazione Angular 19 che gestisce 27 moduli funzionali, supporta 21 lingue, e integra librerie avanzate come Univerjs, Konva, D3.js e MapLibre. In questo articolo esploriamo le scelte architetturali che rendono tutto questo possibile senza sacrificare performance e manutenibilità.
Cosa Troverai in Questo Articolo
- Architettura standalone senza NgModules
- Gestione stato con Signals e BaseStore
- Server-Side Rendering con 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
L'intera applicazione utilizza standalone components, eliminando completamente i NgModules. Ogni componente dichiara le proprie dipendenze direttamente nel decorator, rendendo il codice più esplicito e il tree-shaking più efficace.
@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;
}
Il vantaggio principale e la chiarezza: guardando un componente, sai esattamente da cosa dipende. Non serve navigare attraverso moduli annidati per capire cosa e disponibile nel template.
State Management con Signals: Il Pattern BaseStore
Per la gestione dello stato, Play The Event utilizza un pattern personalizzato basato sui Signals di Angular. Il BaseStore fornisce funzionalità comuni a tutti gli store dell'applicazione.
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>;
}
perchè BaseStore e Non NgRx?
- Semplicita: NgRx richiede actions, reducers, effects e selectors. Il BaseStore e un singolo file.
- Performance: I Signals sono nativi di Angular e ottimizzati per il change detection OnPush.
- Cache integrata: Il TTL evita chiamate API ridondanti senza librerie aggiuntive.
- Tipizzazione: Ogni store e fortemente tipizzato grazie ai generics TypeScript.
Server-Side Rendering con Express
L'SSR e fondamentale per le performance percepite e per la SEO. Play The Event utilizza Angular Universal con un server Express che gestisce il rendering lato server.
Il server Express non si limita al rendering: gestisce anche la compressione, il caching delle pagine statiche e il rewriting degli URL per il supporto multi-lingua. Le pagine pubbliche (landing, pricing, blog) vengono pre-renderizzate e cachate, mentre le pagine autenticate vengono renderizzate on-demand.
27 Moduli Lazy-Loaded
L'applicazione e suddivisa in 27 feature modules, ciascuno caricato on-demand tramite lazy loading. Questo significa che l'utente scarica solo il codice necessario per la pagina che sta 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 Lingue con TranslateService
L'internazionalizzazione e gestita tramite un TranslateService personalizzato che supporta 21 lingue. I file di traduzione sono JSON organizzati per modulo funzionale, caricati on-demand insieme al modulo corrispondente.
Sfide dell'i18n a 21 Lingue
Gestire 21 lingue non e solo tradurre stringhe. Bisogna considerare la direzione del testo (LTR/RTL per l'arabo), i formati di data e valuta locali, la pluralizzazione (che varia enormemente tra le lingue), e la lunghezza variabile dei testi che impatta il layout.
Librerie UI Avanzate
Play The Event integra cinque librerie UI specializzate, ognuna per un caso d'uso specifico.
Librerie Integrate
- Univerjs: Spreadsheet nel browser per budget e fogli di calcolo collaborativi
- Konva: Canvas 2D per l'editor di planimetrie e disposizione tavoli
- D3.js: Grafi delle relazioni tra partecipanti e visualizzazioni dati complesse
- MapLibre GL: Mappe interattive per itinerari di viaggio e localizzazione venue
- Chart.js: Grafici per analytics, dashboard statistiche e report finanziari
HTTP Interceptors
Due interceptor HTTP gestiscono trasversalmente le chiamate API.
Auth Interceptor con Token Refresh
L'auth interceptor aggiunge automaticamente il JWT token a ogni richiesta. Quando il token scade (risposta 401), l'interceptor tenta un refresh automatico usando il refresh token, ripete la richiesta originale con il nuovo token e, se anche il refresh fallisce, reindirizza alla pagina di login.
Error Interceptor
L'error interceptor centralizza la gestione degli errori HTTP: mostra notifiche toast per errori client (4xx), pagine di errore per errori server (5xx), e logga gli errori per il debugging.
Premium Feature Guards
Alcune funzionalità sono disponibili solo per utenti con abbonamento PRO o ENTERPRISE. I guard verificano il livello di abbonamento prima di permettere l'accesso alla rotta.
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
L'accessibilità non e un'aggiunta tardiva: e integrata nel processo di sviluppo. Ogni componente e testato per conformità WCAG 2.1 AA.
Pratiche di Accessibilita
- Contrasto colori minimo 4.5:1 per testo normale, 3:1 per testo grande
- Navigazione completa da tastiera con focus visibili
- Attributi ARIA per componenti interattivi personalizzati
- Annunci per screen reader sulle azioni dinamiche
- Supporto
prefers-reduced-motionper ridurre le animazioni - Test automatizzati con axe-core nella CI pipeline
Testing: Jest, Playwright e axe-core
La strategia di testing si articola su tre livelli.
Unit e Component Test
- Jest: Test unitari per services, pipes e logica di business
- Component testing con TestBed per i componenti Angular
- Mock dei servizi HTTP per test isolati
E2E e Accessibilita
- Playwright: Test end-to-end cross-browser (Chrome, Firefox, Safari)
- axe-core: Test automatici di accessibilità integrati nei test E2E
- Visual regression testing per i componenti critici
Punti Chiave
- Standalone components per massima chiarezza e tree-shaking efficace
- BaseStore con Signals e cache TTL per stato reattivo e performante
- SSR con Express per SEO e performance percepite
- 27 moduli lazy-loaded per bundle size ottimale
- 5 librerie UI specializzate integrate armoniosamente
- Interceptor HTTP per auth automatica e gestione errori centralizzata
- Testing a tre livelli: unit, E2E e accessibilità
Il codice sorgente del frontend e disponibile su GitHub. Visita www.playtheevent.com per vedere il risultato finale.







