Skip to content

Commit

Permalink
refactor: GameService.endGame() 리팩토링 (#304)
Browse files Browse the repository at this point in the history
* refactor: game 도메인 패키지와 gaemResult 도메인 패키지 분리

* refactor: Game.endGame() 메서드 Game이 갖는 상태에 대해서만 처리하도록 변경

- ResultType, 즉 성공 실패 여부는 Game이 알 요소가 아니므로 메서드 내에서 계산하는 부분 삭제

* refactor: Game.endGame() 메서드 Game이 갖는 상태에 대해서만 처리하도록 변경

- ResultType, 즉 성공 실패 여부는 Game이 알 요소가 아니므로 메서드 내에서 계산하는 부분 삭제

* refactor: GameFinishService 게임 결과 생성하는 서비스 인터페이스 추가 및 GameResultService로 결과 생성하는 로직 구현

* refactor: PlayerService에 점수 더하는 로직 추가

* refactor: 서비스 메서드에 대한 NoRollbackFor 제거 후 새로운 트랜잭션 열어서 횟수 차감하는 서비스 로직 추가

* fix: NoRollbackFor 원복

* refactor: endGame() 메서드의 동작 분리

* chore: GameResultService 패키지 변경

* refactor: GameManageService 도메인 서비스 생성

* refactor: GameManageService 도메인 서비스 삭제하고 메서드 도메인으로 넣음

* refactor: Game 시도횟수 줄일 때 검증 추가

* test: GameTest endGame 관련 테스트 추가

* test: GameResultService 테스트 추가

* test: PlayerService 점수 추가 로직 테스트 추가

* test: GameServiceTest 게임 종료 로직 테스트 추가

* chore: 워크플로우 수정

* chore: 워크플로우 수정

* chore: 테스트 통과하도록 수정

* chore: game과 gameresult 패키지의 persistence 패키지를 repository로 변경

* chore: setDone() 메서드 네이밍 변경

* chore: 컨벤션으로 메서드 호출 순서대로 메서드 명시

* refactor: 종료되지 않은 게임에 대한 에러를 GameException으로 변경

* refactor: endGame 메서드 진행중 게임 검증을 제일 처음에 진행하도록 변경

* refactor: findPlayerById 재활용하도록 수정
  • Loading branch information
dooboocookie committed Oct 3, 2023
1 parent db72134 commit da3d959
Show file tree
Hide file tree
Showing 38 changed files with 596 additions and 260 deletions.
15 changes: 14 additions & 1 deletion .github/workflows/backend_dev_pr_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: NAAGA BACKEND PULL REQUEST CI
on:
pull_request:
branches:
- dev
- dev_backend
paths:
- backend/**

Expand Down Expand Up @@ -52,3 +52,16 @@ jobs:
- name: Test with Gradle
run: ./gradlew test

- name: 테스트 결과를 PR에 코멘트로 등록합니다
uses: EnricoMi/publish-unit-test-result-action@v1
if: always()
with:
files: 'backend/build/test-results/test/TEST-*.xml'

- name: 테스트 실패 시, 실패한 코드 라인에 Check 코멘트를 등록합니다
uses: mikepenz/action-junit-report@v3
if: always()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
token: ${{ github.token }}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import com.now.naaga.auth.infrastructure.dto.MemberAuth;
import com.now.naaga.auth.infrastructure.jwt.AuthTokenGenerator;
import com.now.naaga.auth.persistence.AuthRepository;
import com.now.naaga.member.application.CreateMemberCommand;
import com.now.naaga.member.application.DeleteMemberCommand;
import com.now.naaga.member.application.dto.CreateMemberCommand;
import com.now.naaga.member.application.dto.DeleteMemberCommand;
import com.now.naaga.member.application.MemberService;
import com.now.naaga.member.domain.Member;
import com.now.naaga.player.application.PlayerService;
Expand Down
22 changes: 11 additions & 11 deletions backend/src/main/java/com/now/naaga/common/config/BeanConfig.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.now.naaga.common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.now.naaga.game.domain.gamescore.FailGameScorePolicy;
import com.now.naaga.game.domain.gamescore.GameScoreCalculator;
import com.now.naaga.game.domain.gamescore.GameScorePolicy;
import com.now.naaga.game.domain.gamescore.SuccessGameScorePolicy;
import com.now.naaga.gameresult.domain.gamescore.FailResultScorePolicy;
import com.now.naaga.gameresult.domain.gamescore.ResultScoreCalculator;
import com.now.naaga.gameresult.domain.gamescore.ResultScorePolicy;
import com.now.naaga.gameresult.domain.gamescore.SuccessResultScorePolicy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
Expand All @@ -25,21 +25,21 @@ public ObjectMapper objectMapper() {
}

@Bean
public SuccessGameScorePolicy successGameScorePolicy() {
return new SuccessGameScorePolicy();
public SuccessResultScorePolicy successGameScorePolicy() {
return new SuccessResultScorePolicy();
}

@Bean
public FailGameScorePolicy failGameScorePolicy() {
return new FailGameScorePolicy();
public FailResultScorePolicy failGameScorePolicy() {
return new FailResultScorePolicy();
}

@Bean
public GameScoreCalculator gameScoreCalculator() {
final List<GameScorePolicy> gameScorePolicies = List.of(
public ResultScoreCalculator gameScoreCalculator() {
final List<ResultScorePolicy> gameScorePolicies = List.of(
successGameScorePolicy(),
failGameScorePolicy()
);
return new GameScoreCalculator(gameScorePolicies);
return new ResultScoreCalculator(gameScorePolicies);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.now.naaga.game.application;

import com.now.naaga.game.application.dto.CreateGameResultCommand;

public interface GameFinishService {

void createGameResult(final CreateGameResultCommand createGameResultCommand);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.now.naaga.game.application;

import com.now.naaga.game.application.dto.CreateGameCommand;
import com.now.naaga.game.application.dto.EndGameCommand;
import com.now.naaga.game.application.dto.FindAllGamesCommand;
import com.now.naaga.game.application.dto.FindGameByIdCommand;
import com.now.naaga.game.application.dto.FindGameByStatusCommand;
import com.now.naaga.game.application.dto.*;
import com.now.naaga.game.domain.*;
import com.now.naaga.game.domain.gamescore.GameScoreCalculator;
import com.now.naaga.game.exception.GameException;
import com.now.naaga.game.exception.GameNotArrivalException;
import com.now.naaga.game.exception.GameNotFinishedException;
import com.now.naaga.game.repository.GameRepository;
import com.now.naaga.game.repository.GameResultRepository;

// TODO: 8/31/23 제거할 대상 - 이슈 범위를 벗어나서 일단은 제거하지 않음
import com.now.naaga.gameresult.domain.GameResult;
import com.now.naaga.gameresult.exception.GameResultException;
import com.now.naaga.gameresult.repository.GameResultRepository;

import com.now.naaga.place.application.PlaceService;
import com.now.naaga.place.application.dto.RecommendPlaceCommand;
import com.now.naaga.place.domain.Place;
Expand All @@ -19,7 +19,6 @@
import com.now.naaga.player.application.PlayerService;
import com.now.naaga.player.domain.Player;
import com.now.naaga.player.presentation.dto.PlayerRequest;
import com.now.naaga.score.domain.Score;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -28,30 +27,34 @@

import static com.now.naaga.game.exception.GameExceptionType.*;

// TODO: 8/31/23 제거할 대상 - 이슈 범위를 벗어나서 일단은 제거하지 않음
import static com.now.naaga.gameresult.exception.GameResultExceptionType.GAME_RESULT_NOT_EXIST;

@Transactional
@Service
public class GameService {

private final GameRepository gameRepository;

// TODO: 8/31/23 제거할 대상 - 이슈 범위를 벗어나서 일단은 제거하지 않음
private final GameResultRepository gameResultRepository;

private final PlayerService playerService;

private final PlaceService placeService;

private final GameScoreCalculator gameScoreCalculator;
private final GameFinishService gameFinishService;

public GameService(final GameRepository gameRepository,
final GameResultRepository gameResultRepository,
final PlayerService playerService,
final PlaceService placeService,
final GameScoreCalculator gameScoreCalculator) {
final GameResultRepository gameResultRepository,
final PlayerService playerService,
final PlaceService placeService,
final GameFinishService gameFinishService) {
this.gameRepository = gameRepository;
this.gameResultRepository = gameResultRepository;
this.playerService = playerService;
this.placeService = placeService;
this.gameScoreCalculator = gameScoreCalculator;
this.gameFinishService = gameFinishService;
}

public Game createGame(final CreateGameCommand createGameCommand) {
Expand All @@ -72,16 +75,19 @@ public Game createGame(final CreateGameCommand createGameCommand) {
}
}

@Transactional(noRollbackFor = {GameNotArrivalException.class})
@Transactional(noRollbackFor = {GameNotFinishedException.class})
public void endGame(final EndGameCommand endGameCommand) {
final Game game = gameRepository.findById(endGameCommand.gameId())
.orElseThrow(() -> new GameException(NOT_EXIST));
final Game game = gameRepository.findById(endGameCommand.gameId()).orElseThrow(() -> new GameException(NOT_EXIST));
final Player player = playerService.findPlayerById(endGameCommand.playerId());
game.validateOwner(player);
final ResultType resultType = game.endGame(endGameCommand.endType(), endGameCommand.position());
final Score score = gameScoreCalculator.calculate(game, resultType);
player.addScore(score);
gameResultRepository.save(new GameResult(resultType, score, game));

final EndType endType = endGameCommand.endType();
final Position position = endGameCommand.position();

game.endGame(position, endType);

final CreateGameResultCommand createGameResultCommand = new CreateGameResultCommand(player, game, position, endType);
gameFinishService.createGameResult(createGameResultCommand);
}

@Transactional(readOnly = true)
Expand All @@ -104,7 +110,7 @@ public GameResult findGameResultByGameId(final Long gameId) {
final List<GameResult> gameResultsByGameId = gameResultRepository.findByGameId(gameId);

if (gameResultsByGameId.isEmpty()) {
throw new GameException(GAME_RESULT_NOT_EXIST);
throw new GameResultException(GAME_RESULT_NOT_EXIST);
}

return gameResultsByGameId.get(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.now.naaga.game.application.dto;

import com.now.naaga.game.domain.EndType;
import com.now.naaga.game.domain.Game;
import com.now.naaga.place.domain.Position;
import com.now.naaga.player.domain.Player;

// TODO: 8/31/23 player -> ID / game -> game -> ID
public record CreateGameResultCommand(Player player,
Game game,
Position position,
EndType endType) {
}
115 changes: 54 additions & 61 deletions backend/src/main/java/com/now/naaga/game/domain/Game.java
Original file line number Diff line number Diff line change
@@ -1,39 +1,24 @@
package com.now.naaga.game.domain;

import static com.now.naaga.game.domain.EndType.GIVE_UP;
import static com.now.naaga.game.domain.GameStatus.DONE;
import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS;
import static com.now.naaga.game.domain.ResultType.FAIL;
import static com.now.naaga.game.domain.ResultType.SUCCESS;
import static com.now.naaga.game.exception.GameExceptionType.ALREADY_DONE;
import static com.now.naaga.game.exception.GameExceptionType.INACCESSIBLE_AUTHENTICATION;
import static com.now.naaga.game.exception.GameExceptionType.NOT_ARRIVED;

import com.now.naaga.common.domain.BaseEntity;
import com.now.naaga.game.exception.GameException;
import com.now.naaga.game.exception.GameNotArrivalException;
import com.now.naaga.game.exception.GameExceptionType;
import com.now.naaga.game.exception.GameNotFinishedException;
import com.now.naaga.place.domain.Place;
import com.now.naaga.place.domain.Position;
import com.now.naaga.player.domain.Player;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import java.math.BigDecimal;
import jakarta.persistence.*;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.springframework.data.geo.Distance;

import static com.now.naaga.game.domain.EndType.ARRIVED;
import static com.now.naaga.game.domain.EndType.GIVE_UP;
import static com.now.naaga.game.domain.GameStatus.DONE;
import static com.now.naaga.game.domain.GameStatus.IN_PROGRESS;
import static com.now.naaga.game.exception.GameExceptionType.*;

@Entity
public class Game extends BaseEntity {
Expand Down Expand Up @@ -76,7 +61,9 @@ public class Game extends BaseEntity {
protected Game() {
}

public Game(final Player player, final Place place, final Position startPosition) {
public Game(final Player player,
final Place place,
final Position startPosition) {
this(null, IN_PROGRESS, player, place, startPosition, MAX_ATTEMPT_COUNT, new ArrayList<>(), LocalDateTime.now(), null);
}

Expand Down Expand Up @@ -121,56 +108,62 @@ public boolean canUseMoreHint() {
return hints.size() < MAX_HINT_COUNT;
}

public ResultType endGame(final EndType endType, final Position position) {
if (isDone()) {
throw new GameException(ALREADY_DONE);
}
if (endType == GIVE_UP) {
return giveUpGame();
}
return endGameByArrival(position);
public double findDistance() {
final Position destinationPosition = place.getPosition();
return startPosition.calculateDistance(destinationPosition);
}

private boolean isDone() {
return gameStatus == DONE;
public void endGame(final Position position,
final EndType endType) {
validateInProgressing();

if (endType == ARRIVED) {
subtractAttempts();
}

setGameStatusDone(position, endType);
}

private ResultType giveUpGame() {
gameStatus = DONE;
endTime = LocalDateTime.now();
return FAIL;
private void validateInProgressing() {
if (gameStatus == DONE) {
throw new GameException(ALREADY_DONE);
}
}

private ResultType endGameByArrival(final Position position) {
private void subtractAttempts() {
validateAvailableAttempts();
remainingAttempts--;
if (isPlayerArrived(position)) {
return endGameWithSuccess();
}
return endGameWithFailure();
}

private boolean isPlayerArrived(final Position position) {
return place.isCoordinateInsideBounds(position);
private void validateAvailableAttempts() {
if (remainingAttempts <= 0) {
throw new GameException(GameExceptionType.NOT_REMAIN_ATTEMPTS);
}
}

private ResultType endGameWithSuccess() {
gameStatus = DONE;
endTime = LocalDateTime.now();
return SUCCESS;
private void setGameStatusDone(final Position position,
final EndType endType) {
validateForEnd(position, endType);

this.endTime = LocalDateTime.now();
this.gameStatus = DONE;
}

private ResultType endGameWithFailure() {
if (remainingAttempts == 0) {
gameStatus = DONE;
endTime = LocalDateTime.now();
return FAIL;
private void validateForEnd(final Position position,
final EndType endType) {
final boolean isUnfinishedCondition = remainingAttempts > 0
&& !place.isCoordinateInsideBounds(position)
&& endType != GIVE_UP;

if (isUnfinishedCondition) {
throw new GameNotFinishedException(NOT_ARRIVED);
}
throw new GameNotArrivalException(NOT_ARRIVED);
}

public double findDistance() {
final Position destinationPosition = place.getPosition();
return startPosition.calculateDistance(destinationPosition);

public void validateDoneGame() {
if(gameStatus == IN_PROGRESS) {
throw new GameException(NOT_DONE);
}
}

public Long getId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

import static com.now.naaga.game.domain.Game.MAX_ATTEMPT_COUNT;

import com.now.naaga.gameresult.domain.GameResult;
import com.now.naaga.place.domain.Position;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;

public class GameRecord {

Expand Down

This file was deleted.

Loading

0 comments on commit da3d959

Please sign in to comment.