diff --git a/exam/question_1/.gitignore b/exam/question_1/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b6266152c01ee0fdbea0ab8348ed5651c8fe068b
--- /dev/null
+++ b/exam/question_1/.gitignore
@@ -0,0 +1,31 @@
+### IntelliJ IDEA ###
+/.idea
+question_1.iml
+out/
+!**/src/main/**/out/
+!**/src/test/**/out/
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+bin/
+!**/src/main/**/bin/
+!**/src/test/**/bin/
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/exam/question_1/src/Major.java b/exam/question_1/src/Major.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b2d8a8c050c25e22fd4e59c04c55742645b5054
--- /dev/null
+++ b/exam/question_1/src/Major.java
@@ -0,0 +1,51 @@
+import java.util.Objects;
+
+/*
+ * @.classInvariant:
+ *     getId() != null
+ *     && getName() != null
+ */
+public class Major {
+    public record MajorId(Object id) {
+        public MajorId {
+            Objects.requireNonNull(id, "Major Id should not be null");
+        }
+    }
+
+    private final MajorId id;
+    private NotEmptyString name;
+
+    /*
+     * @.pre: true
+     * @.post: Major object is constructed, throw NullPointerException if one of id, name is null.
+     */
+    public Major(MajorId id, NotEmptyString name) {
+        this.id = Objects.requireNonNull(id, "Major Id should not be null");
+
+        setName(name);
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public MajorId getId() {
+        return id;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public NotEmptyString getName() {
+        return name;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: data is assigned, throw NullPointerException if name is null.
+     */
+    private void setName(NotEmptyString name) {
+        this.name = Objects.requireNonNull(name, "Major name should not be null");
+    }
+}
diff --git a/exam/question_1/src/NotEmptyString.java b/exam/question_1/src/NotEmptyString.java
new file mode 100644
index 0000000000000000000000000000000000000000..55a42639c382caf8f69c673f769adc3956386355
--- /dev/null
+++ b/exam/question_1/src/NotEmptyString.java
@@ -0,0 +1,26 @@
+/*
+ * @.classInvariant:
+ *     str() != null && !str.trim().isEmpty()
+ */
+public record NotEmptyString(String str) {
+    /*
+     * @.pre: true
+     * @.post: Object is constructed, throw NotEmptyStringException if string is empty
+     */
+    public NotEmptyString {
+        if (null == str || str.trim().isEmpty()) {
+            throw new NotEmptyStringException("String is null or empty.");
+        }
+    }
+
+    @Override
+    public String toString() {
+        return str;
+    }
+}
+
+class NotEmptyStringException extends RuntimeException {
+    public NotEmptyStringException(String message) {
+        super(message);
+    }
+}
diff --git a/exam/question_1/src/Student.java b/exam/question_1/src/Student.java
new file mode 100644
index 0000000000000000000000000000000000000000..905fe9a280b2899d637307bc6f0e4d9780088c0a
--- /dev/null
+++ b/exam/question_1/src/Student.java
@@ -0,0 +1,155 @@
+import java.util.Objects;
+
+/*
+ * @.classInvariant:
+ *     getId() != null
+ *     && getFirstName() != null
+ *     && getLastName() != null
+ *     && getEnrollmentYear() != null
+ *     && getMajor() != null
+ */
+public class Student {
+    public record StudentId(Object id) {
+        public StudentId {
+            Objects.requireNonNull(id, "Student Id should not be null");
+        }
+    }
+
+    public record StudentDto(StudentId id, NotEmptyString firstName, NotEmptyString lastName, Year enrollmentYear, Major major, Boolean isGraduated) {
+
+    }
+
+    private final StudentId id;
+    private NotEmptyString firstName;
+    private NotEmptyString lastName;
+    private Year enrollmentYear;
+    private Major major;
+    private boolean isGraduated;
+
+    /*
+     * @.pre: true
+     * @.post: Student object is constructed,
+     *      throw NullPointerException if one of id, firstName, lastName, enrollmentYear, major is null.
+     */
+    public Student(StudentId id, NotEmptyString firstName, NotEmptyString lastName, Year enrollmentYear, Major major, boolean isGraduated) {
+        this.id = Objects.requireNonNull(id, "Id should not be null");
+
+        setFirstName(firstName);
+        setLastName(lastName);
+        setEnrollmentYear(enrollmentYear);
+        setMajor(major);
+        setGraduated(isGraduated);
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public StudentId getId() {
+        return id;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public NotEmptyString getFirstName() {
+        return firstName;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public NotEmptyString getLastName() {
+        return lastName;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public Year getEnrollmentYear() {
+        return enrollmentYear;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT != null
+     */
+    public Major getMajor() {
+        return major;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: whether student is graduated or not
+     */
+    public boolean isGraduated() {
+        return isGraduated;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: data is updated if one of first name, last name, enrollment year, major, isGraduated is not null.
+     */
+    void updateData(StudentDto data) {
+        Objects.requireNonNull(data, "Student data should not be null");
+
+        if (null != data.firstName()) {
+            setFirstName(data.firstName());
+        }
+
+        if (null != data.lastName()) {
+            setLastName(data.lastName());
+        }
+
+        if (null != data.enrollmentYear()) {
+            setEnrollmentYear(data.enrollmentYear());
+        }
+
+        if (null != data.major()) {
+            setMajor(data.major());
+        }
+
+        if (null != data.isGraduated()) {
+            setGraduated(data.isGraduated());
+        }
+    }
+
+    /*
+     * @.pre: true
+     * @.post: data is assigned, throw NullPointerException if firstName is null.
+     */
+    private void setFirstName(NotEmptyString firstName) {
+        this.firstName = Objects.requireNonNull(firstName, "First name should not be null");
+    }
+
+    /*
+     * @.pre: true
+     * @.post: data is assigned, throw NullPointerException if lastName is null.
+     */
+    private void setLastName(NotEmptyString lastName) {
+        this.lastName = Objects.requireNonNull(lastName, "Last name should not be null");
+    }
+
+    /*
+     * @.pre: true
+     * @.post: data is assigned, throw NullPointerException if enrollmentYear is null.
+     */
+    private void setEnrollmentYear(Year enrollmentYear) {
+        this.enrollmentYear = Objects.requireNonNull(enrollmentYear, "Enrollment year should not be null");
+    }
+
+    /*
+     * @.pre: true
+     * @.post: data is assigned, throw NullPointerException if major is null.
+     */
+    private void setMajor(Major major) {
+        this.major = Objects.requireNonNull(major, "Major should not be null");
+    }
+
+    private void setGraduated(boolean graduated) {
+        isGraduated = graduated;
+    }
+}
diff --git a/exam/question_1/src/StudentRepository.java b/exam/question_1/src/StudentRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..736f1da0a42f9f84e2b4f085f84030ba5d559a83
--- /dev/null
+++ b/exam/question_1/src/StudentRepository.java
@@ -0,0 +1,28 @@
+import java.util.List;
+
+public interface StudentRepository {
+    /*
+     * @.pre: id != null
+     * @.post: RESULT != null,
+     *      throw StudentNotFoundException if student is not found
+     */
+    Student find(Student.StudentId id) throws StudentNotFoundException;
+
+    /*
+     * @.pre: criteria != null
+     * @.post: Students are returned based on the search criteria
+     */
+    List<Student> search(Student.StudentDto criteria);
+
+    /*
+     * @.pre: student != null
+     * @.post: student is updated
+     */
+    void update(Student student);
+}
+
+class StudentNotFoundException extends RuntimeException {
+    public StudentNotFoundException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/exam/question_1/src/StudentService.java b/exam/question_1/src/StudentService.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6552b01e2a67562b8aa3173d57d3eae83e6c902
--- /dev/null
+++ b/exam/question_1/src/StudentService.java
@@ -0,0 +1,49 @@
+import java.util.List;
+import java.util.Objects;
+
+public class StudentService {
+    private final UserProvider userProvider;
+    private final StudentRepository studentRepository;
+
+    public StudentService(UserProvider userProvider, StudentRepository studentRepository) {
+        this.userProvider = userProvider;
+        this.studentRepository = studentRepository;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: Students are returned based on the search criteria, throw SecurityException if user is not allowed to search
+     */
+    public List<Student> search(Student.StudentDto criteria) {
+        Objects.requireNonNull(criteria, "Criteria should not be null");
+
+        User user = userProvider.getUser();
+
+        if (!user.canSearch()) {
+            throw new SecurityException("User is not allowed to search students.");
+        }
+
+        return studentRepository.search(criteria);
+    }
+
+    /*
+     * @.pre: true
+     * @.post: Student is updated,
+     *      throw SecurityException if user is not allowed to update
+     */
+    public void update(Student.StudentDto dto) {
+        Objects.requireNonNull(dto, "Student data should not be null");
+        Objects.requireNonNull(dto.id(), "Student id should not be null");
+
+        Student student = studentRepository.find(dto.id());
+        User user = userProvider.getUser();
+
+        if (!user.canUpdate(student)) {
+            throw new SecurityException("User is not allowed to update student.");
+        }
+
+        student.updateData(dto);
+
+        studentRepository.update(student);
+    }
+}
diff --git a/exam/question_1/src/User.java b/exam/question_1/src/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..09b849bbd4497dd0ed9c8b8d41ae67389758f702
--- /dev/null
+++ b/exam/question_1/src/User.java
@@ -0,0 +1,23 @@
+public interface User {
+    /*
+     * @.pre: true
+     * @.post: whether user is a staff or not
+     */
+    boolean isStaff();
+
+    /*
+     * @.pre: true
+     * @.post: RESULT == true
+     */
+    default boolean canSearch() {
+        return true;
+    }
+
+    /*
+     * @.pre: true
+     * @.post: RESULT == true if user is a staff, else RESULT == false
+     */
+    default boolean canUpdate(Student student) {
+        return this.isStaff();
+    }
+}
diff --git a/exam/question_1/src/UserProvider.java b/exam/question_1/src/UserProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..48f0fb23b7445d642fce360100a110e7627fac75
--- /dev/null
+++ b/exam/question_1/src/UserProvider.java
@@ -0,0 +1,14 @@
+public interface UserProvider {
+    /*
+     * @.pre: true
+     * @.post: RESULT != null,
+     *      throw UserNotFoundException if there is no user
+     */
+    User getUser() throws UserNotFoundException;
+}
+
+class UserNotFoundException extends RuntimeException {
+    public UserNotFoundException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/exam/question_1/src/Year.java b/exam/question_1/src/Year.java
new file mode 100644
index 0000000000000000000000000000000000000000..c2ea489af516173515c84815201ff27b73c72dbd
--- /dev/null
+++ b/exam/question_1/src/Year.java
@@ -0,0 +1,21 @@
+/*
+ * @.classInvariant:
+ *     year() > 0 && year() <= 9999
+ */
+public record Year(int year) {
+    /*
+     * @.pre: true
+     * @.post: Object is constructed, throw InvalidYearException if year <= 0 || year > 9999
+     */
+    public Year {
+        if (year <= 0 || year > 9999) {
+            throw new InvalidYearException("Year is not valid");
+        }
+    }
+}
+
+class InvalidYearException extends RuntimeException {
+    public InvalidYearException(String message) {
+        super(message);
+    }
+}
\ No newline at end of file
diff --git a/exam/question_1/src/readme.md b/exam/question_1/src/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..965d8e0b38b578072f7f67cdb00f4a2c1aa94ef1
--- /dev/null
+++ b/exam/question_1/src/readme.md
@@ -0,0 +1,34 @@
+In the university's student records database, objects of type "Student" are stored. Assume that a student has typical fields such as name, enrollment year, major, etc.
+
+The university's students and staff can make queries in the student database to search for specific student records. Additionally, the database supports updates to the data by university staff. Why is this? For example, there may have been initial errors in the data, or it might be necessary to update a student's major, mark a student as graduated, etc.
+
+(In reality, the database would likely be stored on disk and use some type of database engine, but let's assume here that the database is always entirely in the memory of a Java process as simple objects, and the state of the objects is saved back to a file at the end of the day and reloaded into memory at the beginning of the next day. All data processing would thus simply take place with objects in the memory of the Java process.)
+
+## What principle of data protection would you choose in the case of the Student type when you want to serve the two different roles mentioned:
+
+    A student makes a search query and you want to offer the student a view that only supports reading the student records.
+    University staff want to permanently edit the information of a specific student.
+
+
+### Answer:
+The principle is: **Encapsulation and Integrity**.
+
+
+## Describe the mechanisms you choose and their consequences for class features, class invariant, and usage.
+
+### Answer:
+Based on the role, student and staff can view data, but only university staff can update the student, so:
+- Class features:
+  + Get student data.
+  + Update student data.
+- Class invariant: these data of Student should not be null:
+  + Id.
+  + First name.
+  + Last name.
+  + Enrollment year.
+  + Major.
+- We use `Class-Level Visibility Protection Modifiers` to limit the update on these methods of `Student`:
+  + **public**: `getId()`, `getFirstName()`, `getLastName()`, `getEnrollmentYear()`, `getMajor()`, `isGraduated()`.
+  + **package**: `updateData()`.
+  + **private**: `setFirstName()`, `setLastName()`, `setEnrollmentYear()`, `setMajor()`, `setGraduated()`.
+- Then we use a `StudentService` in the same package with `Student` to determine the current role of user, check permission and execute the action (for example: call `Student::updateData()` when a Staff sends the request to update a Student). 
\ No newline at end of file