Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
76c972d
feat: .gitignore 파일 추가
jeongyuneo Jan 29, 2021
02e55ea
feat: .gitignore 파일 추가
jeongyuneo Jan 29, 2021
760b00c
fix: untracked files
jeongyuneo Jan 29, 2021
e8af171
docs: .gitignore 수정
jeongyuneo Jan 29, 2021
7c96cdb
fix: untracked files
jeongyuneo Jan 29, 2021
b4958b7
feat: 자동차 경주 게임 추가
jeongyuneo Mar 27, 2021
ca7217d
docs: CQRS.md 추가
jeongyuneo Mar 28, 2021
cf05236
refactor: Game 클래스 - IntStream 수정
jeongyuneo Mar 28, 2021
bf0b150
refactor: isMaxPosition 메소드 추출
jeongyuneo Mar 28, 2021
4ba7275
refactor: run() 함수 수정
jeongyuneo Mar 28, 2021
5959e76
refactor: InputView 클래스 리팩토링
jeongyuneo Mar 28, 2021
145f1eb
refactor: Cars 객체 추출
jeongyuneo Mar 28, 2021
f5b367b
refactor: OutputView 메소드 매개변수 수정
jeongyuneo Mar 28, 2021
ab8880e
refactor: Car 객체 - go() 수정
jeongyuneo Mar 28, 2021
0c7fa68
docs: 디미터 법칙.md 추가
jeongyuneo Mar 28, 2021
90a6376
refactor: Cars 수정
jeongyuneo Mar 28, 2021
98c3fba
docs: 일급 컬렉션.md 추가
jeongyuneo Mar 28, 2021
d4e8d12
fix: 자동차 이름 예외처리
jeongyuneo Apr 4, 2021
3890dde
refactor: 코드 리뷰 수정
jeongyuneo Apr 5, 2021
cd14028
refactor: 상수 위치 수정
jeongyuneo Apr 5, 2021
091f34d
refactor: 상수 추가
jeongyuneo Apr 6, 2021
c0956b9
refactor: Application 리팩토링
jeongyuneo Apr 7, 2021
e2d68bc
refactor: GameCounter - validate 책임 수정
jeongyuneo Apr 12, 2021
54ba13b
refactor: InputView - 상수명 수정
jeongyuneo Apr 12, 2021
6c89104
refactor: Car - 상수 및 변수 구분, 상수 추가
jeongyuneo Apr 12, 2021
21e9cce
refactor: view 코드 리팩토링
jeongyuneo Apr 12, 2021
9f4eb08
refactor: InputView 검증과 변환 분리
jeongyuneo Apr 30, 2021
7c07c08
refactor: Cars - findWinner 수정
jeongyuneo Apr 30, 2021
35d591f
refactor: getter 최소화
jeongyuneo Apr 30, 2021
da0bf05
refactor: 코드 컨벤션
jeongyuneo Apr 30, 2021
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
41 changes: 41 additions & 0 deletions 8주차/CQRS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# CQRS
## 1. CQRS란
CQRS는 Command and Query Responsibility Segregation(명령과 조회의 책임 분리)를 나타낸다.
CQRS는 시스템에서 명령을 처리하는 책임과 조회를 처리하는 책임을 분리하는 것이 핵심이다.
- 명령은 시스템의 상태를 변경하는 작업을 의미
- 조회는 시스템의 상태를 반환하는 작업을 의미

정리하면, **CQRS는 시스템의 상태를 변경하는 작업과 시스템의 상태를 반환하는 작업의 책임을 분리하는 것**이다.

일반적인 애플리케이션은 쿼리와 업데이트에 동일한 데이터 모델을 사용한다.

