# DI-004: Esquema GraphQL

**Proyecto:** Sistema de Registro de Estudiantes
**Fecha:** 2026-01-07

---

## 1. Schema Completo

```graphql
# ═══════════════════════════════════════════════════════════════
# TYPES
# ═══════════════════════════════════════════════════════════════

type Student {
  id: Int!
  name: String!
  email: String!
  totalCredits: Int!
  enrollments: [Enrollment!]!
}

type Subject {
  id: Int!
  name: String!
  credits: Int!
  professor: Professor!
  enrolledStudents: [Student!]!
}

type Professor {
  id: Int!
  name: String!
  subjects: [Subject!]!
}

type Enrollment {
  id: Int!
  student: Student!
  subject: Subject!
  enrolledAt: DateTime!
}

type AvailableSubject {
  subject: Subject!
  isAvailable: Boolean!
  unavailableReason: String
}

type Classmate {
  subjectName: String!
  students: [String!]!
}

# ═══════════════════════════════════════════════════════════════
# QUERIES
# ═══════════════════════════════════════════════════════════════

type Query {
  # Estudiantes
  students: [Student!]!
  student(id: Int!): Student

  # Materias
  subjects: [Subject!]!
  subject(id: Int!): Subject
  availableSubjects(studentId: Int!): [AvailableSubject!]!

  # Profesores
  professors: [Professor!]!
  professor(id: Int!): Professor

  # Compañeros de clase
  classmates(studentId: Int!): [Classmate!]!
}

# ═══════════════════════════════════════════════════════════════
# MUTATIONS
# ═══════════════════════════════════════════════════════════════

type Mutation {
  # Estudiantes
  createStudent(input: CreateStudentInput!): StudentPayload!
  updateStudent(id: Int!, input: UpdateStudentInput!): StudentPayload!
  deleteStudent(id: Int!): DeletePayload!

  # Inscripciones
  enrollStudent(input: EnrollInput!): EnrollmentPayload!
  unenrollStudent(enrollmentId: Int!): DeletePayload!
}

# ═══════════════════════════════════════════════════════════════
# INPUTS
# ═══════════════════════════════════════════════════════════════

input CreateStudentInput {
  name: String!
  email: String!
}

input UpdateStudentInput {
  name: String
  email: String
}

input EnrollInput {
  studentId: Int!
  subjectId: Int!
}

# ═══════════════════════════════════════════════════════════════
# PAYLOADS (Union para errores)
# ═══════════════════════════════════════════════════════════════

type StudentPayload {
  student: Student
  errors: [String!]
}

type EnrollmentPayload {
  enrollment: Enrollment
  errors: [String!]
}

type DeletePayload {
  success: Boolean!
  errors: [String!]
}

# ═══════════════════════════════════════════════════════════════
# SCALARS
# ═══════════════════════════════════════════════════════════════

scalar DateTime
```

---

## 2. Ejemplos de Queries

### Listar estudiantes con inscripciones
```graphql
query GetStudents {
  students {
    id
    name
    email
    totalCredits
    enrollments {
      subject { name professor { name } }
    }
  }
}
```

### Materias disponibles para inscripción
```graphql
query GetAvailableSubjects($studentId: Int!) {
  availableSubjects(studentId: $studentId) {
    subject { id name credits professor { name } }
    isAvailable
    unavailableReason
  }
}
```

### Compañeros de clase
```graphql
query GetClassmates($studentId: Int!) {
  classmates(studentId: $studentId) {
    subjectName
    students
  }
}
```

---

## 3. Ejemplos de Mutations

### Crear estudiante
```graphql
mutation CreateStudent {
  createStudent(input: { name: "Juan Pérez", email: "juan@email.com" }) {
    student { id name email }
    errors
  }
}
```

### Inscribir en materia
```graphql
mutation Enroll {
  enrollStudent(input: { studentId: 1, subjectId: 3 }) {
    enrollment {
      id
      subject { name }
      enrolledAt
    }
    errors
  }
}
```

---

## 4. DataLoaders (Evitar N+1)

| DataLoader | Carga Batch |
|------------|-------------|
| `StudentByIdDataLoader` | Estudiantes por IDs |
| `SubjectByIdDataLoader` | Materias por IDs |
| `ProfessorByIdDataLoader` | Profesores por IDs |
| `EnrollmentsByStudentDataLoader` | Inscripciones por estudiante |
| `SubjectsByProfessorDataLoader` | Materias por profesor |

---

## 5. Errores de Negocio

| Código | Mensaje | Contexto |
|--------|---------|----------|
| `MAX_ENROLLMENTS` | "Máximo 3 materias permitidas" | enrollStudent |
| `SAME_PROFESSOR` | "Ya tienes materia con este profesor" | enrollStudent |
| `DUPLICATE_EMAIL` | "Email ya registrado" | createStudent |
| `NOT_FOUND` | "Estudiante no encontrado" | updateStudent |
