diff --git a/src/main/java/nextstep/courses/domain/Capacity.java b/src/main/java/nextstep/courses/domain/Capacity.java
new file mode 100644
index 000000000..0cc606ab2
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/Capacity.java
@@ -0,0 +1,22 @@
+package nextstep.courses.domain;
+
+import nextstep.courses.exception.BusinessInvalidValueException;
+
+public class Capacity {
+    private final int capacity;
+
+    public Capacity(int capacity) {
+        this.capacity = capacity;
+    }
+
+    public void validateCapacity(int count) {
+        if (count >= capacity) {
+            throw new BusinessInvalidValueException("최대수강인원을 초과했습니다.");
+        }
+    }
+
+    public int capacity() {
+         return capacity;
+    }
+}
+
diff --git a/src/main/java/nextstep/courses/domain/Course.java b/src/main/java/nextstep/courses/domain/Course.java
index 0f6971604..f76223d3a 100644
--- a/src/main/java/nextstep/courses/domain/Course.java
+++ b/src/main/java/nextstep/courses/domain/Course.java
@@ -1,25 +1,31 @@
 package nextstep.courses.domain;
 
+import nextstep.courses.utils.BaseEntity;
+
 import java.time.LocalDateTime;
 
-public class Course {
+public class Course extends BaseEntity {
     private Long id;
 
     private String title;
 
     private Long creatorId;
 
-    private LocalDateTime createdAt;
-
-    private LocalDateTime updatedAt;
-
     public Course() {
     }
 
+    public Course(Long id) {
+        this.id = id;
+    }
+
     public Course(String title, Long creatorId) {
         this(0L, title, creatorId, LocalDateTime.now(), null);
     }
 
+    public Course(Long id, String title, Long creatorId) {
+        this(id, title, creatorId, LocalDateTime.now(), null);
+    }
+
     public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, LocalDateTime updatedAt) {
         this.id = id;
         this.title = title;
@@ -28,6 +34,10 @@ public Course(Long id, String title, Long creatorId, LocalDateTime createdAt, Lo
         this.updatedAt = updatedAt;
     }
 
+    public Long id() {
+        return id;
+    }
+
     public String getTitle() {
         return title;
     }
diff --git a/src/main/java/nextstep/courses/domain/FreeSession.java b/src/main/java/nextstep/courses/domain/FreeSession.java
deleted file mode 100644
index 8ae90c460..000000000
--- a/src/main/java/nextstep/courses/domain/FreeSession.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package nextstep.courses.domain;
-
-import nextstep.users.domain.NsUser;
-
-import java.time.LocalDateTime;
-
-public class FreeSession extends Session {
-
-    public FreeSession(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course) {
-        super(id, beginDt, endDt, sessionCover, course);
-    }
-
-    @Override
-    public void enroll(NsUser participant, Long amount) {
-        validateStatus();
-        this.participants.add(participant);
-    }
-}
diff --git a/src/main/java/nextstep/courses/domain/PaidSession.java b/src/main/java/nextstep/courses/domain/PaidSession.java
deleted file mode 100644
index 195b646f5..000000000
--- a/src/main/java/nextstep/courses/domain/PaidSession.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package nextstep.courses.domain;
-
-import nextstep.courses.exception.BusinessInvalidValueException;
-import nextstep.users.domain.NsUser;
-
-import java.time.LocalDateTime;
-
-public class PaidSession extends Session {
-    private final int capacity;
-    private final long price;
-
-    public PaidSession(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, int capacity, long price) {
-        super(id, beginDt, endDt, sessionCover, course);
-        this.capacity = capacity;
-        this.price = price;
-    }
-
-    @Override
-    public void enroll(NsUser participant, Long amount) {
-        validateCapacity();
-        validatePrice(amount);
-        validateStatus();
-        this.participants.add(participant);
-    }
-
-    private void validateCapacity() {
-        if (this.participants.size() >= capacity) {
-            throw new BusinessInvalidValueException("최대수강인원을 초과했습니다.");
-        }
-    }
-
-    private void validatePrice(long price) {
-        if (this.price != price) {
-            throw new BusinessInvalidValueException("강의 가격이 변동되었습니다.");
-        }
-    }
-
-    public long price() {
-        return price;
-    }
-
-    public int capacity() {
-        return capacity;
-    }
-}
diff --git a/src/main/java/nextstep/courses/domain/Period.java b/src/main/java/nextstep/courses/domain/Period.java
index 4a24dacf9..219b3c412 100644
--- a/src/main/java/nextstep/courses/domain/Period.java
+++ b/src/main/java/nextstep/courses/domain/Period.java
@@ -10,4 +10,12 @@ public Period(LocalDateTime beginDt, LocalDateTime endDt) {
         this.beginDt = beginDt;
         this.endDt = endDt;
     }
+
+    public LocalDateTime getBeginDt() {
+        return beginDt;
+    }
+
+    public LocalDateTime getEndDt() {
+        return endDt;
+    }
 }
diff --git a/src/main/java/nextstep/courses/domain/Price.java b/src/main/java/nextstep/courses/domain/Price.java
new file mode 100644
index 000000000..c87140dd0
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/Price.java
@@ -0,0 +1,21 @@
+package nextstep.courses.domain;
+
+import nextstep.courses.exception.BusinessInvalidValueException;
+
+public class Price {
+    private final long price;
+
+    public Price(long price) {
+        this.price = price;
+    }
+
+    public void validatePrice(long price) {
+        if (this.price != price) {
+            throw new BusinessInvalidValueException("강의 가격이 변동되었습니다.");
+        }
+    }
+
+    public long price() {
+        return price;
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/Registration.java b/src/main/java/nextstep/courses/domain/Registration.java
index 4d0b22577..95e200cb6 100644
--- a/src/main/java/nextstep/courses/domain/Registration.java
+++ b/src/main/java/nextstep/courses/domain/Registration.java
@@ -1,13 +1,50 @@
 package nextstep.courses.domain;
 
+import nextstep.courses.utils.BaseEntity;
 import nextstep.users.domain.NsUser;
 
-public class Registration {
+import java.time.LocalDateTime;
 
-    public Registration() {
+public class Registration extends BaseEntity {
+    private Long id = null;
+    private final NsUser user;
+    private final Session session;
+    private final Long amount;
+
+    public Registration(Long id, NsUser user, Session session, Long amount, LocalDateTime createdAt, LocalDateTime updatedAt) {
+        this.id = id;
+        this.user = user;
+        this.session = session;
+        this.amount = amount;
+        this.createdAt = createdAt;
+        this.updatedAt = updatedAt;
+    }
+
+    public Registration(NsUser user, Session session, Long amount) {
+        this.user = user;
+        this.session = session;
+        this.amount = amount;
     }
 
-    public void register(NsUser user, Session session, Long amount) {
+    public static Registration register(NsUser user, Session session, Long amount) {
+        Registration registration = new Registration(user, session, amount);
         session.enroll(user, amount);
+        return registration;
+    }
+
+    public Long id() {
+        return id;
+    }
+
+    public NsUser nsUser() {
+        return user;
+    }
+
+    public Session session() {
+        return session;
+    }
+
+    public Long amount() {
+        return amount;
     }
 }
diff --git a/src/main/java/nextstep/courses/domain/Semester.java b/src/main/java/nextstep/courses/domain/Semester.java
new file mode 100644
index 000000000..082e64053
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/Semester.java
@@ -0,0 +1,11 @@
+package nextstep.courses.domain;
+
+import nextstep.courses.utils.BaseEntity;
+
+public class Semester extends BaseEntity {
+    private final Course course;
+
+    public Semester(Course course) {
+        this.course = course;
+    }
+}
diff --git a/src/main/java/nextstep/courses/domain/Session.java b/src/main/java/nextstep/courses/domain/Session.java
index 99e79af17..32e4d66b2 100644
--- a/src/main/java/nextstep/courses/domain/Session.java
+++ b/src/main/java/nextstep/courses/domain/Session.java
@@ -2,40 +2,117 @@
 
 import nextstep.courses.enums.SessionStatus;
 import nextstep.courses.exception.BusinessInvalidValueException;
+import nextstep.courses.utils.BaseEntity;
 import nextstep.users.domain.NsUser;
 
 import java.time.LocalDateTime;
-import java.util.ArrayList;
 import java.util.List;
 
-public abstract class Session {
-    final Long id;
-    final Course course;
-    final SessionCover sessionCover;
-    final Period period;
-    SessionStatus status = SessionStatus.PREPARE;
-    final List<NsUser> participants = new ArrayList<>();
+public class Session extends BaseEntity {
+    public static long FREE_PRICE = 0L;
+    public static int MAX_CAPACITY = Integer.MAX_VALUE;
+    private final Long id;
+    private Course course;
+    private SessionCover sessionCover;
+    private Period period;
+    private Capacity capacity;
+    private Price price;
 
-    public Session(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course) {
+    private SessionStatus status = SessionStatus.PREPARE;
+    private List<NsUser> participants;
+
+    public Session(Long id) {
+        this.id = id;
+    }
+
+    private Session(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, Capacity capacity, Price price, List<NsUser> participants) {
+        this.id = id;
+        this.period = new Period(beginDt, endDt);
+        this.sessionCover = sessionCover;
+        this.course = course;
+        this.capacity = capacity;
+        this.price = price;
+        this.participants = participants;
+    }
+
+    public Session(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionStatus status, Capacity capacity, Price price, Course course, SessionCover sessionCover, LocalDateTime createdAt, LocalDateTime updatedAt) {
         this.id = id;
         this.period = new Period(beginDt, endDt);
+        this.status = status;
         this.sessionCover = sessionCover;
         this.course = course;
+        this.capacity = capacity;
+        this.price = price;
+        this.createdAt = createdAt;
+        this.updatedAt = updatedAt;
     }
 
-    public abstract void enroll(NsUser participant, Long amount);
+    public static Session fromSessionForFree(Session session, SessionCover sessionCover, Course course, List<NsUser> participants) {
+        return Session.ofFree(session.id, session.period.getBeginDt(), session.period.getEndDt(), sessionCover, course, participants);
+    }
+
+    public static Session fromSessionForPaid(Session session, SessionCover sessionCover, Course course, List<NsUser> participants) {
+        return Session.ofPaid(session.id, session.period.getBeginDt(), session.period.getEndDt(), sessionCover, course, session.price.price(), session.capacity.capacity(), participants);
+    }
+
+    public static Session ofFree(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, List<NsUser> participants) {
+        return new Session(id, beginDt, endDt, sessionCover, course, new Capacity(MAX_CAPACITY), new Price(FREE_PRICE), participants);
+    }
+
+    public static Session ofPaid(Long id, LocalDateTime beginDt, LocalDateTime endDt, SessionCover sessionCover, Course course, Long price, Integer capacity, List<NsUser> participants) {
+        return new Session(id, beginDt, endDt, sessionCover, course, new Capacity(capacity), new Price(price), participants);
+    }
+
+    public static Session fromSession(Session session, SessionCover sessionCover, Course course, List<NsUser> participants) {
+        if (session.price.equals(0L)) {
+            return fromSessionForFree(session, sessionCover, course, participants);
+        }
+        return fromSessionForPaid(session, sessionCover, course, participants);
+    }
 
     public void startEnrollment() {
         this.status = SessionStatus.ENROLL;
     }
 
-    public void validateStatus() {
+    public void enroll(NsUser participant, Long amount) {
+        capacity.validateCapacity(participants.size());
+        price.validatePrice(amount);
+        validateStatus();
+        this.participants.add(participant);
+    }
+
+
+    private void validateStatus() {
         if (SessionStatus.ENROLL != this.status) {
             throw new BusinessInvalidValueException("수강신청 가능한 상태가 아닙니다.");
         }
     }
 
+    public Price price() {
+        return price;
+    }
+
+    public Capacity capacity() {
+        return capacity;
+    }
+
     public Long id() {
         return id;
     }
+
+    public Period period() {
+        return period;
+    }
+
+    public Course course() {
+        return course;
+    }
+
+    public SessionCover sessionCover() {
+        return sessionCover;
+    }
+
+    public String status() {
+        return this.status.name();
+    }
 }
diff --git a/src/main/java/nextstep/courses/domain/SessionCover.java b/src/main/java/nextstep/courses/domain/SessionCover.java
index 3f2822d5e..c9843c4f7 100644
--- a/src/main/java/nextstep/courses/domain/SessionCover.java
+++ b/src/main/java/nextstep/courses/domain/SessionCover.java
@@ -1,10 +1,31 @@
 package nextstep.courses.domain;
 
-public class SessionCover {
+import nextstep.courses.utils.BaseEntity;
+
+import java.time.LocalDateTime;
+
+public class SessionCover extends BaseEntity {
     public static final double RATIO = (double) 3 / 2;
-    private final byte[] image;
 
-    public SessionCover(int width, int height, long size, byte[] image) {
+    private Long id;
+    private byte[] image;
+
+    public SessionCover(Long id) {
+        this.id = id;
+    }
+
+    public SessionCover(byte[] image) {
+        this.image = image;
+    }
+
+    public SessionCover(Long id, byte[] image, LocalDateTime createdAt, LocalDateTime updatedAt) {
+        this.id = id;
+        this.image = image;
+        this.createdAt = createdAt;
+        this.updatedAt = updatedAt;
+    }
+
+    public SessionCover(long id, int width, int height, long size, byte[] image) {
         validateSize(size);
         validatePixel(width, height);
         validateRatio(width, height);
@@ -12,6 +33,7 @@ public SessionCover(int width, int height, long size, byte[] image) {
             throw new IllegalArgumentException("이미지 값이 없습니다.");
         }
         this.image = image;
+        this.id = id;
     }
 
     private static void validateSize(long size) {
@@ -35,4 +57,8 @@ private static void validateRatio(int width, int height) {
     public byte[] image() {
         return image;
     }
+
+    public Long id() {
+        return id;
+    }
 }
diff --git a/src/main/java/nextstep/courses/domain/Symester.java b/src/main/java/nextstep/courses/domain/Symester.java
deleted file mode 100644
index e9963f91f..000000000
--- a/src/main/java/nextstep/courses/domain/Symester.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package nextstep.courses.domain;
-
-public class Symester {
-    private final Course course;
-
-    public Symester(Course course) {
-        this.course = course;
-    }
-}
diff --git a/src/main/java/nextstep/courses/domain/CourseRepository.java b/src/main/java/nextstep/courses/domain/repository/CourseRepository.java
similarity index 54%
rename from src/main/java/nextstep/courses/domain/CourseRepository.java
rename to src/main/java/nextstep/courses/domain/repository/CourseRepository.java
index 6aaeb638d..675e42f31 100644
--- a/src/main/java/nextstep/courses/domain/CourseRepository.java
+++ b/src/main/java/nextstep/courses/domain/repository/CourseRepository.java
@@ -1,4 +1,6 @@
-package nextstep.courses.domain;
+package nextstep.courses.domain.repository;
+
+import nextstep.courses.domain.Course;
 
 public interface CourseRepository {
     int save(Course course);
diff --git a/src/main/java/nextstep/courses/domain/repository/RegistrationRepository.java b/src/main/java/nextstep/courses/domain/repository/RegistrationRepository.java
new file mode 100644
index 000000000..9dab72269
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/repository/RegistrationRepository.java
@@ -0,0 +1,11 @@
+package nextstep.courses.domain.repository;
+
+import nextstep.courses.domain.Registration;
+
+import java.util.List;
+
+public interface RegistrationRepository {
+    int save(Registration registration);
+
+    List<Registration> findAllBySessionId(Long sessionId);
+}
diff --git a/src/main/java/nextstep/courses/domain/repository/SessionCoverRepository.java b/src/main/java/nextstep/courses/domain/repository/SessionCoverRepository.java
new file mode 100644
index 000000000..582ea641f
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/repository/SessionCoverRepository.java
@@ -0,0 +1,9 @@
+package nextstep.courses.domain.repository;
+
+import nextstep.courses.domain.SessionCover;
+
+public interface SessionCoverRepository {
+    int save(SessionCover sessionCover);
+
+    SessionCover findById(Long id);
+}
diff --git a/src/main/java/nextstep/courses/domain/repository/SessionRepository.java b/src/main/java/nextstep/courses/domain/repository/SessionRepository.java
new file mode 100644
index 000000000..535cf1062
--- /dev/null
+++ b/src/main/java/nextstep/courses/domain/repository/SessionRepository.java
@@ -0,0 +1,11 @@
+package nextstep.courses.domain.repository;
+
+import nextstep.courses.domain.Session;
+
+import java.util.Optional;
+
+public interface SessionRepository {
+    int save(Session session);
+
+    Optional<Session> findById(Long id);
+}
diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java
index f9122cbe3..99c8b5922 100644
--- a/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java
+++ b/src/main/java/nextstep/courses/infrastructure/JdbcCourseRepository.java
@@ -1,13 +1,12 @@
 package nextstep.courses.infrastructure;
 
 import nextstep.courses.domain.Course;
-import nextstep.courses.domain.CourseRepository;
+import nextstep.courses.domain.repository.CourseRepository;
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.stereotype.Repository;
 
-import java.sql.Timestamp;
-import java.time.LocalDateTime;
+import static nextstep.courses.utils.DateUtil.toLocalDateTime;
 
 @Repository("courseRepository")
 public class JdbcCourseRepository implements CourseRepository {
@@ -35,10 +34,4 @@ public Course findById(Long id) {
         return jdbcTemplate.queryForObject(sql, rowMapper, id);
     }
 
-    private LocalDateTime toLocalDateTime(Timestamp timestamp) {
-        if (timestamp == null) {
-            return null;
-        }
-        return timestamp.toLocalDateTime();
-    }
 }
diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcRegistrationRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcRegistrationRepository.java
new file mode 100644
index 000000000..61b5c01ff
--- /dev/null
+++ b/src/main/java/nextstep/courses/infrastructure/JdbcRegistrationRepository.java
@@ -0,0 +1,45 @@
+package nextstep.courses.infrastructure;
+
+import nextstep.courses.domain.Registration;
+import nextstep.courses.domain.Session;
+import nextstep.courses.domain.repository.RegistrationRepository;
+import nextstep.users.domain.NsUser;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static nextstep.courses.utils.DateUtil.toLocalDateTime;
+
+@Repository("registrationRepository")
+public class JdbcRegistrationRepository implements RegistrationRepository {
+    private JdbcOperations jdbcTemplate;
+
+    public JdbcRegistrationRepository(JdbcOperations jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+    @Override
+    public int save(Registration registration) {
+        String sql = "insert into registration (ns_user_id, session_id, amount, created_at) values(?, ?, ?, ?)";
+        return jdbcTemplate.update(sql, registration.nsUser().getId(), registration.session().id(), registration.amount(), LocalDateTime.now());
+    }
+
+    @Override
+    public List<Registration> findAllBySessionId(Long sessionId) {
+        String sql = "select id, session_id, ns_user_id, amount, created_at, updated_at " +
+                "from registration r where session_id = ?";
+
+        RowMapper<Registration> rowMapper = (rs, rowNum) -> new Registration(
+                rs.getLong(1),
+                new NsUser(rs.getLong(2)),
+                new Session(rs.getLong(3)),
+                rs.getLong(4),
+                toLocalDateTime(rs.getTimestamp(5)),
+                toLocalDateTime(rs.getTimestamp(6)));
+
+        return jdbcTemplate.query(sql, rowMapper, sessionId);
+    }
+}
diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionCoverRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionCoverRepository.java
new file mode 100644
index 000000000..bc1de599c
--- /dev/null
+++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionCoverRepository.java
@@ -0,0 +1,38 @@
+package nextstep.courses.infrastructure;
+
+import nextstep.courses.domain.SessionCover;
+import nextstep.courses.domain.repository.SessionCoverRepository;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+
+import static nextstep.courses.utils.DateUtil.toLocalDateTime;
+
+@Repository("sessionCoverRepository")
+public class JdbcSessionCoverRepository implements SessionCoverRepository {
+    private final JdbcOperations jdbcTemplate;
+
+    public JdbcSessionCoverRepository(JdbcOperations jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+    @Override
+    public int save(SessionCover sessionCover) {
+        String sql = "insert into session_cover (image, created_at, updated_at) values(?,?,?)";
+        return jdbcTemplate.update(sql, sessionCover.image(), LocalDateTime.now(), LocalDateTime.now());
+    }
+
+    @Override
+    public SessionCover findById(Long id) {
+        String sql = "select id, image, created_at, updated_at from session_cover where id = ?";
+        RowMapper<SessionCover> rowMapper = (rs, rowNum) -> new SessionCover(
+                rs.getLong(1),
+                rs.getBytes(2),
+                toLocalDateTime(rs.getTimestamp(3)),
+                toLocalDateTime(rs.getTimestamp(4)));
+
+        return jdbcTemplate.queryForObject(sql, rowMapper, id);
+    }
+}
diff --git a/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java
new file mode 100644
index 000000000..c10eb3a94
--- /dev/null
+++ b/src/main/java/nextstep/courses/infrastructure/JdbcSessionRepository.java
@@ -0,0 +1,52 @@
+package nextstep.courses.infrastructure;
+
+import nextstep.courses.domain.*;
+import nextstep.courses.domain.repository.SessionRepository;
+import nextstep.courses.enums.SessionStatus;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+import static nextstep.courses.utils.DateUtil.toLocalDateTime;
+
+@Repository("sessionRepository")
+public class JdbcSessionRepository implements SessionRepository {
+
+    private JdbcOperations jdbcTemplate;
+
+    public JdbcSessionRepository(JdbcOperations jdbcTemplate) {
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+    @Override
+    public int save(Session session) {
+        String sql = "insert into session (begin_dt, end_dt, session_status, capacity, price, course_id, session_cover_id, created_at, updated_at) values(?, ?, ?, ?, ?, ?, ?, ? ,?)";
+        return jdbcTemplate.update(sql, session.period().getBeginDt(), session.period().getBeginDt(), session.status(), session.capacity().capacity(),
+                session.price().price(), session.course().id(), session.sessionCover().id(), LocalDateTime.now(), LocalDateTime.now());
+    }
+
+    @Override
+    public Optional<Session> findById(Long id) {
+        String sql = "select id, begin_dt, end_dt, session_status, capacity, price, " +
+                "course_id, session_cover_id, created_at, updated_at " +
+                "from session where id = ?";
+
+        RowMapper<Session> rowMapper = (rs, rowNum) -> new Session(
+                rs.getLong(1),
+                toLocalDateTime(rs.getTimestamp(2)),
+                toLocalDateTime(rs.getTimestamp(3)),
+                SessionStatus.valueOf(rs.getString(4)),
+                new Capacity(rs.getInt(5)),
+                new Price(rs.getInt(6)),
+                new Course(rs.getLong(7)),
+                new SessionCover(rs.getLong(8)),
+                toLocalDateTime(rs.getTimestamp(9)),
+                toLocalDateTime(rs.getTimestamp(10))
+        );
+
+        return Optional.of(jdbcTemplate.queryForObject(sql, rowMapper, id));
+    }
+}
diff --git a/src/main/java/nextstep/courses/service/SessionService.java b/src/main/java/nextstep/courses/service/SessionService.java
new file mode 100644
index 000000000..b69a191fe
--- /dev/null
+++ b/src/main/java/nextstep/courses/service/SessionService.java
@@ -0,0 +1,57 @@
+package nextstep.courses.service;
+
+import nextstep.courses.domain.Course;
+import nextstep.courses.domain.Registration;
+import nextstep.courses.domain.Session;
+import nextstep.courses.domain.SessionCover;
+import nextstep.courses.domain.repository.CourseRepository;
+import nextstep.courses.domain.repository.RegistrationRepository;
+import nextstep.courses.domain.repository.SessionCoverRepository;
+import nextstep.courses.domain.repository.SessionRepository;
+import nextstep.users.domain.NsUser;
+import nextstep.users.domain.UserRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class SessionService {
+    private final CourseRepository courseRepository;
+    private final RegistrationRepository registrationRepository;
+    private final SessionRepository sessionRepository;
+    private final UserRepository userRepository;
+    private final SessionCoverRepository sessionCoverRepository;
+
+    public SessionService(CourseRepository courseRepository, RegistrationRepository registrationRepository, SessionRepository sessionRepository, UserRepository userRepository, SessionCoverRepository sessionCoverRepository) {
+        this.courseRepository = courseRepository;
+        this.registrationRepository = registrationRepository;
+        this.sessionRepository = sessionRepository;
+        this.userRepository = userRepository;
+        this.sessionCoverRepository = sessionCoverRepository;
+    }
+
+    @Transactional
+    public void registerSession(String userId, Long sessionId, Long amount) {
+        NsUser user = userRepository.findByUserId(userId).orElseThrow(() -> new IllegalArgumentException("유효한 userId가 아닙니다."));
+        Session session = retrieveSession(sessionId);
+
+        registrationRepository.save(Registration.register(user, session, amount));
+    }
+
+    private Session retrieveSession(Long sessionId) {
+        Session session = sessionRepository.findById(sessionId).orElseThrow(() -> new IllegalArgumentException("유효한 sessionId가 아닙니다."));
+
+        Course course = courseRepository.findById(session.course().id());
+
+        SessionCover sessionCover = sessionCoverRepository.findById(session.sessionCover().id());
+
+        List<Registration> registrations = registrationRepository.findAllBySessionId(sessionId);
+        List<NsUser> users = userRepository.findAllByIds(registrations.stream().map(r -> r.nsUser().getId()).collect(Collectors.toList()));
+
+        return Session.fromSession(session, sessionCover, course, users);
+    }
+
+
+}
diff --git a/src/main/java/nextstep/courses/utils/BaseEntity.java b/src/main/java/nextstep/courses/utils/BaseEntity.java
new file mode 100644
index 000000000..56420fad1
--- /dev/null
+++ b/src/main/java/nextstep/courses/utils/BaseEntity.java
@@ -0,0 +1,17 @@
+package nextstep.courses.utils;
+
+import java.time.LocalDateTime;
+
+public class BaseEntity {
+    public LocalDateTime createdAt;
+
+    public LocalDateTime updatedAt;
+
+    public LocalDateTime createdAt() {
+        return createdAt;
+    }
+
+    public LocalDateTime updatedAt() {
+        return updatedAt;
+    }
+}
diff --git a/src/main/java/nextstep/courses/utils/DateUtil.java b/src/main/java/nextstep/courses/utils/DateUtil.java
new file mode 100644
index 000000000..0d5b91174
--- /dev/null
+++ b/src/main/java/nextstep/courses/utils/DateUtil.java
@@ -0,0 +1,13 @@
+package nextstep.courses.utils;
+
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+
+public class DateUtil {
+    public static LocalDateTime toLocalDateTime(Timestamp timestamp) {
+        if (timestamp == null) {
+            return null;
+        }
+        return timestamp.toLocalDateTime();
+    }
+}
diff --git a/src/main/java/nextstep/courses/utils/RasterProcessor.java b/src/main/java/nextstep/courses/utils/RasterProcessor.java
index 7462317cb..4373d6fa2 100644
--- a/src/main/java/nextstep/courses/utils/RasterProcessor.java
+++ b/src/main/java/nextstep/courses/utils/RasterProcessor.java
@@ -18,7 +18,7 @@ public SessionCover processImage(File file) throws IOException {
             throw new FileNotFoundException("Invalid image file.");
         }
 
-        return new SessionCover(image.getWidth(), image.getHeight(), file.length(), writeRasterImage(image));
+        return new SessionCover(2L, image.getWidth(), image.getHeight(), file.length(), writeRasterImage(image));
     }
 
     private static byte[] writeRasterImage(BufferedImage image) throws IOException {
diff --git a/src/main/java/nextstep/courses/utils/VectorProcessor.java b/src/main/java/nextstep/courses/utils/VectorProcessor.java
index 855b896ff..a447b4e0a 100644
--- a/src/main/java/nextstep/courses/utils/VectorProcessor.java
+++ b/src/main/java/nextstep/courses/utils/VectorProcessor.java
@@ -17,7 +17,7 @@ public class VectorProcessor implements ImageProcessor {
     @Override
     public SessionCover processImage(File file) throws IOException {
         Element svgRoot = parseXML(file);
-        return new SessionCover(parseInt(svgRoot.getAttribute("width")), parseInt(svgRoot.getAttribute("height")), file.length(), writeVectorImage(file));
+        return new SessionCover(1L, parseInt(svgRoot.getAttribute("width")), parseInt(svgRoot.getAttribute("height")), file.length(), writeVectorImage(file));
     }
 
     private int parseInt(String pixel) {
diff --git a/src/main/java/nextstep/payments/service/PaymentService.java b/src/main/java/nextstep/payments/service/PaymentService.java
index 7bfc4d357..e43203c2d 100644
--- a/src/main/java/nextstep/payments/service/PaymentService.java
+++ b/src/main/java/nextstep/payments/service/PaymentService.java
@@ -1,14 +1,14 @@
 package nextstep.payments.service;
 
-import nextstep.courses.domain.Registration;
 import nextstep.courses.domain.Session;
 import nextstep.payments.domain.Payment;
 import nextstep.users.domain.NsUser;
+import org.springframework.stereotype.Service;
 
+@Service
 public class PaymentService {
+
     public Payment payment(NsUser user, Session session, Long amount) {
-        Registration registration = new Registration();
-        registration.register(user, session, amount);
         return new Payment(session.id());
     }
 }
diff --git a/src/main/java/nextstep/users/domain/NsUser.java b/src/main/java/nextstep/users/domain/NsUser.java
index 62ec5138c..01ab7fc85 100755
--- a/src/main/java/nextstep/users/domain/NsUser.java
+++ b/src/main/java/nextstep/users/domain/NsUser.java
@@ -25,6 +25,10 @@ public class NsUser {
     public NsUser() {
     }
 
+    public NsUser(Long id) {
+        this.id = id;
+    }
+
     public NsUser(Long id, String userId, String password, String name, String email) {
         this(id, userId, password, name, email, LocalDateTime.now(), null);
     }
diff --git a/src/main/java/nextstep/users/domain/UserRepository.java b/src/main/java/nextstep/users/domain/UserRepository.java
index 745a85dc1..b0618c3b7 100755
--- a/src/main/java/nextstep/users/domain/UserRepository.java
+++ b/src/main/java/nextstep/users/domain/UserRepository.java
@@ -1,7 +1,10 @@
 package nextstep.users.domain;
 
+import java.util.List;
 import java.util.Optional;
 
 public interface UserRepository {
     Optional<NsUser> findByUserId(String userId);
+
+    List<NsUser> findAllByIds(List<Long> ids);
 }
diff --git a/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java b/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java
index 005f04fc5..2c42cb12e 100644
--- a/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java
+++ b/src/main/java/nextstep/users/infrastructure/JdbcUserRepository.java
@@ -4,10 +4,14 @@
 import nextstep.users.domain.UserRepository;
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
 import org.springframework.stereotype.Repository;
 
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 @Repository("userRepository")
@@ -32,6 +36,27 @@ public Optional<NsUser> findByUserId(String userId) {
         return Optional.of(jdbcTemplate.queryForObject(sql, rowMapper, userId));
     }
 
+    @Override
+    public List<NsUser> findAllByIds(List<Long> ids) {
+        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+
+        String sql = "SELECT id, user_id, password, name, email, created_at, updated_at FROM ns_user WHERE id IN (:ids)";
+
+        Map<String, Object> params = new HashMap<>();
+        params.put("ids", ids);
+
+        RowMapper<NsUser> rowMapper = (rs, rowNum) -> new NsUser(
+                rs.getLong("id"),
+                rs.getString("user_id"),
+                rs.getString("password"),
+                rs.getString("name"),
+                rs.getString("email"),
+                toLocalDateTime(rs.getTimestamp("created_at")),
+                toLocalDateTime(rs.getTimestamp("updated_at")));
+
+        return namedParameterJdbcTemplate.query(sql, params, rowMapper);
+    }
+
     private LocalDateTime toLocalDateTime(Timestamp timestamp) {
         if (timestamp == null) {
             return null;
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
index 8d5a988c8..9763759b4 100644
--- a/src/main/resources/schema.sql
+++ b/src/main/resources/schema.sql
@@ -1,50 +1,100 @@
-create table course (
-    id bigint generated by default as identity,
-    title varchar(255) not null,
-    creator_id bigint not null,
-    created_at timestamp not null,
+create table course
+(
+    id         bigint generated by default as identity,
+    title      varchar(255) not null,
+    creator_id bigint       not null,
+    created_at timestamp    not null,
     updated_at timestamp,
     primary key (id)
 );
 
-create table ns_user (
-    id bigint generated by default as identity,
-    user_id varchar(20) not null,
-    password varchar(20) not null,
-    name varchar(20) not null,
-    email varchar(50),
+create table semester
+(
+    id         bigint generated by default as identity,
+    course_id  bigint    not null,
     created_at timestamp not null,
     updated_at timestamp,
     primary key (id)
 );
 
-create table question (
-    id bigint generated by default as identity,
-    created_at timestamp not null,
+create table session
+(
+    id               bigint generated by default as identity,
+    begin_dt         timestamp,
+    end_dt           timestamp,
+    session_status   varchar(10),
+    capacity         bigint,
+    price            bigint,
+    course_id        bigint not null,
+    session_cover_id bigint,
+    created_at       timestamp,
+    updated_at       timestamp,
+    primary key (id)
+);
+
+create table registration
+(
+    id         bigint generated by default as identity,
+    ns_user_id bigint not null,
+    session_id bigint not null,
+    amount     bigint,
+    created_at timestamp,
     updated_at timestamp,
-    contents clob,
-    deleted boolean not null,
-    title varchar(100) not null,
-    writer_id bigint,
     primary key (id)
 );
 
-create table answer (
-    id bigint generated by default as identity,
-    created_at timestamp not null,
+create table session_cover
+(
+    id         bigint generated by default as identity,
+    image      blob,
+    created_at timestamp,
+    updated_at timestamp,
+    primary key (id)
+);
+
+
+create table ns_user
+(
+    id         bigint generated by default as identity,
+    user_id    varchar(20) not null,
+    password   varchar(20) not null,
+    name       varchar(20) not null,
+    email      varchar(50),
+    created_at timestamp   not null,
     updated_at timestamp,
-    contents clob,
-    deleted boolean not null,
+    primary key (id)
+);
+
+create table question
+(
+    id         bigint generated by default as identity,
+    created_at timestamp    not null,
+    updated_at timestamp,
+    contents   clob,
+    deleted    boolean      not null,
+    title      varchar(100) not null,
+    writer_id  bigint,
+    primary key (id)
+);
+
+create table answer
+(
+    id          bigint generated by default as identity,
+    created_at  timestamp not null,
+    updated_at  timestamp,
+    contents    clob,
+    deleted     boolean   not null,
     question_id bigint,
-    writer_id bigint,
+    writer_id   bigint,
     primary key (id)
 );
 
-create table delete_history (
-    id bigint not null,
-    content_id bigint,
-    content_type varchar(255),
-    created_date timestamp,
+create table delete_history
+(
+    id            bigint not null,
+    content_id    bigint,
+    content_type  varchar(255),
+    created_date  timestamp,
     deleted_by_id bigint,
     primary key (id)
 );
diff --git a/src/test/java/nextstep/courses/domain/SessionTest.java b/src/test/java/nextstep/courses/domain/SessionTest.java
index c3ad572ca..6a36f4d25 100644
--- a/src/test/java/nextstep/courses/domain/SessionTest.java
+++ b/src/test/java/nextstep/courses/domain/SessionTest.java
@@ -5,6 +5,7 @@
 import org.junit.jupiter.api.Test;
 
 import java.time.LocalDateTime;
+import java.util.ArrayList;
 
 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
 import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -13,8 +14,8 @@ class SessionTest {
 
     @Test
     void 무료강의생성() {
-        Session freeSession = new FreeSession(1L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(300, 200, 1024, null), new Course());
-        assertThat(freeSession.id).isEqualTo(1L);
+        Session freeSession = Session.ofFree(1L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(3L, 300, 200, 1024, new byte[100]), new Course(), new ArrayList<>());
+        assertThat(freeSession.id()).isEqualTo(1L);
     }
 
     @Test
@@ -22,14 +23,14 @@ class SessionTest {
         long sessionPrice = 1_000_000L;
         int sessionCapacity = 100;
 
-        Session paidSession = new PaidSession(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1),
-                new SessionCover(300, 200, 1024, null), new Course(), sessionCapacity, sessionPrice);
-        assertThat(paidSession.id).isEqualTo(2L);
+        Session paidSession = Session.ofPaid(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1),
+                new SessionCover(4L, 300, 200, 1024, new byte[100]), new Course(), sessionPrice, sessionCapacity, new ArrayList<>());
+        assertThat(paidSession.id()).isEqualTo(2L);
     }
 
     @Test
     void 수강신청상태_exception() {
-        Session session = new FreeSession(5L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(300, 200, 1024, null), new Course());
+        Session session = Session.ofFree(5L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(5L, 300, 200, 1024, new byte[100]), new Course(), new ArrayList<>());
         assertThatExceptionOfType(BusinessInvalidValueException.class)
                 .isThrownBy(() -> session.enroll(new NsUser(), 0L))
                 .withMessage("수강신청 가능한 상태가 아닙니다.");
@@ -37,8 +38,8 @@ class SessionTest {
 
     @Test
     void 최대수강인원_exception() {
-        Session paidSession = new PaidSession(3L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1),
-                new SessionCover(300, 200, 1024, null), new Course(), 0, 1_000_000L);
+        Session paidSession = Session.ofPaid(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1),
+                new SessionCover(6L, 300, 200, 1024, new byte[100]), new Course(), 10000L, 0, new ArrayList<>());
         paidSession.startEnrollment();
 
         assertThatExceptionOfType(BusinessInvalidValueException.class)
@@ -48,8 +49,8 @@ class SessionTest {
 
     @Test
     void 가격비교_exception() {
-        Session paidSession = new PaidSession(4L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1),
-                new SessionCover(300, 200, 1024, null), new Course(), 10, 1_000_000L);
+        Session paidSession = Session.ofPaid(2L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1),
+                new SessionCover(7L, 300, 200, 1024, new byte[100]), new Course(), 10000L, 1, new ArrayList<>());
         paidSession.startEnrollment();
 
         assertThatExceptionOfType(BusinessInvalidValueException.class)
diff --git a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java
index f087fc0ad..5b7987c4f 100644
--- a/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java
+++ b/src/test/java/nextstep/courses/infrastructure/CourseRepositoryTest.java
@@ -1,7 +1,7 @@
 package nextstep.courses.infrastructure;
 
 import nextstep.courses.domain.Course;
-import nextstep.courses.domain.CourseRepository;
+import nextstep.courses.domain.repository.CourseRepository;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
diff --git a/src/test/java/nextstep/courses/infrastructure/RegistrationRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/RegistrationRepositoryTest.java
new file mode 100644
index 000000000..9fdd6198a
--- /dev/null
+++ b/src/test/java/nextstep/courses/infrastructure/RegistrationRepositoryTest.java
@@ -0,0 +1,33 @@
+package nextstep.courses.infrastructure;
+
+import nextstep.courses.domain.Course;
+import nextstep.courses.domain.Registration;
+import nextstep.courses.domain.Session;
+import nextstep.courses.domain.SessionCover;
+import nextstep.courses.domain.repository.RegistrationRepository;
+import nextstep.users.domain.NsUser;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+
+@SpringBootTest
+class RegistrationRepositoryTest {
+    @Autowired
+    private RegistrationRepository registrationRepository;
+
+    @Test
+    void crud() {
+        int id = registrationRepository.save(new Registration(new NsUser(1L, "1", "2", "3", "4")
+                , Session.ofFree(1L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(1L, 300, 200, 1024, new byte[100]), new Course(), new ArrayList<>())
+                , 100000L));
+        List<Registration> registrations = registrationRepository.findAllBySessionId(1L);
+        assertThat(registrations.get(0).nsUser().getId()).isEqualTo(1L);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/nextstep/courses/infrastructure/SessionCoverRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionCoverRepositoryTest.java
new file mode 100644
index 000000000..f0c0b1bed
--- /dev/null
+++ b/src/test/java/nextstep/courses/infrastructure/SessionCoverRepositoryTest.java
@@ -0,0 +1,22 @@
+package nextstep.courses.infrastructure;
+
+import nextstep.courses.domain.SessionCover;
+import nextstep.courses.domain.repository.SessionCoverRepository;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@SpringBootTest
+class SessionCoverRepositoryTest {
+    @Autowired
+    private SessionCoverRepository sessionCoverRepository;
+
+    @Test
+    void crud() {
+        int id = sessionCoverRepository.save(new SessionCover(new byte[100]));
+        SessionCover sessionCover = sessionCoverRepository.findById((long) id);
+        assertThat(sessionCover.id()).isEqualTo(id);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java
new file mode 100644
index 000000000..dc357724e
--- /dev/null
+++ b/src/test/java/nextstep/courses/infrastructure/SessionRepositoryTest.java
@@ -0,0 +1,28 @@
+package nextstep.courses.infrastructure;
+
+import nextstep.courses.domain.Course;
+import nextstep.courses.domain.Session;
+import nextstep.courses.domain.SessionCover;
+import nextstep.courses.domain.repository.SessionRepository;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+@SpringBootTest
+class SessionRepositoryTest {
+
+    @Autowired
+    private SessionRepository sessionRepository;
+
+    @Test
+    void crud() {
+        int id = sessionRepository.save(Session.ofFree(3L, LocalDateTime.now(), LocalDateTime.now().plusMonths(1), new SessionCover(100L, 300, 200, 1024, new byte[100]), new Course(100L, "제목", 3L), new ArrayList<>()));
+        Session session = sessionRepository.findById((long) id).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 id입니다."));
+        assertThat(session.id()).isEqualTo(id);
+    }
+}
\ No newline at end of file