Frontend Angular 19 : Architecture moderne pour une application complexe
L'interface de Jouez à l'événement et une application Angular 19 qui gère 27 modules fonctionnels, elle prend en charge 21 langages, et intègre des bibliothèques avancées telles que Univerjs, Konva, D3.js et MapLibre. Dans cet article, nous explorons les choix architecturaux qui rendre tout cela possible sans sacrifier les performances et la maintenabilité.
Ce que vous trouverez dans cet article
- Architecture autonome sans NgModules
- Gestion de l'état avec Signals et BaseStore
- Rendu côté serveur avec 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'ensemble de l'application utilise composants autonomes, éliminant complètement les NgModules. Chaque composant déclare le sien dépendances directement dans le décorateur, rendant le code plus explicite et un secouement des arbres plus 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;
}
Le principal avantage et la clarté : en regardant un composant, vous savez exactement de quoi cela dépend. Pas besoin de naviguer dans les formulaires imbriqués pour comprendre ce qui est disponible dans le modèle.
Gestion d'état avec des signaux : le modèle BaseStore
Pour la gestion de l'état, Play The Event utilise un modèle personnalisé en fonction de Signaux angulaires. Le BaseStore fournit des fonctionnalités communes à tous les magasins d'applications.
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>;
}
Pourquoi BaseStore et pas NgRx ?
- Simplicité: NgRx nécessite des actions, des réducteurs, des effets et des sélecteurs. Le BaseStore est un fichier unique.
- Performance: Les signaux sont natifs d'Angular et optimisés pour la détection des changements OnPush.
- Cache intégré : TTL évite les appels d'API redondants sans bibliothèques supplémentaires.
- Typification : Chaque boutique est fortement typée grâce aux génériques TypeScript.
Rendu côté serveur avec Express
Le SSR est essentiel à la performance perçue et au référencement. Jouez à l'événement utilise Angular Universal avec un serveur Express qui gère le rendu côté serveur.
Le serveur Express ne se limite pas au rendu : il gère également la compression, mise en cache des pages statiques et réécriture d'URL pour le support multilingue. Les pages publiques (landing, tarification, blog) sont pré-rendues et mises en cache, tandis que les pages authentifiées sont rendues à la demande.
27 Moduli Lazy-Loaded
L'application est divisée en 27 modules de fonctionnalités, chacun chargé à la demande via un chargement différé. Cela signifie que l'utilisateur télécharge uniquement le code nécessaire à la page que vous visitez.
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 langues avec TranslateService
L'internationalisation est gérée à travers un TraduireService personnalisé qui prend en charge 21 langues. Les fichiers de traduction sont JSON organisés par modules fonctionnels, chargés à la demande avec les formulaire correspondant.
Défis i18n en 21 langues
Gérer 21 langues ne consiste pas seulement à traduire des chaînes. Il faut considérer direction du texte (LTR/RTL pour l'arabe), formats de date et monnaies locales, pluralisation (qui varie énormément selon les langues), et la longueur variable des textes qui impacte la mise en page.
Librerie UI Avanzate
Play The Event intègre cinq bibliothèques d'interface utilisateur spécialisées, chacune pour un cas d’utilisation spécifique.
Librerie Integrate
- Univers : Feuille de calcul dans le navigateur pour les budgets et feuilles de calcul collaboratifs
- Konva : Canevas 2D pour l'éditeur de plan d'étage et de disposition de table
- D3.js : Graphiques des relations entre les participants et visualisations de données complexes
- MapLibre GL : Cartes interactives pour les itinéraires de voyage et l'emplacement des lieux
- Chart.js : Graphiques pour analyses, tableaux de bord statistiques et rapports financiers
HTTP Interceptors
Deux intercepteurs HTTP gèrent les appels API à tous les niveaux.
Intercepteur d'authentification avec actualisation des jetons
L'intercepteur d'authentification ajoute automatiquement le jeton JWT à chaque requête. Lorsque le jeton expire (réponse 401), l'intercepteur tente une rafraîchissement automatique à l'aide du jeton d'actualisation, répétez l'opération demande d'origine avec le nouveau jeton et, si l'actualisation échoue également, redirige vers la page de connexion.
Error Interceptor
L'intercepteur d'erreurs centralise la gestion des erreurs HTTP : show notifications toast pour les erreurs client (4xx), pages d'erreur pour les erreurs serveur (5xx) et consigner les erreurs pour le débogage.
Premium Feature Guards
Certaines fonctionnalités ne sont disponibles que pour les utilisateurs abonnés PRO ou ENTREPRISE. LE regarder vérifier le niveau de abonnement avant d’autoriser l’accès à l’itinéraire.
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é n’est pas un ajout tardif : elle est intégrée au processus de développement. Chaque composant est testé pour sa conformité aux WCAG 2.1 AA.
Pratiques d'accessibilité
- Contraste de couleur minimum 4,5:1 pour le texte normal, 3:1 pour le texte de grande taille
- Navigation complète au clavier avec focus visibles
- Attributs ARIA pour les composants interactifs personnalisés
- Annonces sur lecteur d'écran sur les actions dynamiques
- Soutien
prefers-reduced-motionpour réduire les animations - Tests automatisés avec noyau de hache dans le pipeline CI
Testing: Jest, Playwright e axe-core
La stratégie de test est divisée en trois niveaux.
Unit e Component Test
- Plaisanter: Tests unitaires pour les services, les tuyaux et la logique métier
- Test de composants avec TestBed pour les composants angulaires
- Services HTTP simulés pour des tests isolés
E2E e Accessibilita
- Playwright: Test end-to-end cross-browser (Chrome, Firefox, Safari)
- noyau de hache : Tests d'accessibilité automatiques intégrés aux tests E2E
- Tests de régression visuelle pour les composants critiques
Punti Chiave
- Composants autonomes pour une clarté maximale et un tremblement d'arbre efficace
- BaseStore avec signaux et cache TTL pour un état réactif et performant
- SSR avec Express pour le référencement et la performance perçue
- 27 modules chargés paresseux pour une taille de paquet optimale
- 5 librerie UI specializzate integrate armoniosamente
- Intercepteur HTTP pour l'authentification automatique et la gestion centralisée des erreurs
- Testing a tre livelli: unit, E2E e accessibilità
Le code source du frontend est disponible sur GitHub. Visite www.playtheevent.com pour voir le résultat final.