![](https://images.velog.io/images/minide/post/da85b570-1aef-49f5-939e-d51eebd9c39f/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-03-27%20%EC%98%A4%ED%9B%84%209.58.50.png)

간단한 애플리케이션에는 이것이 적합하지만 애플리케이션이 복잡해질수록 위 방법은 어려워진다. 왜냐하면 애플리케이션은 읽기 쪽에서 다른 쿼리를 실행할 수 있고, 그러면 모양이 다른 DTO를 반환하기 때문에 개체 매핑이 복잡해지기 때문이다. 또한 동일한 데이터 집합에서 작업을 병렬로 수행할 때 데이터 경합이 발생할 수 있으며, 데이터 저장소 및 데이터 액세스 계층에 대한 로드 및 정보를 검색하는데 필요한 쿼리의 복잡성으로 성능에 부정적인 영향을 미칠 수 있다. 그리고 각 엔터티는 읽기 및 쓰기 작업의 대상이 되므로 잘못된 컨텍스트에서 데이터를 노출할 수 있으므로 보안 및 사용 권한 관리가 복잡해질 수 있다.

반면 CQRS는 명령 및 쿼리의 책임을 분리하는 패턴으로, CQRS를 구현하면 성능, 확장성 보안을 극대화할 수 있다.

![](https://images.velog.io/images/minide/post/a1b540ca-786a-43af-a204-98654fb6df0a/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-03-28%20%EC%98%A4%ED%9B%84%201.33.10.png)

이 패턴에서 명령은 데이터 중심이 아닌 작업을 기반으로 해야하며, 비동기 처리를 위해 큐에 배치될 수도 있다. 또한 쿼리는 데이터베이스를 수정하지 않으며, 쿼리는 도메인 정보를 캡슐화지 않는 DTO를 반환한다.

읽기 저장소는 쓰기 저장소의 읽기 전용 복제본이거나 읽기 및 쓰기 저장소가 전혀 다른 구조일 수도 있다. 여러 일긱 전용 복제본을 사용하며 쿼리의 성능이 당연히 향상될 수 있으며, 부하를 감안하여 각 저장소를 적절하게 확장할 수도 있다.

![](https://images.velog.io/images/minide/post/054032dd-89cf-495b-8d75-7e2aa93e0cf8/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-03-28%20%EC%98%A4%ED%9B%84%201.44.27.png)

메모리 내 모델은 동일한 데이터베이스를 공유할 수 있으며, 이 경우 데이터베이스는 두 모델 간의 통신 역할을 한다. 위와 같이 여러 형태로 변형을 하여 CQRS를 적용할 수 있다. 별도의 데이터베이스를 허용하며 쿼리측 데이터베이스를 실시간 리포팅 데이터베이스(Reporting Database)로 만들 수도 있다. 이 경우 두 모델 또는 데이터베이스 간에 통신 매커니즘이 추가되어야 한다.

## 2. CQRS의 사용
CQRS를 사용해야 하는 경우는 아래와 같다.

1. 많은 사용자가 동일한 데이터에 병렬로 액세스하는 공동작업 도메인일 경우
2. 개발자 중 한 팀은 쓰기 모델에 포함되는 복잡한 도메인 모델에 집중하고 다른 한 팀은 읽기 모델과 사용자 인터페이스에 집중할 수 있는 경우
3. 시스템이 시간이 지나면서 진화할 것으로 예상되어 여러 버전의 모델을 포함할 수 있거나 비즈니스 규칙이 저기적으로 변하는 경우
4. 가장 가치있는 시스템의 제한된 구역에 CQRS 적용을 고려해야 한다.
-> 도메인 또는 비즈니스 규칙이 간단하거나 간단한 CRUD 스타일의 사용자 인터페이스와 데이터 액세스 작업만으로 충분하다면 CQRS는 어울리지 않는다. 모든 상황에서 최상단에 패턴으로 CQRS를 위치하는 건 어디에서도 추천하지 않는 방법이다.
-> DDD 용어에서 말하는 시스템 전체가 아닌 시스템의 특정 부분(Bounded Context)에서만 사용해야한다.
5. CQRS는 이벤트 기간 프로그래밍 모델에 적합하다.
-> CQRS 시스템이 이벤트 콜라보레이션(Event Collaboration)과 통신하는 별도의 서비스로 분리되는 것이 일반적이며, 이를 통해 이벤트 소싱(Event Sourcing)을 쉽게 이용할 수 있다.

> ### 이벤트 소싱(Event Sourcing)
> : 이벤트 전체를 하나의 데이터로 저장하는 방식
19 changes: 19 additions & 0 deletions 8주차/racingcar/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plugins {
id 'java'
}

group 'org.javastudy'
version '1.0-SNAPSHOT'

repositories {
mavenCentral()
}

dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

test {
useJUnitPlatform()
}
Binary file not shown.
5 changes: 5 additions & 0 deletions 8주차/racingcar/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
185 changes: 185 additions & 0 deletions 8주차/racingcar/gradlew
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
echo "$*"
}

die () {
echo
echo "$*"
echo
exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option

if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

exec "$JAVACMD" "$@"
89 changes: 89 additions & 0 deletions 8주차/racingcar/gradlew.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem

@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto execute

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar


@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega
2 changes: 2 additions & 0 deletions 8주차/racingcar/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rootProject.name = 'javastudy'

8 changes: 8 additions & 0 deletions 8주차/racingcar/src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import service.Game;

public class Application {
public static void main(String[] args) {
Game game = new Game();
game.play();
}
}
Loading