Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary
37 changes: 37 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
## [본 과정] 이커머스 핵심 프로세스 구현
[단기 스킬업 Redis 교육 과정](https://hh-skillup.oopy.io/) 을 통해 상품 조회 및 주문 과정을 구현하며 현업에서 발생하는 문제를 Redis의 핵심 기술을 통해 해결합니다.
> Indexing, Caching을 통한 성능 개선 / 단계별 락 구현을 통한 동시성 이슈 해결 (낙관적/비관적 락, 분산락 등)
## Multi Module Design
Module을 나누는 기준은 여러 개가 있지만, 이번 프로젝트에서는 Layered Architecture 에서 설명되는 Layer 별로 구분하였습니다.
- module-presentation: 사용자의 요청을 받고, 응답한다.
- module-application: 사용자가 요청한 기능을 처리한다.
- module-domain: 시스템이 제공할 도메인 규칙을 구현한다.
- module-infra: 데이터베이스 같은 외부 시스템과의 연동을 처리한다.
- module-core: Convertor, Error-Response 와 같이 특정 layer에 종속되지 않는 기능을 처리한다.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layer 를 기준으로 모듈을 잘 구성해주셨네요 :) 많은 고민의 흔적이 보이는 것 같아서 좋습니다 ㅎㅎ

추가로 몇가지 리뷰를 드리면,

Layered Architecture 를 사용한다고 해서 모듈도 완전 동일한 형태의 Layer 로 나눌 필요는 없습니다 :)
presentation + application 을 하나의 모듈로 구성할 수도 있을 것 같아요.
그리고 추후에 분리가 필요하면 그때 분리해도 될 것 같습니다.

단, 지금은 현재 모듈 형태를 그대로 유지하고 2주차로 넘어가주세요 ! Layer 를 많이 쪼갠 형태의 모듈 구조를 사용하는 경우 프로젝트가 점진적으로 발전하면서 어떤 영향이 있을지, 직접 느껴보는 것도 많은 인사이트를 느낄 수 있을 것 같습니다. 진행 하시면서 presentation or application 이 통합되어도 괜찮겠다고 판단되는 지점이 올 수 있고, 혹은 분리해도 충분히 괜찮다고 느끼실 수도 있을 것 같아요.


core 모듈은 common 역할을 하는 모듈로 보여지는데, 경우에 따라서 해당 모듈을 anti-pattern 으로 보기도 합니다. (팀 by 팀)

공통 기능을 위한 하나의 모듈을 만드는 경우, 개발자들이 클래스들을 설계 하면서 (실제로 공통 역할이 아니지만) core(or common) 모듈에 패키지를 생성해서 정의하는 경우도 있습니다 !


전체적으로 모듈을 잘 구성해주셨습니다 !

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모듈을 구성하면서 너무 많은 모듈로 쪼갠 건 아닐까 라는 고민이 많이 되었습니다! 멘토님 말씀대로 직접 경험하면서 느껴봐야겠습니다! core 모듈은 제 프로젝트에서는 없애는 게 맞는 것 같아요 수정하겠습니다!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

| AS-IS
infra -> domain <- application <- presentation
infra <- application <- presentation
| TO-BE : infra -> domain <- application <- presentation

에서, MoviesNowShowingDbDto를 domain 모듈로 위치를 변경해도 application이 여전히 JpaRepository를 참조하고 있기 때문에 TO-BE 관계로 변경은 되지 않습니다.
그럼에도, MoviesNowShowingDbDto를 domain 모듈로 위치하는 것이 좋을까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TO-BE 로 만들려면 MovieRepository 또한 domain 으로 이동시켜야 합니다.

그러면 현재 설계 기준으로 infra 가 모듈이 없어지겠죠?, 즉, 의존성 방향을 TO-BE 처럼� 고수준(domain) 으로 흐르게끔 만드는 것이 clean architecture 입니다.

앞으로 남은 주차를 진행하면서 Presentation > Application > Persistence > Database 와 같이 단방향으로 흐르는 Layered Architecture 를 끝까지 가져가면서 개선해볼지, 혹은 진행하면서 다른 아키텍처로 변경할 것인지 자신만의 trade-off 기준을 세워서 선택해주시면 됩니다 :)


각 module은 하위 module에만 의존합니다. <br>
JPA에서 다른 기술로 변경될 것이라고 생각하여 DIP를 적용하는 것은 오버 엔지니어링이라고 생각되어, JPA 기술에 종속되지 않는 POJO 객체들을 생성하지 않고, 도메인 계층에 infra 계층에 종속되는 코드로 구현하였습니다. (@Entity 클래스, JpaRepository 등)

## Table Design
![img.png](img.png)
- Movie 테이블과 Theater 테이블은 N:N 관계로 중간에 Screening 테이블을 두고 있습니다.
- Theater 별로 시간표가 구분되는 것을 고려하여 Screening 테이블은 상영 시간표 정보를 포함하고 있습니다.
- 좌석별 등급 등 좌석 개별의 특성이 추가될 수 있다고 생각하여 Seat 테이블을 생성하였습니다.
- Theater 테이블과 Seat 테이블은 1:N 관계입니다.
- Seat 테이블과 User 테이블은 1:N 관계입니다.

## N+1 문제 해결
저는 N+1 문제가 ID 참조을 사용하기 때문이라고 생각합니다. 따라서 해당 프로젝트에 간접참조를 사용하여, N+1 문제를 해결하고자 합니다. 뿐만 아니라, 간접 참조를 사용하면 도메인 간 물리적인 연결을 제거하기 때문에 도메인 간 의존을 강제적으로 제거합니다.
45 changes: 45 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.1'
id 'io.spring.dependency-management' version '1.1.7'
}

repositories {
mavenCentral()
}

allprojects {
// 모든 하위 모듈들에 이 설정을 적용
group 'com.example'
version '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}

dependencies { // 모든 하위 모듈에 추가 될 의존성 목록입니다.
implementation 'org.springframework.boot:spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5.8.1 와 같이 버전을 의존성 마다 직접 명시하는 방법대신, 의존성 버전들을 한곳에서 관리하는 방법도 있습니다 !

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dependencyManagement 말씀하시는 걸까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yeonjookang 네 dependencyManagement 도 있지만, 명시적인 의존성이 필요한 경우도 있는데요,
이 경우 gradle.properties 에 아래와 같이 정의해서 가져다 사용할 수 있습니다.

springBootVersion=3.4.0

testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}

test {
useJUnitPlatform()
}

}
11 changes: 11 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3.8'
services:
mysql:
image: mysql:latest # 이미 로컬에 있는 MySQL 이미지 사용
environment:
MYSQL_ROOT_PASSWORD: 1234 # 루트 계정 비밀번호
MYSQL_DATABASE: hanghae99 # 생성할 기본 데이터베이스
MYSQL_USER: user # 사용자 계정 (선택)
MYSQL_PASSWORD: password # 사용자 비밀번호 (선택)
ports:
- "3305:3306" # 호스트의 3306 포트를 컨테이너의 3306 포트와 연결
Binary file added gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
252 changes: 252 additions & 0 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading