diff --git a/src/main/java/id/ac/ui/cs/advprog/turorial0/controller/CourseController.java b/src/main/java/id/ac/ui/cs/advprog/turorial0/controller/CourseController.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb5cca447bc9c0182b3a4defd9c4e6c45ed5a32e
--- /dev/null
+++ b/src/main/java/id/ac/ui/cs/advprog/turorial0/controller/CourseController.java
@@ -0,0 +1,46 @@
+package id.ac.ui.cs.advprog.turorial0.controller;
+
+import id.ac.ui.cs.advprog.turorial0.exception.DuplicateCourseNameException;
+import id.ac.ui.cs.advprog.turorial0.model.Course;
+import id.ac.ui.cs.advprog.turorial0.service.CourseService;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Controller
+@RequestMapping("/course")
+public class CourseController {
+    @Autowired
+    private CourseService service;
+
+    @GetMapping("/list")
+    public String courseListPage(Model model) {
+        List<Course> allCourses = service.findAll();
+        model.addAttribute("courses", allCourses);
+        return "coursesList";
+    }
+
+    @GetMapping("/create")
+    public String createCoursePage(Model model) {
+        Course course = new Course();
+        model.addAttribute("course", course);
+        return "createCourse";
+    }
+
+    @PostMapping("/create")
+    public String createCoursePost(@ModelAttribute Course course, Model model) {
+        try {
+            service.create(course);
+        } catch (DuplicateCourseNameException e) {
+            model.addAttribute("error", e);
+            model.addAttribute("course", course);
+            return "createCourse";
+        }
+        return "redirect:list";
+    }
+}
+
diff --git a/src/main/java/id/ac/ui/cs/advprog/turorial0/exception/DuplicateCourseNameException.java b/src/main/java/id/ac/ui/cs/advprog/turorial0/exception/DuplicateCourseNameException.java
new file mode 100644
index 0000000000000000000000000000000000000000..5898d132c25dc5d94e657ec6c2170155f08c897d
--- /dev/null
+++ b/src/main/java/id/ac/ui/cs/advprog/turorial0/exception/DuplicateCourseNameException.java
@@ -0,0 +1,7 @@
+package id.ac.ui.cs.advprog.turorial0.exception;
+
+public class DuplicateCourseNameException extends RuntimeException {
+    public DuplicateCourseNameException(String courseName) {
+        super(String.format("The course name %s is a duplicate!", courseName));
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/id/ac/ui/cs/advprog/turorial0/model/Course.java b/src/main/java/id/ac/ui/cs/advprog/turorial0/model/Course.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a1dd2a0579fe65d8fe0c9921b5c85c3320d442c
--- /dev/null
+++ b/src/main/java/id/ac/ui/cs/advprog/turorial0/model/Course.java
@@ -0,0 +1,12 @@
+package id.ac.ui.cs.advprog.turorial0.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Course {
+    private String courseName;
+    private String courseId;
+    private boolean vacancyStatus = true;
+}
\ No newline at end of file
diff --git a/src/main/java/id/ac/ui/cs/advprog/turorial0/repository/CourseRepository.java b/src/main/java/id/ac/ui/cs/advprog/turorial0/repository/CourseRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..0da0b7abb1602aafdf464de12bf6d6b845c46715
--- /dev/null
+++ b/src/main/java/id/ac/ui/cs/advprog/turorial0/repository/CourseRepository.java
@@ -0,0 +1,30 @@
+package id.ac.ui.cs.advprog.turorial0.repository;
+
+import id.ac.ui.cs.advprog.turorial0.model.Course;
+import id.ac.ui.cs.advprog.turorial0.exception.DuplicateCourseNameException;
+import org.springframework.stereotype.Repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.*;
+
+@Repository
+public class CourseRepository {
+    private List<Course> courseList = new ArrayList<>();
+    private Map<String, Course> courseMap = new HashMap<>();
+
+    public Course create(Course course) {
+        if (courseMap.get(course.getCourseName()) == null) {
+            courseMap.put(course.getCourseName(), course);
+            courseList.add(course);
+            return course;
+        }
+
+        throw new DuplicateCourseNameException(course.getCourseName());
+    }
+
+    public Iterator<Course> findAll() {
+        return courseList.iterator();
+    }
+}
diff --git a/src/main/java/id/ac/ui/cs/advprog/turorial0/service/CourseService.java b/src/main/java/id/ac/ui/cs/advprog/turorial0/service/CourseService.java
new file mode 100644
index 0000000000000000000000000000000000000000..f6eeb25ceb13e393e030754966a381a822cde7be
--- /dev/null
+++ b/src/main/java/id/ac/ui/cs/advprog/turorial0/service/CourseService.java
@@ -0,0 +1,11 @@
+package id.ac.ui.cs.advprog.turorial0.service;
+
+import id.ac.ui.cs.advprog.turorial0.model.Course;
+
+import java.util.List;
+
+public interface CourseService {
+    public Course create(Course course);
+
+    public List<Course> findAll();
+}
diff --git a/src/main/java/id/ac/ui/cs/advprog/turorial0/service/CourseServiceImpl.java b/src/main/java/id/ac/ui/cs/advprog/turorial0/service/CourseServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..432ce0ea6d97b2583d0d7124e0267bdd1e602f48
--- /dev/null
+++ b/src/main/java/id/ac/ui/cs/advprog/turorial0/service/CourseServiceImpl.java
@@ -0,0 +1,40 @@
+package id.ac.ui.cs.advprog.turorial0.service;
+
+import id.ac.ui.cs.advprog.turorial0.model.Course;
+import id.ac.ui.cs.advprog.turorial0.repository.CourseRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+@Service
+public class CourseServiceImpl implements CourseService {
+    @Autowired
+    private CourseRepository courseRepo;
+
+    @Override
+    public Course create(Course course) {
+        courseRepo.create(course);
+        generateCourseId(course);
+        return course;
+    }
+
+    private void generateCourseId(Course course) {
+        StringBuilder stringBuilder = new StringBuilder();
+        for (char letter : course.getCourseName().toCharArray()) {
+            if (stringBuilder.toString().length() <= 30) {
+                stringBuilder.append(String.valueOf((int) letter));
+            }
+        }
+
+        course.setCourseId(stringBuilder.toString());
+    }
+
+    @Override
+    public List<Course> findAll() {
+        Iterator<Course> courseIterator = courseRepo.findAll();
+        List<Course> courseList = new ArrayList<>();
+        courseIterator.forEachRemaining(courseList::add);
+        return courseList;
+    }
+}
diff --git a/src/main/resources/templates/courseList.html b/src/main/resources/templates/courseList.html
new file mode 100644
index 0000000000000000000000000000000000000000..a5f68a3b3fa3cdd42c09f3f5cbf9dbd508f35d88
--- /dev/null
+++ b/src/main/resources/templates/courseList.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Course List</title>
+    <link
+            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
+            rel="stylesheet"
+            integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
+            crossorigin="anonymous"
+    >
+</head>
+<body class="p-2">
+
+<div class="w-50 mx-auto">
+    <h2>Course' List</h2>
+    <a th:href="@{/course/create}" class="btn btn-primary btn-sm mb-3">Create course</a>
+
+    <table class="table">
+        <thead>
+        <tr>
+            <th scope="col">Course ID</th>
+            <th scope="col">Course Name</th>
+            <th scope="col">Vacancy Status</th>
+        </tr>
+        </thead>
+        <tbody th:each="course: ${courses}">
+        <tr>
+            <td th:text="${course.courseId}"></td>
+            <td th:text="${course.courseName}"></td>
+            <td th:text="${course.vacancyStatus}"></td>
+        </tr>
+
+        </tbody>
+    </table>
+</div>
+
+<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
+        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
+        crossorigin="anonymous">
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/templates/createCourse.html b/src/main/resources/templates/createCourse.html
new file mode 100644
index 0000000000000000000000000000000000000000..4d21e90478c845ed9d845010284b1c7aeba9493e
--- /dev/null
+++ b/src/main/resources/templates/createCourse.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Create new Course</title>
+    <link
+            href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
+            rel="stylesheet"
+            integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
+            crossorigin="anonymous"
+    >
+</head>
+<body class="p-2">
+<div class="w-25 mx-auto">
+    <h3>Create new Course</h3>
+    <form th:action="@{/course/create}" th:object="${course}" method="post">
+        <div class="form-group">
+            <label for="nameInput">Course Name</label>
+            <input
+                    th:field="*{courseName}"
+                    type="text"
+                    class="form-control"
+                    id="nameInput"
+                    aria-describedby="nameHelp"
+                    placeholder="Enter course' name"
+                    required
+            />
+            <!--  bagian input field tersebut dipasangkan dengan field `name` pada `Student`.-->
+            <small id="nameHelp" class="form-text text-muted">Please enter unique course.</small>
+        </div>
+        <div class="form-group">
+            <label for="addressTextarea">Vacancy Status</label>
+            <textarea th:field="*{vacancyStatus}" class="form-control" id="addressTextarea" rows="3"></textarea>
+        </div>
+        <button type="submit" class="btn btn-primary my-2">Submit</button>
+    </form>
+</div>
+<script
+        src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
+        integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
+        crossorigin="anonymous">
+</script>
+</body>
+</html>
\ No newline at end of file