# DI-005: Arquitectura Frontend **Proyecto:** Sistema de Registro de Estudiantes **Fecha:** 2026-01-07 --- ## 1. Stack Tecnológico | Tecnología | Propósito | |------------|-----------| | Angular 21 | Framework SPA | | Angular Material | UI Components | | Apollo Angular | Cliente GraphQL | | Signals | Estado reactivo | | TypeScript | Tipado estático | --- ## 2. Estructura de Carpetas ``` src/frontend/src/app/ ├── core/ # Singleton services │ ├── services/ │ │ ├── student.service.ts │ │ └── enrollment.service.ts │ ├── graphql/ │ │ ├── generated/ # Tipos generados │ │ ├── queries/ │ │ │ ├── students.graphql │ │ │ └── subjects.graphql │ │ └── mutations/ │ │ ├── student.graphql │ │ └── enrollment.graphql │ └── interceptors/ │ └── error.interceptor.ts │ ├── shared/ # Reutilizables │ ├── components/ │ │ ├── confirm-dialog/ │ │ └── loading-spinner/ │ └── pipes/ │ └── credits.pipe.ts │ └── features/ # Módulos por funcionalidad ├── students/ │ ├── pages/ │ │ ├── student-list/ │ │ └── student-form/ │ └── components/ │ └── student-card/ │ ├── enrollment/ │ ├── pages/ │ │ └── enrollment-page/ │ └── components/ │ ├── subject-selector/ │ └── enrolled-subjects/ │ └── classmates/ └── pages/ └── classmates-page/ ``` --- ## 3. Configuración Apollo ```typescript // app.config.ts export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideApollo(() => ({ link: httpLink.create({ uri: environment.graphqlUrl }), cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network' }, mutate: { errorPolicy: 'all' } } })) ] }; ``` --- ## 4. Gestión de Estado ``` ┌─────────────────────────────────────────────┐ │ Apollo Cache │ │ (Estado del servidor: students, subjects) │ └─────────────────────────────────────────────┘ ▲ │ watchQuery / mutate ▼ ┌─────────────────────────────────────────────┐ │ Services │ │ (Encapsulan operaciones GraphQL) │ └─────────────────────────────────────────────┘ ▲ │ inject() ▼ ┌─────────────────────────────────────────────┐ │ Components │ │ (Signals para estado local/UI) │ └─────────────────────────────────────────────┘ ``` --- ## 5. Ejemplo de Service ```typescript @Injectable({ providedIn: 'root' }) export class StudentService { private apollo = inject(Apollo); getStudents() { return this.apollo.watchQuery<{ students: Student[] }>({ query: GET_STUDENTS, fetchPolicy: 'cache-and-network' }).valueChanges; } createStudent(input: CreateStudentInput) { return this.apollo.mutate<{ createStudent: StudentPayload }>({ mutation: CREATE_STUDENT, variables: { input }, refetchQueries: [{ query: GET_STUDENTS }] }); } } ``` --- ## 6. Ejemplo de Componente (Signals) ```typescript @Component({ selector: 'app-student-list', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: ` @if (loading()) { } @else { } ` }) export class StudentListComponent { private studentService = inject(StudentService); students = signal([]); loading = signal(true); constructor() { this.studentService.getStudents().subscribe(({ data, loading }) => { this.students.set(data?.students ?? []); this.loading.set(loading); }); } } ``` --- ## 7. Rutas (Lazy Loading) ```typescript export const routes: Routes = [ { path: '', redirectTo: 'students', pathMatch: 'full' }, { path: 'students', loadComponent: () => import('./features/students/pages/student-list/student-list.component') }, { path: 'students/new', loadComponent: () => import('./features/students/pages/student-form/student-form.component') }, { path: 'enrollment/:studentId', loadComponent: () => import('./features/enrollment/pages/enrollment-page/enrollment-page.component') }, { path: 'classmates/:studentId', loadComponent: () => import('./features/classmates/pages/classmates-page/classmates-page.component') } ]; ``` --- ## 8. Decisiones de Diseño | Decisión | Razón | |----------|-------| | Standalone Components | Angular moderno, tree-shakeable | | Signals vs RxJS | Más simple para estado local, menos boilerplate | | Apollo Cache | Single source of truth para datos del servidor | | OnPush | Mejor rendimiento, menos re-renders | | Lazy Loading | Bundle inicial pequeño |