Salta al contenuto principale

16 - Frontend Angular 19: Architettura Moderna per un\

Frontend Angular 19: Architettura Moderna per un'App Complessa Il frontend di Play The Event e un'applicazione Angular 19 che gestisce 27 moduli...

Cosa ne pensi di questo articolo?

16 - Frontend Angular 19: Architettura Moderna per un\
Play the EventFoto:Brett SaylesPexels

In Breve

Frontend Angular 19: Architettura Moderna per un'App Complessa Il frontend di Play The Event e un'applicazione Angular 19 che gestisce 27 moduli...

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.

Esempio di Standalone Component
@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.

BaseStore: Pattern con Cache TTL
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.

Routing con Lazy Loading
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 e Design System
/* 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.

Premium Guard
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-motion per 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.

Domande frequenti

Come funziona la gestione dello stato in Play The Event?

La gestione dello stato in Play The Event utilizza un pattern personalizzato basato sui Signals di Angular, che fornisce funzionalità comuni a tutti gli store dell'applicazione.

Quali sono le librerie UI avanzate integrate in Play The Event?

Cinque librerie UI specializzate sono integrate in Play The Event, ognuna per un caso d'uso specifico.

Come funziona la gestione degli errori HTTP in Play The Event?

L'error interceptor centralizza la gestione degli errori HTTP, mostrando notifiche toast per errori client (4xx), pagine di errore per errori server (5xx) e loggando gli errori per il debugging.

Qual è lo standard di accessibilità utilizzato in Play The Event?

L'accessibilità non è un'aggiunta tardiva, ma è integrata nel processo di sviluppo, con la conformità WCAG 2.1 AA per ogni componente.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti