Frontend Angular 19: arquitectura moderna para una aplicación compleja
La interfaz de Reproducir el evento y una aplicación Angular 19 que maneja 27 módulos funcionales, admite 21 idiomas e integra bibliotecas avanzadas como Univerjs, Konva, D3.js y MapaLibre. En este artículo exploramos las opciones arquitectónicas que Haga todo esto posible sin sacrificar el rendimiento y la mantenibilidad.
Lo que encontrarás en este artículo
- Arquitectura independiente sin NgModules
- Gestión de estados con Signals y BaseStore
- Representación del lado del servidor 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
Toda la aplicación utiliza componentes independientes, eliminando por completo NgModules. Cada componente declara el suyo dependencies directly in the decorator, making the code more explicit y una sacudida de árboles más 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;
}
La principal ventaja y claridad: mirando un componente, ya sabes exactamente de qué depende. No es necesario navegar por formularios anidado para comprender lo que está disponible en la plantilla.
Gestión de estados con señales: el patrón BaseStore
Para la gestión estatal, Play The Event utiliza un patrón. personalizado basado en Señales angulares. El Tienda base proporciona funcionalidad común a todos las tiendas de aplicaciones.
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 qué BaseStore y no NgRx?
- Sencillez: NgRx requiere acciones, reductores, efectos y selectores. BaseStore es un único archivo.
- Actuación: Las señales son nativas de Angular y están optimizadas para la detección de cambios OnPush.
- Caché integrado: TTL evita llamadas API redundantes sin bibliotecas adicionales.
- Tipificación: Cada tienda está fuertemente tipada gracias a los genéricos de TypeScript.
Representación del lado del servidor con Express
SSR es fundamental para el rendimiento percibido y el SEO. Reproducir el evento utiliza Angular Universal con un servidor Express que maneja el renderizado lado del servidor.
El servidor Express no se limita al renderizado: también maneja la compresión, almacenamiento en caché de páginas estáticas y reescritura de URL para soporte multilenguaje. Las páginas públicas (landing, precios, blog) están renderizadas previamente y almacenadas en caché, mientras que las páginas autenticadas se muestran según demanda.
27 Moduli Lazy-Loaded
La aplicación se divide en 27 módulos de funciones, cada uno cargado bajo demanda mediante carga diferida. Esto significa que el usuario descarga sólo el código necesario para la página que 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 con TranslateService
La internacionalización se gestiona a través de un Traducir servicio personalizado que soporta 21 idiomas. Los archivos de traducción son JSON. organizado por módulo funcional, cargado bajo demanda junto con el formulario correspondiente.
Retos i18n en 21 idiomas
Manejar 21 idiomas no es solo traducir cadenas. debe ser considerado dirección del texto (LTR/RTL para árabe), formatos de fecha y monedas locales, pluralización (que varía enormemente entre idiomas), y la longitud variable de los textos que impacta el diseño.
Librerie UI Avanzate
Play The Event integra cinco bibliotecas de UI especializadas, cada una para un caso de uso específico.
Librerie Integrate
- Universidades: Hoja de cálculo en el navegador para presupuestos colaborativos y hojas de cálculo
- Konva: Lienzo 2D para el editor de planos de planta y diseño de tablas
- D3.js: Gráficos de relaciones con los participantes y visualizaciones de datos complejos.
- MapaLibre GL: Mapas interactivos para itinerarios de viaje y ubicación del lugar.
- Gráfico.js: Gráficos para análisis, paneles estadísticos e informes financieros.
HTTP Interceptors
Dos interceptores HTTP manejan llamadas API en todos los ámbitos.
Interceptor de autenticación con actualización de token
El interceptor de autenticación agrega automáticamente el token JWT a cada solicitud. Cuando el token expira (respuesta 401), el interceptor intenta una actualización automática usando el token de actualización, repita el solicitud original con el nuevo token y, si la actualización también falla, redirige a la página de inicio de sesión.
Error Interceptor
El interceptor de errores centraliza el manejo de errores HTTP: mostrar notificaciones de brindis para errores del cliente (4xx), páginas de error para errores servidor (5xx) y registrar errores para la depuración.
Premium Feature Guards
Algunas funciones solo están disponibles para usuarios de suscripción PRO o EMPRESA. EL mirar comprobar el nivel de suscripción antes de permitir el acceso a la ruta.
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
La accesibilidad no es un añadido tardío: está integrada en el proceso de desarrollo. Cada componente se prueba para cumplir con WCAG 2.1 AA.
Prácticas de accesibilidad
- Contraste de color mínimo 4,5:1 para texto normal, 3:1 para texto grande
- Navegación completa por teclado con focos visibles
- Atributos ARIA para componentes interactivos personalizados
- Anuncios de lectores de pantalla en acciones dinámicas
- Apoyo
prefers-reduced-motionpara reducir animaciones - Pruebas automatizadas con núcleo de hacha en el proceso de CI
Testing: Jest, Playwright e axe-core
La estrategia de prueba se divide en tres niveles.
Unit e Component Test
- Broma: Pruebas unitarias para servicios, tuberías y lógica de negocios.
- Pruebas de componentes con TestBed para componentes Angular
- Servicios HTTP simulados para pruebas aisladas
E2E e Accessibilita
- Playwright: Test end-to-end cross-browser (Chrome, Firefox, Safari)
- núcleo de hacha: Pruebas automáticas de accesibilidad integradas en las pruebas E2E
- Pruebas de regresión visual para componentes críticos.
Punti Chiave
- Componentes independientes para una máxima claridad y una sacudida eficaz de los árboles.
- BaseStore con señales y caché TTL para un estado de respuesta y rendimiento
- SSR con Express para SEO y rendimiento percibido
- 27 módulos de carga diferida para un tamaño de paquete óptimo
- 5 librerie UI specializzate integrate armoniosamente
- Interceptor HTTP para autenticación automática y manejo centralizado de errores
- Testing a tre livelli: unit, E2E e accessibilità
El código fuente de la interfaz está disponible en GitHub. Visita www.playtheevent.com para ver el resultado final.







