# 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()) {
      <mat-spinner />
    } @else {
      <mat-table [dataSource]="students()">
        <!-- columns -->
      </mat-table>
    }
  `
})
export class StudentListComponent {
  private studentService = inject(StudentService);

  students = signal<Student[]>([]);
  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 |
