DEV Community

Insight 105
Insight 105

Posted on • Edited on

Quiz Management & Question Bank

After the authentication and authorization system is implemented, the primary activity in a Learning Management System (LMS) is creating and managing exam content. At this stage, the system begins to handle complex and interrelated data.

In this chapter, we will discuss how Academic Suite manages quizzes, questions, and answer options on the backend, including advanced CRUD design challenges often encountered in real-world applications.


4.1 Relationship Model of Quiz, Question, and Option

Conceptually, the quiz data structure can be described as follows:

  • One Quiz has many Questions
  • One Question has many Options
  • One Option can be marked as the correct answer

These nested relations appear simple during initial data creation but become significantly more complex when entering the editing process.


4.2 Main Challenge: Nested Updates

One of the biggest challenges in CRUD applications is handling Complex Data Editing features.

Imagine the following scenario:

  • A teacher opens the quiz editor
  • Changes the quiz title
  • Deletes question number 2
  • Edits the text of question number 5
  • Adds a new question at the end

All these changes are sent to the backend in a single click of the Save button.

The question is: how should the backend handle this condition?

Two Common Approaches

  1. Partial Update (Diffing) The backend tries to compare the old data and new data, then only updates the changed parts.
*   Complex
*   Hard to maintain
*   Prone to bugs in nested relations
Enter fullscreen mode Exit fullscreen mode
  1. Full Replacement The backend deletes all old data and then re-inserts the new data.
*   Simple implementation
*   Risk of losing data references
Enter fullscreen mode Exit fullscreen mode

4.3 Academic Suite Strategy: Modified Full Replacement

Academic Suite uses a Modified Full Replacement approach wrapped in a database transaction.

The goal is to maintain a balance between:

  • Implementation simplicity
  • Data consistency
  • Safe saving process

The entire update process is performed atomically: if one step fails, all changes are rolled back.

UpdateQuiz Implementation

This logic is implemented in handlers/quiz.go:

err := database.DB.Transaction(func(tx *gorm.DB) error {
    // 1. Update main quiz data (title, duration, etc.)
    if err := tx.Save(&quiz).Error; err != nil {
        return err
    }

    // 2. Delete all old questions
    tx.Delete(&models.Question{}, "quiz_id = ?", id)

    // 3. Re-insert questions from request
    for _, q := range req.Questions {
        q.QuizID = id
        tx.Create(&q)
    }

    return nil
})
Enter fullscreen mode Exit fullscreen mode

This approach ensures that the stored quiz structure is always consistent with the data sent by the frontend.


Critical Note: Risk of Question Deletion

The delete and recreate approach has important consequences:

  • Question IDs will change every time the quiz is edited
  • If the answers or attempts tables reference question_id, student grade data can be corrupted

Best Practice

In a production system:

  • Quizzes that are already Active or have Attempts should be locked (frozen)
  • Another alternative is to use soft delete or versioning for questions

This approach prevents changes to the quiz structure that could damage exam history.


4.4 Question Import Feature from Excel

To increase teacher productivity, Academic Suite provides a question import feature from Excel files.

Typing dozens of questions via a web form is inefficient, so the .xlsx format becomes a practical solution.

Library used:

  • github.com/xuri/excelize/v2

Excel Data Validation Challenges

Excel files are very free-form:

  • Columns can be empty
  • Formatting can be inconsistent
  • Correct answers can be in the wrong position

Therefore, the backend performs validation row by row.

for i, row := range rows {
    if len(row) < 7 {
        errors = append(errors, fmt.Sprintf(
            "Row %d: Incomplete columns",
            i+1,
        ))
        continue
    }

    // Further parsing and validation
}
Enter fullscreen mode Exit fullscreen mode

This approach allows:

  • Valid rows to still be saved
  • Problematic rows to be reported specifically to the user

The user experience becomes better without sacrificing data consistency.


4.5 Data Fetching with Eager Loading

To display a quiz along with all questions and answer options, Academic Suite uses the eager loading feature from GORM.

// Fetch Quiz + Questions + Options in a single query
database.DB.
    Preload("Questions").
    Preload("Questions.Options").
    First(&quiz, "id = ?", id)
Enter fullscreen mode Exit fullscreen mode

This approach prevents the N+1 Query Problem, where the application has to run many additional queries for interrelated data.

With eager loading, application performance remains stable even if the number of questions in the quiz is quite large.


Chapter Summary

In this chapter, we built the foundation of a stable and scalable Question Bank and Quiz Management system.

We discussed:

  • Challenges of nested updates
  • Transaction strategies to maintain data consistency
  • Risks of editing active quizzes
  • User-friendly Excel import features
  • Eager loading techniques for optimal performance

In Chapter 5, we will enter the heart of the online exam system: the Exam Engine, including timer settings, exam status, and precise grading mechanisms.

Top comments (0)