diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java
index 0f69716043..eb708c73bd 100644
--- a/src/main/java/nextstep/courses/domain/Course.java
+++ b/src/main/java/nextstep/courses/domain/Course.java
@@ -9,6 +9,8 @@ public class Course {
 
     private Long creatorId;
 
+    private final Sessions sessions = new Sessions();
+
     private LocalDateTime createdAt;
 
     private LocalDateTime updatedAt;
@@ -40,6 +42,11 @@ public LocalDateTime getCreatedAt() {
         return createdAt;
     }
 
+    public void addSession(Session session) {
+        session.toCourse(this);
+        sessions.add(session);
+    }
+
     @Override
     public String toString() {
         return "Course{" +
diff --git a/src/main/java/nextstep/courses/domain/CoverImage.java b/src/main/java/nextstep/courses/domain/CoverImage.java
new file mode 100644
index 0000000000..b640e02a7a
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/CoverImage.java
@@ -0,0 +1,14 @@
+package nextstep.courses.domain;
+
+public class CoverImage {
+
+    private final CoverImageFileSize size;
+    private final CoverImageType type;
+    private final CoverImageResolution resolution;
+
+    CoverImage(CoverImageType type, long size, int width, int height) {
+        this.size = new CoverImageFileSize(size);
+        this.type = type;
+        this.resolution = new CoverImageResolution(width, height);
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/CoverImageFactory.java b/src/main/java/nextstep/courses/domain/CoverImageFactory.java
new file mode 100644
index 0000000000..28466e1363
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/CoverImageFactory.java
@@ -0,0 +1,9 @@
+package nextstep.courses.domain;
+
+public class CoverImageFactory {
+
+    public static CoverImage ofGif(long size, int width, int height) {
+        return new CoverImage(CoverImageType.GIF, size, width, height);
+    }
+
+}
diff --git a/src/main/java/nextstep/courses/domain/CoverImageFileSize.java b/src/main/java/nextstep/courses/domain/CoverImageFileSize.java
new file mode 100644
index 0000000000..f2899ac5c5
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/CoverImageFileSize.java
@@ -0,0 +1,21 @@
+package nextstep.courses.domain;
+
+public class CoverImageFileSize {
+    private static final long MAX_SIZE = 1024 * 1024; // 1MB
+    private final long size;
+
+    public CoverImageFileSize(long size) {
+        validate(size);
+        this.size = size;
+    }
+
+    private static void validate(long size) {
+        if (size <= 0 || size > MAX_SIZE) {
+            throw new IllegalArgumentException("이미지 크기는 0보다 크고 1MB 이하여야 합니다.");
+        }
+    }
+
+    public long getSize() {
+        return size;
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/CoverImageResolution.java b/src/main/java/nextstep/courses/domain/CoverImageResolution.java
new file mode 100644
index 0000000000..2ff63cec9c
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/CoverImageResolution.java
@@ -0,0 +1,38 @@
+package nextstep.courses.domain;
+
+public class CoverImageResolution {
+    private static final int MIN_WIDTH = 300;
+    private static final int MIN_HEIGHT = 200;
+    private static final int ASPECT_RATIO_WIDTH = 3;
+    private static final int ASPECT_RATIO_HEIGHT = 2;
+
+    private final int width;
+    private final int height;
+
+    public CoverImageResolution(int width, int height) {
+        validate(width, height);
+        this.width = width;
+        this.height = height;
+    }
+
+    private static void validate(int width, int height) {
+        validateWidthAndHeight(width, height);
+        validateAspectRatioValid(width, height);
+    }
+
+    private static void validateWidthAndHeight(int width, int height) {
+        boolean isValid = width >= MIN_WIDTH && height >= MIN_HEIGHT;
+
+        if (!isValid) {
+            throw new IllegalArgumentException("이미지의 width는 300픽셀, height는 200픽셀 이상이어야 합니다.");
+        }
+    }
+
+    private static void validateAspectRatioValid(int width, int height) {
+        boolean isValid = width * ASPECT_RATIO_HEIGHT == height * ASPECT_RATIO_WIDTH;
+
+        if (!isValid) {
+            throw new IllegalArgumentException("width와 height의 비율은 3:2여야 합니다.");
+        }
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/CoverImageType.java b/src/main/java/nextstep/courses/domain/CoverImageType.java
new file mode 100644
index 0000000000..1bc185ec41
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/CoverImageType.java
@@ -0,0 +1,8 @@
+package nextstep.courses.domain;
+
+public enum CoverImageType {
+    GIF,
+    JPG,
+    PNG,
+    SVG
+}
diff --git a/src/main/java/nextstep/courses/domain/FreeRegistrationPolicy.java b/src/main/java/nextstep/courses/domain/FreeRegistrationPolicy.java
new file mode 100644
index 0000000000..bde34b9eb1
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/FreeRegistrationPolicy.java
@@ -0,0 +1,9 @@
+package nextstep.courses.domain;
+
+public class FreeRegistrationPolicy implements RegistrationPolicy {
+
+    @Override
+    public void validateRegistration(Session session, Money paymentAmount) {
+        // 무조건 패스
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/Money.java b/src/main/java/nextstep/courses/domain/Money.java
new file mode 100644
index 0000000000..e165eae831
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/Money.java
@@ -0,0 +1,28 @@
+package nextstep.courses.domain;
+
+import java.util.Objects;
+
+public class Money {
+    private final NaturalNumber value;
+
+    public Money(long amount) {
+        this.value = new NaturalNumber(amount);
+    }
+
+    public long getAmount() {
+        return value.getValue();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Money money = (Money) o;
+        return Objects.equals(value, money.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value);
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/NaturalNumber.java b/src/main/java/nextstep/courses/domain/NaturalNumber.java
new file mode 100644
index 0000000000..33521c8965
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/NaturalNumber.java
@@ -0,0 +1,50 @@
+package nextstep.courses.domain;
+
+import java.util.Objects;
+
+public class NaturalNumber implements Comparable<NaturalNumber> {
+
+    private final long value;
+
+    public NaturalNumber(long value) {
+        validate(value);
+        this.value = value;
+    }
+
+    private static void validate(long value) {
+        if (value < 0) {
+            throw new IllegalArgumentException("0을 포함한 자연수만 허용 가능합니다.");
+        }
+    }
+
+    public long getValue() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NaturalNumber that = (NaturalNumber) o;
+        return value == that.value;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value);
+    }
+
+    @Override
+    public String toString() {
+        return getValue() + "";
+    }
+
+    @Override
+    public int compareTo(NaturalNumber o) {
+        return Long.compare(getValue(), o.getValue());
+    }
+
+    public int compareTo(int o) {
+        return compareTo(new NaturalNumber(o));
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/PaidRegistrationPolicy.java b/src/main/java/nextstep/courses/domain/PaidRegistrationPolicy.java
new file mode 100644
index 0000000000..8e870435cd
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/PaidRegistrationPolicy.java
@@ -0,0 +1,24 @@
+package nextstep.courses.domain;
+
+public class PaidRegistrationPolicy implements RegistrationPolicy {
+
+    private final Money sessionFee;
+    private final NaturalNumber maxStudentCount;
+
+    public PaidRegistrationPolicy(int sessionFee, int maxStudentCount) {
+        this.sessionFee = new Money(sessionFee);
+        this.maxStudentCount = new NaturalNumber(maxStudentCount);
+    }
+
+    @Override
+    public void validateRegistration(Session session, Money paymentAmount) {
+        if (!session.isStudentCountLessThan((int) maxStudentCount.getValue())) {
+            throw new IllegalArgumentException("강의 최대 수강 인원을 초과할 수 없습니다.");
+        }
+
+        if (!sessionFee.equals(paymentAmount)) {
+            throw new IllegalArgumentException("수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능합니다.");
+        }
+    }
+
+}
diff --git a/src/main/java/nextstep/courses/domain/RegistrationPolicy.java b/src/main/java/nextstep/courses/domain/RegistrationPolicy.java
new file mode 100644
index 0000000000..a40824812d
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/RegistrationPolicy.java
@@ -0,0 +1,5 @@
+package nextstep.courses.domain;
+
+public interface RegistrationPolicy {
+    void validateRegistration(Session session, Money paymentAmount);
+}
diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java
new file mode 100644
index 0000000000..03cef7eaf4
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/Session.java
@@ -0,0 +1,45 @@
+package nextstep.courses.domain;
+
+import nextstep.payments.domain.Payment;
+import nextstep.users.domain.NsUser;
+import nextstep.users.domain.NsUsers;
+
+public class Session {
+    private Long id;
+    private Course course;
+    private final NsUsers nsUsers = new NsUsers();
+    private CoverImage coverImage;
+    private SessionStatus sessionStatus;
+    private RegistrationPolicy registrationPolicy;
+    private SessionPeriod sessionPeriod;
+
+    Session(long id, CoverImage coverImage, SessionStatus sessionStatus, RegistrationPolicy registrationPolicy, SessionPeriod sessionPeriod) {
+        this.id = id;
+        this.coverImage = coverImage;
+        this.sessionStatus = sessionStatus;
+        this.registrationPolicy = registrationPolicy;
+        this.sessionPeriod = sessionPeriod;
+    }
+
+    public void toCourse(Course course) {
+        this.course = course;
+    }
+
+    public boolean isStudentCountLessThan(int count) {
+        return nsUsers.getSize() < count;
+    }
+
+    public Payment register(NsUser nsUser, Money paymentAmount) {
+        if (!sessionStatus.isRegistrable()) {
+            throw new IllegalStateException("수강신청이 불가능한 상태입니다.");
+        }
+
+        registrationPolicy.validateRegistration(this, paymentAmount);
+
+        nsUsers.add(nsUser);
+
+        return new Payment("", this, nsUser, paymentAmount);
+    }
+
+
+}
diff --git a/src/main/java/nextstep/courses/domain/SessionPeriod.java b/src/main/java/nextstep/courses/domain/SessionPeriod.java
new file mode 100644
index 0000000000..5e2de2af48
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/SessionPeriod.java
@@ -0,0 +1,15 @@
+package nextstep.courses.domain;
+
+import java.time.LocalDateTime;
+
+public class SessionPeriod {
+
+    private final LocalDateTime startedAt;
+    private final LocalDateTime endedAt;
+
+    public SessionPeriod(LocalDateTime startedAt, LocalDateTime endedAt) {
+        this.startedAt = startedAt;
+        this.endedAt = endedAt;
+    }
+
+}
diff --git a/src/main/java/nextstep/courses/domain/SessionStatus.java b/src/main/java/nextstep/courses/domain/SessionStatus.java
new file mode 100644
index 0000000000..cd7412ac9c
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/SessionStatus.java
@@ -0,0 +1,11 @@
+package nextstep.courses.domain;
+
+public enum SessionStatus {
+    PREPARING,
+    RECRUITING,
+    ENDED;
+
+    public boolean isRegistrable() {
+        return this == RECRUITING;
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/Sessions.java b/src/main/java/nextstep/courses/domain/Sessions.java
new file mode 100644
index 0000000000..7b94288b8b
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/Sessions.java
@@ -0,0 +1,24 @@
+package nextstep.courses.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Sessions {
+    private final List<Session> sessions;
+
+    public Sessions(List<Session> sessions) {
+        this.sessions = sessions;
+    }
+
+    public Sessions() {
+        this(new ArrayList<>());
+    }
+
+    public void add(Session session) {
+        sessions.add(session);
+    }
+
+    public List<Session> getSessions() {
+        return sessions;
+    }
+}
diff --git a/src/main/java/nextstep/payments/domain/Payment.java b/src/main/java/nextstep/payments/domain/Payment.java
index 57d833f851..3fe953b815 100644
--- a/src/main/java/nextstep/payments/domain/Payment.java
+++ b/src/main/java/nextstep/payments/domain/Payment.java
@@ -2,28 +2,44 @@
 
 import java.time.LocalDateTime;
 
+import nextstep.courses.domain.Money;
+import nextstep.courses.domain.Session;
+import nextstep.users.domain.NsUser;
+
 public class Payment {
     private String id;
 
-    // 결제한 강의 아이디
-    private Long sessionId;
+    // 결제한 강의
+    private Session session;
 
-    // 결제한 사용자 아이디
-    private Long nsUserId;
+    // 결제한 사용자
+    private NsUser nsUser;
 
     // 결제 금액
-    private Long amount;
+    private Money amount;
 
     private LocalDateTime createdAt;
 
     public Payment() {
     }
 
-    public Payment(String id, Long sessionId, Long nsUserId, Long amount) {
+    public Payment(String id, Session session, NsUser nsUser, Money amount) {
         this.id = id;
-        this.sessionId = sessionId;
-        this.nsUserId = nsUserId;
+        this.session = session;
+        this.nsUser = nsUser;
         this.amount = amount;
         this.createdAt = LocalDateTime.now();
     }
+
+    public Session getSession() {
+        return session;
+    }
+
+    public NsUser getNsUser() {
+        return nsUser;
+    }
+
+    public Money getAmount() {
+        return amount;
+    }
 }
diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java
index 04070abf19..333a3730d6 100644
--- a/src/main/java/nextstep/qna/domain/Answer.java
+++ b/src/main/java/nextstep/qna/domain/Answer.java
@@ -75,7 +75,7 @@ public DeleteHistory delete(NsUser loginUser) {
         validateDelete(loginUser);
         this.deleted = true;
         this.updatedDate = LocalDateTime.now();
-        return new DeleteHistory(ContentType.ANSWER, getId(), getWriter(), LocalDateTime.now());
+        return DeleteHistoryFactory.ofAnswer(getId(), loginUser, LocalDateTime.now());
     }
 
     @Override
diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java
new file mode 100644
index 0000000000..89b86b7fec
--- /dev/null
+++ b/src/main/java/nextstep/qna/domain/Answers.java
@@ -0,0 +1,24 @@
+package nextstep.qna.domain;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import nextstep.users.domain.NsUser;
+
+public class Answers {
+    private final List<Answer> answers = new ArrayList<>();
+
+    public void add(Answer answer) {
+        answers.add(answer);
+    }
+
+    public List<DeleteHistory> delete(NsUser loginUser) {
+        return answers
+            .stream()
+            .map(answer -> answer.delete(loginUser))
+            .collect(Collectors.toList());
+    }
+
+}
diff --git a/src/main/java/nextstep/qna/domain/DeleteHistory.java b/src/main/java/nextstep/qna/domain/DeleteHistory.java
index 596b6ee9c4..d5b84ce75d 100644
--- a/src/main/java/nextstep/qna/domain/DeleteHistory.java
+++ b/src/main/java/nextstep/qna/domain/DeleteHistory.java
@@ -16,7 +16,7 @@ public class DeleteHistory {
 
     private LocalDateTime createdDate = LocalDateTime.now();
 
-    public DeleteHistory(ContentType contentType, Long contentId, NsUser deletedBy, LocalDateTime createdDate) {
+    DeleteHistory(ContentType contentType, Long contentId, NsUser deletedBy, LocalDateTime createdDate) {
         this.contentType = contentType;
         this.contentId = contentId;
         this.deletedBy = deletedBy;
diff --git a/src/main/java/nextstep/qna/domain/DeleteHistoryFactory.java b/src/main/java/nextstep/qna/domain/DeleteHistoryFactory.java
new file mode 100644
index 0000000000..10b8fbbcdd
--- /dev/null
+++ b/src/main/java/nextstep/qna/domain/DeleteHistoryFactory.java
@@ -0,0 +1,17 @@
+package nextstep.qna.domain;
+
+import java.time.LocalDateTime;
+
+import nextstep.users.domain.NsUser;
+
+public class DeleteHistoryFactory {
+
+    public static DeleteHistory ofQuestion(Long contentId, NsUser deletedBy, LocalDateTime createdDate) {
+        return new DeleteHistory(ContentType.QUESTION, contentId, deletedBy, createdDate);
+    }
+
+    public static DeleteHistory ofAnswer(Long contentId, NsUser deletedBy, LocalDateTime createdDate) {
+        return new DeleteHistory(ContentType.ANSWER, contentId, deletedBy, createdDate);
+    }
+
+}
diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java
index 3984b80466..e94d2c9b88 100644
--- a/src/main/java/nextstep/qna/domain/Question.java
+++ b/src/main/java/nextstep/qna/domain/Question.java
@@ -6,7 +6,6 @@
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.stream.Collectors;
 
 public class Question {
     private Long id;
@@ -17,7 +16,7 @@ public class Question {
 
     private NsUser writer;
 
-    private List<Answer> answers = new ArrayList<>();
+    private final Answers answers = new Answers();
 
     private boolean deleted = false;
 
@@ -65,7 +64,7 @@ public boolean isDeleted() {
         return deleted;
     }
 
-    public List<Answer> getAnswers() {
+    public Answers getAnswers() {
         return answers;
     }
 
@@ -73,13 +72,7 @@ public DeleteHistories delete(NsUser loginUser) {
         List<DeleteHistory> deleteHistories = new ArrayList<>();
 
         deleteHistories.add(deleteQuestion(loginUser));
-
-        List<DeleteHistory> answerHistories = getAnswers()
-            .stream()
-            .map(ans -> ans.delete(loginUser))
-            .collect(Collectors.toList());
-
-        deleteHistories.addAll(answerHistories);
+        deleteHistories.addAll(getAnswers().delete(loginUser));
 
         return new DeleteHistories(deleteHistories);
     }
@@ -87,7 +80,7 @@ public DeleteHistories delete(NsUser loginUser) {
     private DeleteHistory deleteQuestion(NsUser loginUser) {
         validateDelete(loginUser);
         this.deleted = true;
-        return new DeleteHistory(ContentType.QUESTION, getId(), getWriter(), LocalDateTime.now());
+        return DeleteHistoryFactory.ofQuestion(getId(), loginUser, LocalDateTime.now());
     }
 
     private void validateDelete(NsUser loginUser) {
diff --git a/src/main/java/nextstep/users/domain/NsUsers.java b/src/main/java/nextstep/users/domain/NsUsers.java
new file mode 100644
index 0000000000..fdc4000ddd
--- /dev/null
+++ b/src/main/java/nextstep/users/domain/NsUsers.java
@@ -0,0 +1,29 @@
+package nextstep.users.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NsUsers {
+
+    private final List<NsUser> nsUsers;
+
+    public NsUsers() {
+        this(new ArrayList<>());
+    }
+
+    public NsUsers(List<NsUser> nsUsers) {
+        this.nsUsers = nsUsers;
+    }
+
+    public List<NsUser> getNsUsers() {
+        return nsUsers;
+    }
+
+    public void add(NsUser nsUser) {
+        nsUsers.add(nsUser);
+    }
+
+    public int getSize() {
+        return nsUsers.size();
+    }
+}
diff --git a/src/test/java/nextstep/courses/domain/CoverImageFileSizeTest.java b/src/test/java/nextstep/courses/domain/CoverImageFileSizeTest.java
new file mode 100644
index 0000000000..e2a6bb60f8
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/CoverImageFileSizeTest.java
@@ -0,0 +1,27 @@
+package nextstep.courses.domain;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.catchIllegalArgumentException;
+import static org.junit.jupiter.api.Assertions.*;
+
+class CoverImageFileSizeTest {
+
+    @Test
+    void 사이즈가_1MB가_초과되면_예외가_발생한다() {
+        IllegalArgumentException e = catchIllegalArgumentException(
+            () -> new CoverImageFileSize(1024 * 1024 + 1));
+
+        Assertions.assertThat(e).hasMessageContaining("이미지 크기는 0보다 크고 1MB 이하여야 합니다.");
+    }
+
+    @Test
+    void 사이즈가_0보다_작으면_예외가_발생한다() {
+        IllegalArgumentException e = catchIllegalArgumentException(
+            () -> new CoverImageFileSize(-1));
+
+        Assertions.assertThat(e).hasMessageContaining("이미지 크기는 0보다 크고 1MB 이하여야 합니다.");
+    }
+
+}
diff --git a/src/test/java/nextstep/courses/domain/CoverImageResolutionTest.java b/src/test/java/nextstep/courses/domain/CoverImageResolutionTest.java
new file mode 100644
index 0000000000..028c6ff24c
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/CoverImageResolutionTest.java
@@ -0,0 +1,34 @@
+package nextstep.courses.domain;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import static org.assertj.core.api.Assertions.catchIllegalArgumentException;
+
+class CoverImageResolutionTest {
+
+    @ParameterizedTest
+    @CsvSource(value = {
+        "1, 200",
+        "300, 1",
+        "1, 1"})
+    void 이미지의_width는_300픽셀_height는_200픽셀_이상이어야_합니다(int width, int height) {
+        IllegalArgumentException e
+            = catchIllegalArgumentException(() -> new CoverImageResolution(width, height));
+        Assertions.assertThat(e).hasMessage("이미지의 width는 300픽셀, height는 200픽셀 이상이어야 합니다.");
+    }
+
+    @ParameterizedTest
+    @CsvSource(value = {
+        "300, 201",
+        "301, 200",
+        "500, 1000"})
+    void 이미지의_width와_height의_비율은_3대2여야_합니다(int width, int height) {
+        IllegalArgumentException e
+            = catchIllegalArgumentException(() -> new CoverImageResolution(width, height));
+        Assertions.assertThat(e).hasMessage("width와 height의 비율은 3:2여야 합니다.");
+    }
+
+}
diff --git a/src/test/java/nextstep/courses/domain/CoverImageTest.java b/src/test/java/nextstep/courses/domain/CoverImageTest.java
new file mode 100644
index 0000000000..ea53d45a8e
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/CoverImageTest.java
@@ -0,0 +1,11 @@
+package nextstep.courses.domain;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CoverImageTest {
+
+    public static CoverImage createCoverImage1() {
+        return CoverImageFactory.ofGif(1024 * 1024, 300, 200);
+    }
+
+}
diff --git a/src/test/java/nextstep/courses/domain/NaturalNumberTest.java b/src/test/java/nextstep/courses/domain/NaturalNumberTest.java
new file mode 100644
index 0000000000..327ba85e7f
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/NaturalNumberTest.java
@@ -0,0 +1,26 @@
+package nextstep.courses.domain;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.*;
+
+
+class NaturalNumberTest {
+
+    @ParameterizedTest
+    @ValueSource(ints = {-1, -2, -3})
+    void _0을_포함한_자연수가_아닐_경우_예외가_발생한다(int number) {
+        IllegalArgumentException e
+            = catchIllegalArgumentException(() -> new NaturalNumber(number));
+
+        assertThat(e).hasMessageContaining("0을 포함한 자연수만 허용 가능합니다.");
+    }
+
+    @ParameterizedTest
+    @ValueSource(ints = {0, 1})
+    void _0을_포함한_자연수만_생성이_가능하다(int number) {
+        new NaturalNumber(number);
+    }
+
+}
diff --git a/src/test/java/nextstep/courses/domain/PaidRegistrationPolicyTest.java b/src/test/java/nextstep/courses/domain/PaidRegistrationPolicyTest.java
new file mode 100644
index 0000000000..ee14e33a72
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/PaidRegistrationPolicyTest.java
@@ -0,0 +1,50 @@
+package nextstep.courses.domain;
+
+import java.time.LocalDateTime;
+
+import org.junit.jupiter.api.Test;
+
+import nextstep.users.domain.NsUserTest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchIllegalArgumentException;
+import static org.junit.jupiter.api.Assertions.*;
+
+class PaidRegistrationPolicyTest {
+
+    @Test
+    void 유료_강의는_강의_최대_수강_인원을_초과할_수_없다() {
+        int sessionFee = 20000;
+        int maxStudentCount = 1;
+
+        Session session = new SessionBuilder()
+                .paid(sessionFee, maxStudentCount)
+                .sessionStatus(SessionStatus.RECRUITING)
+                .build();
+
+        IllegalArgumentException e = catchIllegalArgumentException(() -> {
+            session.register(NsUserTest.JAVAJIGI, new Money(sessionFee));
+            session.register(NsUserTest.SANJIGI, new Money(sessionFee));
+        });
+
+        assertThat(e).hasMessage("강의 최대 수강 인원을 초과할 수 없습니다.");
+    }
+
+    @Test
+    void 유료_강의는_수강생이_결제한_금액과_수강료가_일치할_때_수강_신청이_가능하다() {
+        int sessionFee = 20000;
+        int maxStudentCount = 1;
+
+        Session session = new SessionBuilder()
+                .paid(sessionFee, maxStudentCount)
+                .sessionStatus(SessionStatus.RECRUITING)
+                .build();
+
+        IllegalArgumentException e = catchIllegalArgumentException(() -> {
+            session.register(NsUserTest.JAVAJIGI, new Money(sessionFee - 1));
+        });
+
+        assertThat(e).hasMessage("수강생이 결제한 금액과 수강료가 일치할 때 수강 신청이 가능합니다.");
+    }
+
+}
diff --git a/src/test/java/nextstep/courses/domain/SessionBuilder.java b/src/test/java/nextstep/courses/domain/SessionBuilder.java
new file mode 100644
index 0000000000..2c9065ee41
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/SessionBuilder.java
@@ -0,0 +1,61 @@
+package nextstep.courses.domain;
+
+public class SessionBuilder {
+    private Long id = 1L;
+    private Course course;
+    private CoverImage coverImage = CoverImageTest.createCoverImage1();
+    private SessionStatus sessionStatus;
+    private RegistrationPolicy registrationPolicy;
+    private SessionPeriod sessionPeriod = SessionPeriodTest.createSessionPeriod1();
+
+    public SessionBuilder id(Long id) {
+        this.id = id;
+        return this;
+    }
+
+    public SessionBuilder course(Course course) {
+        this.course = course;
+        return this;
+    }
+
+    public SessionBuilder coverImage(CoverImage coverImage) {
+        this.coverImage = coverImage;
+        return this;
+    }
+
+    public SessionBuilder sessionStatus(SessionStatus sessionStatus) {
+        this.sessionStatus = sessionStatus;
+        return this;
+    }
+
+    public SessionBuilder paid(int sessionFee, int maxStudentCount) {
+        this.registrationPolicy = new PaidRegistrationPolicy(sessionFee, maxStudentCount);
+        return this;
+    }
+
+    public SessionBuilder free() {
+        this.registrationPolicy = new FreeRegistrationPolicy();
+        return this;
+    }
+
+    public SessionBuilder sessionPeriod(SessionPeriod sessionPeriod) {
+        this.sessionPeriod = sessionPeriod;
+        return this;
+    }
+
+    public Session build() {
+        Session session = new Session(
+                id,
+                coverImage,
+                sessionStatus,
+                registrationPolicy,
+                sessionPeriod
+        );
+
+        if (course != null) {
+            session.toCourse(course);
+        }
+
+        return session;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/nextstep/courses/domain/SessionPeriodTest.java b/src/test/java/nextstep/courses/domain/SessionPeriodTest.java
new file mode 100644
index 0000000000..c7dc871c0d
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/SessionPeriodTest.java
@@ -0,0 +1,15 @@
+package nextstep.courses.domain;
+
+import java.time.LocalDateTime;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SessionPeriodTest {
+
+    public static SessionPeriod createSessionPeriod1() {
+        LocalDateTime startedAt = LocalDateTime.of(2023, 10, 1, 0, 0, 0);
+        LocalDateTime endedAt = LocalDateTime.of(2023, 10, 1, 23, 0, 0);
+        return new SessionPeriod(startedAt, endedAt);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java
new file mode 100644
index 0000000000..df34768844
--- /dev/null
+++ b/src/test/java/nextstep/courses/domain/SessionTest.java
@@ -0,0 +1,58 @@
+package nextstep.courses.domain;
+
+import java.time.LocalDateTime;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import nextstep.payments.domain.Payment;
+import nextstep.users.domain.NsUser;
+import nextstep.users.domain.NsUserTest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+
+class SessionTest {
+
+    @ParameterizedTest
+    @EnumSource(value = SessionStatus.class, mode = EnumSource.Mode.EXCLUDE, names = "RECRUITING")
+    void 강의_수강신청은_강의_상태가_모집중일_때만_가능하다(SessionStatus sessionStatus) {
+        int sessionFee = 20000;
+        int maxStudentCount = 1;
+
+        Session session = new SessionBuilder()
+                .paid(sessionFee, maxStudentCount)
+                .sessionStatus(sessionStatus)
+                .build();
+
+        IllegalStateException e = assertThrows(IllegalStateException.class,
+            () -> session.register(NsUserTest.JAVAJIGI, new Money(sessionFee)));
+
+        assertThat(e).hasMessage("수강신청이 불가능한 상태입니다.");
+    }
+
+    @Test
+    void register하면_Payment객체를_응답한다() {
+        int sessionFee = 20000;
+        int maxStudentCount = 1;
+        NsUser loginUser = NsUserTest.JAVAJIGI;
+
+        Session session = new SessionBuilder()
+                .paid(sessionFee, maxStudentCount)
+                .sessionStatus(SessionStatus.RECRUITING)
+                .build();
+
+        Payment payment = session.register(loginUser, new Money(sessionFee));
+
+        assertThat(payment.getSession()).isEqualTo(session);
+        assertThat(payment.getNsUser()).isEqualTo(loginUser);
+        assertThat(payment.getAmount()).isEqualTo(new Money(sessionFee));
+    }
+
+
+
+}
diff --git a/src/test/java/nextstep/qna/service/QnaServiceTest.java b/src/test/java/nextstep/qna/service/QnaServiceTest.java
index cb5a60a448..23227010d4 100644
--- a/src/test/java/nextstep/qna/service/QnaServiceTest.java
+++ b/src/test/java/nextstep/qna/service/QnaServiceTest.java
@@ -83,8 +83,8 @@ public void setUp() throws Exception {
 
     private void verifyDeleteHistories() {
         List<DeleteHistory> deleteHistories = Arrays.asList(
-                new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(), LocalDateTime.now()),
-                new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()));
+                DeleteHistoryFactory.ofQuestion(question.getId(), question.getWriter(), LocalDateTime.now()),
+                DeleteHistoryFactory.ofAnswer(answer.getId(), answer.getWriter(), LocalDateTime.now()));
         verify(deleteHistoryService).saveAll(deleteHistories);
     }
 }